Killing a Word session


I created a Word session from C++ using:

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:

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

Obviously it is a wrong type conversion.

Can you help me?

Who is Participating?

Improve company productivity with a Business Account.Sign Up

stefanrConnect With a Mentor Commented:
There is another form for ShellExecute, namely ShellExecuteEx, that can return additional information like a process handle.
In the online help it says


Performs an action on a file.

Returns a nonzero value if successful, or zero otherwise. To get extended error information, call the GetLastError function.
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;

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

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.
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.
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).
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

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.
SergeDAuthor Commented:
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.

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.
SergeDAuthor Commented:
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!


You can use FindWindow() or use EnumWindows() along with GetWindowThreadProcessId().
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:

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

   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.
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.
SergeDAuthor Commented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.