Solved

Killing a Word session

Posted on 1999-01-19
11
342 Views
Last Modified: 2008-03-10
Hi,

I created a Word session from C++ using:

HINSTANCE hErrorCode;
hErrorCode = ShellExecute (hParent, "open", "winword.exe", sTemplate, NULL, SW_HIDE);

I like to close it, but I always have the system error 6 (invalide handle) using:

HINSTANCE hErrorCode;
HWND hParent;
HANDLE hWord;
LPDWORD pExitCode;
UINT uExitCode;
hWord = (HANDLE) lErrorCode;
Response = GetExitCodeProcess(hWord, pExitCode);
if (Response == 0)
   lReturnCode = GetLastError();
else
{
 uExitCode = (UINT) pExitCode;
 Response = TerminateProcess(hWord, uExitCode);
}

Obviously it is a wrong type conversion.

Can you help me?
Thanx

Serge
0
Comment
Question by:SergeD
  • 5
  • 3
  • 3
11 Comments
 
LVL 22

Expert Comment

by:nietod
Comment Utility
The return value of ShellExecute is an HINSTANCE, that is not a process handle (which is what TerminateProcess needs.  You cannot get the process handle using ShellExecute().  Use CreateProcess() instead.
0
 
LVL 3

Expert Comment

by:stefanr
Comment Utility
The return value of ShellExecute can't be used as a process handle. The online help says that the value that ShellExecute returns
. "a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise. The following table lists the error values. The return value is cast as an HINSTANCE for backward compatibility with 16-bit Microsoft® Windows® applications. It is not a true HINSTANCE, however. The only thing that can be done with the returned HINSTANCE is to cast it to an integer and compare it with the value 32 or one of the error codes below." ...

Use CreateProcess instead of ShellExecute to obtain the process handle, or find the process id (the method depends if it is Windows 95/98 or Windows NT, I'm not familiar with anything else but NT) and use OpenProcess for that process id. And furthermore, before trying to use the TerminateProcess call, try to send a WM_CLOSE window message to the Word's main window (for example using using FindWindow).
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
Note The last parameter to CreateProcess() is a pointer to a PROCESS_INFORMATION structure.  This gets filled in with the new process's handle and its primary thread's handle.  You need to use this process handle.  Both these handles must be closed when you are done with them.  You probably don't need the thread handle so you can close it immediately.  The process handle you can close when you terminate the process.
0
 

Author Comment

by:SergeD
Comment Utility
Thanx nietod (and stefanr).

The problem with CreateProcess is that the system will not use a running Word session if it exists, but will create a new one for each single call. That's not really what I'm looking for. I definitevely need a ShellExecute (unless there is another function that can do the job. I'm not aware of such other function).

Unfortunately the problem remains. How to kill a running session created (or not) with ShellExecute)?

Thanx to both of you.
Serge

0
 
LVL 3

Accepted Solution

by:
stefanr earned 50 total points
Comment Utility
There is another form for ShellExecute, namely ShellExecuteEx, that can return additional information like a process handle.
In the online help it says

WINSHELLAPI BOOL WINAPI ShellExecuteEx(LPSHELLEXECUTEINFO pExecInfo);

Performs an action on a file.

Returns a nonzero value if successful, or zero otherwise. To get extended error information, call the GetLastError function.
lpExecInfo
Address of a SHELLEXECUTEINFO structure that contains and receives information about the application being executed.
If the function succeeds, it sets the hInstApp member of the SHELLEXECUTEINFO structure to the instance handle to the application that the function started. If the function fails, hInstApp is one of the SE_ERR_ error values indicating the cause of the failure. (An instance handle will always be greater than 32 and an error value less than 32.) Note that the SE_ERR_ error values are for compatibility with the ShellExecute function; use the GetLastError function to retrieve error information.

The error values returned by GetLastError correspond to the SE_ERR_ values and may be one of the following: ERROR_FILE_NOT_FOUND  The specified file was not found.  
ERROR_PATH_NOT_FOUND  The specified path was not found.  
ERROR_DDE_FAIL  The DDE transaction failed.  
ERROR_NO_ASSOCIATION  There is no application associated with the given file name extension.  
ERROR_ACCESS_DENIED  Access to the specified file is denied.  
ERROR_DLL_NOT_FOUND  One of the library files necessary to run the application can't be found.  
ERROR_CANCELLED  The function prompted the user for the location of the application, but the user canceled the request.  
ERROR_NOT_ENOUGH_MEMORY  There is not enough memory to perform the specified action.  
ERROR_SHARING_VIOLATION  A sharing violation occurred.

And the SHELLEXECUTEINFO structure has the following definition

typedef struct _SHELLEXECUTEINFO{
    DWORD cbSize;
    ULONG fMask;
    HWND hwnd;
    LPCTSTR lpVerb;
    LPCTSTR lpFile;
    LPCTSTR lpParameters;
    LPCTSTR lpDirectory;
    int nShow;
    HINSTANCE hInstApp;

    // Optional members
    LPVOID lpIDList;
    LPCSTR lpClass;
    HKEY hkeyClass;
    DWORD dwHotKey;
    HANDLE hIcon;
    HANDLE hProcess;
} SHELLEXECUTEINFO, FAR *LPSHELLEXECUTEINFO;

The last member of the structure is a hProcess which is described as

hProcess
Handle to the newly started application. This member is set on return and is always NULL if fMask is not set to SEE_MASK_NOCLOSEPROCESS.

That hProcess is what you need to kill a process. Still, it is recommended to send a WM_CLOSE message to the main window as described before, and if that fails call TerminateProcess.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 22

Expert Comment

by:nietod
Comment Utility
For what its worth, I agree with stefanr that you should use TerminateProcess only as a last resort.  Try to close Word by sending it a WM_CLOSE command first.
0
 

Author Comment

by:SergeD
Comment Utility
Hi Lads,

sorry for the delay.
I have tried the SendMessage function, but it requires the windows handle of the application (HWND). With the ShellExecute, I have the process handle (HINSTANCE). How can I get the windows handle? Specially in the case where I'm launching a Word session with the HIDE parameter (I cannot use the GetActiveWindows function)?

Have you read 1984? Sometimes C++ appears to be really like this!

Thanx
Serge

0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
You can use FindWindow() or use EnumWindows() along with GetWindowThreadProcessId().
0
 
LVL 3

Expert Comment

by:stefanr
Comment Utility
In the MSDN Article Q178893 (HOWTO: Terminate an Application "Cleanly" in Win32) it says that

."If you absolutely must shut down a process, follow these steps:


Post a WM_CLOSE to all Top-Level windows owned by the process that you want to shut down. Many Windows applications respond to this message by shutting down. NOTE: A console application's response to WM_CLOSE depends on whether or not it has installed a control handler. For additional information, please see the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q102429

   TITLE     : HOWTO: Detect Closure of Command Window from a Console
               App

   Use EnumWindows() to find the handles to your target windows. In your
   callback function, check to see if the windows' process ID matches
   the process you want to shut down. You can do this by calling
   GetWindowThreadProcessId(). Once you have established a match, use
   PostMessage() or SendMessageTimeout() to post the WM_CLOSE message to
   the window.


Use WaitForSingleObject() to wait for the handle of the process. Make sure you wait with a timeout value, because there are many situations in which the WM_CLOSE will not shut down the application. Remember to make the timeout long enough (either with WaitForSingleObject(), or with SendMessageTimeout()) so that a user can respond to any dialog boxes that were created in response to the WM_CLOSE message.

If the return value is WAIT_OBJECT_0, then the application closed itself down cleanly. If the return value is WAIT_TIMEOUT, then you must use TerminateProcess() to shutdown the application. NOTE: If you are getting a return value from WaitForSingleObject() other then WAIT_OBJECT_0 or WAIT_TIMEOUT, use GetLastError() to determine the cause.

By following these steps, you give the application the best possible chance to shutdown cleanly (aside from IPC or user-intervention)."...

Unfortunately, there seem not to be a simple way of matching the process id retrieved with GetWindowThreadProcessId to the hProcess returned by the ShellExecuteEx function. I can only find functions to retrieve the process id of the CURRENT process. Maybe there would be a way using the Performance Data (on NT) or the Tool Help functions (on Windows 95/98).
One simpler idea would be to use the HWND obtained through the EnumWindows callback function and call GetWindowText. If the obtained caption string contains the text "Microsoft Word" you would be pretty sure that you have found it. Then you could send it the WM_CLOSE message.
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
CreateProcess() returns the child process's handle and ID, as well as the primary thread's handle and ID, unfortuantely ShellExecute() does not.

You can try obtaining a window's proccess ID and converting it to a handle using OpenProcess(), and then comparing that with the handle you have for the word process.  However, I don't think that two handles to a process are necessarily the same, but you could try that.
0
 

Author Comment

by:SergeD
Comment Utility
Thank you both for your suggestions!

I haven't too much time to investigate now all your suggestions, so I close the question and hope that I will find a definitive solution next week.

Thank you for all
Serge
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now