1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 /*
25  * Description: Put MSO in a state where it can be closed using
26  *              automation or kill it completely
27  */
28 
29 #include "stdafx.h"
30 #include <stdio.h>
31 
32 
33 void KillOffice();
34 BOOL KillAppFromWindow(HWND hWnd, char *appName);
35 BOOL CloseActiveDialogs();
36 void printUsage();
37 
38 //Callbacks used in closing
39 BOOL CALLBACK CloseOfficeDlgProc(HWND hwndChild, LPARAM lParam);
40 BOOL CALLBACK CountOfficeDlgProc(HWND hwndChild, LPARAM lParam);
41 
42 //Global counters for number of windows found
43 int gWDDlgCount = 0;
44 int gXLDlgCount = 0;
45 int gPPDlgCount = 0;
46 
47 //Dialog window class names for excel, powerpoint and word
48 //These are "Best guess" dialog names
49 const char *pWordDlg2k  = "bosa_sdm_Microsoft Word 9.0";
50 const char *pWordDlg2k3 = "bosa_sdm_Microsoft Office Word";
51 const char *pXLDlg2k    = "bosa_sdm_XL9";
52 const char *pPPDlg2k    = "#32770";
53 const char *pXLDlg2k3   = "bosa_sdm_XL9";
54 const char *pPPDlg2k3   = "#32770";
55 const char *pGenMSODlg  = "bosa_sdm_Mso96";
56 //consider adding - bosa_sdm_Mso96
57 
58 //Command Line Argument constants
59 const char *ARG_HELP  = "--help";
60 const char *ARG_KILL  = "--kill";
61 const char *ARG_CLOSE = "--close";
62 
63 //Window class names for MSO apps - if we need to look at other office instances
64 //then this list would need to be expanded
65 #define NUM_WINDOWCLASSNAMES 4
66 char *wndClassName[NUM_WINDOWCLASSNAMES] = {"OpusApp", "XLMAIN", "PP9FrameClass", "PP10FrameClass"};
67 
68 int main(int argc, char* argv[])
69 {
70 	if (argc < 2) {
71 		printUsage();
72 		return 0;
73 	}
74 
75 	if (strcmpi(argv[1], ARG_HELP) == 0) {
76 		printUsage();
77 		return 0;
78 	}
79 
80 	if (strcmpi(argv[1], ARG_KILL) == 0) {
81 		KillOffice();
82 		return 0;
83 	}
84 
85 	if (strcmpi(argv[1], ARG_CLOSE) == 0) {
86 		CloseActiveDialogs();
87 		return 0;
88 	}
89 
90 	return 0;
91 }
92 
93 /*--------------------------------------------------------------
94   Find the MSO window if it is available and explictly kill it
95   MSO apps in this case are Excel, Word and PP
96   Use FindWindow Win32 API to detect if they are available
97 
98   -------------------------------------------------------------*/
99 void KillOffice() {
100 	HWND hWnd;
101 
102 	for (int i=0;i<NUM_WINDOWCLASSNAMES;i++) {
103 		int j = 0;
104 		while (((hWnd = FindWindow(wndClassName[i], NULL )) != NULL) && (j < 10)) {
105 			KillAppFromWindow(hWnd, wndClassName[i]);
106 			j++;
107 		}
108 	}
109 }
110 
111 /*--------------------------------------------------------------
112   Using window handle, get process handle and try to kill the
113   app. This may not be successful if you do not have enough
114   privileges to kill the app.
115 
116   --------------------------------------------------------------*/
117 BOOL KillAppFromWindow(
118     HWND hWnd,
119     char *
120 #ifdef _DEBUG
121     appName
122 #endif
123 )
124 {
125 	BOOL bRet = TRUE;
126 
127 	if(hWnd == NULL) {
128 		//The app doesn't appear to be running
129 #ifdef _DEBUG
130 		printf("App %s: window not found.\n,", appName);
131 #endif
132 		bRet = FALSE;
133 	} else {
134 		DWORD pid;  // Variable to hold the process ID.
135 		DWORD dThread;  // Variable to hold (unused) thread ID.
136 		dThread = GetWindowThreadProcessId(hWnd, &pid);
137 		HANDLE hProcess; // Handle to existing process
138 
139 		hProcess = OpenProcess(SYNCHRONIZE | PROCESS_ALL_ACCESS, TRUE, pid);
140 		if (hProcess == NULL) {
141 #ifdef _DEBUG
142 			printf("App %s : Failed to get process handle",appName);
143 #endif
144 			bRet = FALSE;
145 		} else {
146 			if (!TerminateProcess(hProcess, 0)) {
147 				LPTSTR lpMsgBuf;
148 				FormatMessage(
149 					FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
150 					NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
151 					(LPTSTR) &lpMsgBuf, 0, NULL );
152 				printf("%s\n", lpMsgBuf);
153 				LocalFree( lpMsgBuf );
154 				bRet = FALSE;
155 			}
156 #ifdef _DEBUG
157 			else {
158 				printf("Kill %s appears to be successful.\n", appName);
159 			}
160 #endif
161 		}
162 	}
163 	return bRet;
164 }
165 
166 /*--------------------------------------------------------------
167   Close the dialogs if possible based on their window class
168   Use the EnumChildWindows win32 api for this
169   --------------------------------------------------------------*/
170 BOOL CloseActiveDialogs() {
171 	char buff[1024];
172 
173 	gWDDlgCount = 0;
174 	gXLDlgCount = 0;
175 	gPPDlgCount = 0;
176 
177 	EnumChildWindows(GetDesktopWindow(), CloseOfficeDlgProc, (LPARAM) 0);
178 	sprintf(buff, "Word: %d\tExcel: %d\tPP: %d", gWDDlgCount, gXLDlgCount, gPPDlgCount);
179 	return TRUE;
180 }
181 
182 /*--------------------------------------------------------------
183   Callback for EnumChildWindows that sends close message to
184   any dialogs that match window class of MSO dialogs
185 
186   --------------------------------------------------------------*/
187 BOOL CALLBACK CloseOfficeDlgProc(HWND hwndChild, LPARAM)
188 {
189 	//bosa_sdm_Microsoft Word 9.0
190 	//bosa_sdm_XL9
191 	//#32770 (Dialog)
192 
193  	char szBuff[4096];
194 	if (GetClassName(hwndChild, szBuff, 4096) == 0) {
195 
196 	} else {
197 		if ((strcmpi(szBuff, pWordDlg2k) == 0) || (strcmpi(szBuff, pWordDlg2k3) == 0)) {
198 			gWDDlgCount++;
199 			SendMessage(hwndChild, WM_CLOSE, 0, 0);
200 		}
201 		if (strcmpi(szBuff, pXLDlg2k) == 0) {
202 			gXLDlgCount++;
203 			SendMessage(hwndChild, WM_CLOSE, 0, 0);
204 		}
205 		if (strcmpi(szBuff, pPPDlg2k) == 0) {
206 			gPPDlgCount++;
207 			SendMessage(hwndChild, WM_CLOSE, 0, 0);
208 		}
209 		if (strcmpi(szBuff, pGenMSODlg) == 0) {
210 			SendMessage(hwndChild, WM_CLOSE, 0, 0);
211 		}
212 	}
213 
214     return TRUE;
215 }
216 
217 
218 /*--------------------------------------------------------------
219   Callback for EnumChildWindows that counts numnnber of
220   dialogs that match window class of MSO dialogs
221 
222   --------------------------------------------------------------*/
223 BOOL CALLBACK CountOfficeDlgProc(HWND hwndChild, LPARAM)
224 {
225  	char szBuff[4096];
226 	if (GetClassName(hwndChild, szBuff, 4096) == 0) {
227 
228 	} else {
229 		if ((strcmpi(szBuff, pWordDlg2k) == 0) || (strcmpi(szBuff, pWordDlg2k3) == 0)) {
230 			gWDDlgCount++;
231 		}
232 		if (strcmpi(szBuff, pXLDlg2k) == 0) {
233 			gXLDlgCount++;
234 		}
235 		if (strcmpi(szBuff, pPPDlg2k) == 0) {
236 			gPPDlgCount++;
237 		}
238 	}
239 
240 	return TRUE;
241 }
242 
243 /*--------------------------------------------------------------
244   Simple usage message...
245 
246   -------------------------------------------------------------*/
247 void printUsage() {
248 	printf("Recovery Assistant Utility - try and put MSO apps in a recoverable state\n");
249 	printf("Copyright Sun Microsystems 2008\n");
250 	printf("Options:\n");
251 	printf("   --help : This message\n");
252 	printf("   --close: Attempt to close any open dialogs owned by \n");
253 	printf("            MSO apps so Application.Quit() can succeed\n");
254 	printf("   --kill : Kill any open MSO apps. Use with caution and only as a last resort\n\n");
255 }