]> git.saurik.com Git - wxWidgets.git/blob - src/msw/utilsexc.cpp
9429ecb4f7f45c1613a6516d8ce9cc12fff4d731
[wxWidgets.git] / src / msw / utilsexc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/utilsexec.cpp
3 // Purpose: Various utilities
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/utils.h"
33 #include "wx/app.h"
34 #include "wx/intl.h"
35 #endif
36
37 #include "wx/log.h"
38
39 #ifdef __WIN32__
40 #include "wx/process.h"
41 #endif
42
43 #include "wx/msw/private.h"
44
45 #include <ctype.h>
46
47 #if !defined(__GNUWIN32__) && !defined(__WXWINE__) && !defined(__SALFORDC__)
48 #include <direct.h>
49 #ifndef __MWERKS__
50 #include <dos.h>
51 #endif
52 #endif
53
54 #if defined(__GNUWIN32__) && !defined(__TWIN32__)
55 #include <sys/unistd.h>
56 #include <sys/stat.h>
57 #endif
58
59 #if defined(__WIN32__) && !defined(__WXWINE__)
60 #include <io.h>
61
62 #ifndef __GNUWIN32__
63 #include <shellapi.h>
64 #endif
65 #endif
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #ifndef __WATCOMC__
71 #if !(defined(_MSC_VER) && (_MSC_VER > 800))
72 #include <errno.h>
73 #endif
74 #endif
75 #include <stdarg.h>
76
77 #if wxUSE_IPC
78 #include "wx/dde.h" // for WX_DDE hack in wxExecute
79 #endif // wxUSE_IPC
80
81 // ----------------------------------------------------------------------------
82 // constants
83 // ----------------------------------------------------------------------------
84
85 // this message is sent when the process we're waiting for terminates
86 #define wxWM_PROC_TERMINATED (WM_USER + 10000)
87
88 // ----------------------------------------------------------------------------
89 // this module globals
90 // ----------------------------------------------------------------------------
91
92 // we need to create a hidden window to receive the process termination
93 // notifications and for this we need a (Win) class name for it which we will
94 // register the first time it's needed
95 static const wxChar *gs_classForHiddenWindow = NULL;
96
97 // ----------------------------------------------------------------------------
98 // private types
99 // ----------------------------------------------------------------------------
100
101 // structure describing the process we're being waiting for
102 struct wxExecuteData
103 {
104 public:
105 ~wxExecuteData()
106 {
107 #ifndef __WIN16__
108 if ( !::CloseHandle(hProcess) )
109 {
110 wxLogLastError("CloseHandle(hProcess)");
111 }
112 #endif
113 }
114
115 HWND hWnd; // window to send wxWM_PROC_TERMINATED to
116 HANDLE hProcess; // handle of the process
117 DWORD dwProcessId; // pid of the process
118 wxProcess *handler;
119 DWORD dwExitCode; // the exit code of the process
120 bool state; // set to FALSE when the process finishes
121 };
122
123 // ============================================================================
124 // implementation
125 // ============================================================================
126
127 #ifdef __WIN32__
128 static DWORD wxExecuteThread(wxExecuteData *data)
129 {
130 WaitForSingleObject(data->hProcess, INFINITE);
131
132 // get the exit code
133 if ( !GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
134 {
135 wxLogLastError("GetExitCodeProcess");
136 }
137
138 wxASSERT_MSG( data->dwExitCode != STILL_ACTIVE,
139 wxT("process should have terminated") );
140
141 // send a message indicating process termination to the window
142 SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
143
144 return 0;
145 }
146
147 // window procedure of a hidden window which is created just to receive
148 // the notification message when a process exits
149 LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
150 WPARAM wParam, LPARAM lParam)
151 {
152 if ( message == wxWM_PROC_TERMINATED )
153 {
154 DestroyWindow(hWnd); // we don't need it any more
155
156 wxExecuteData *data = (wxExecuteData *)lParam;
157 if ( data->handler )
158 {
159 data->handler->OnTerminate((int)data->dwProcessId,
160 (int)data->dwExitCode);
161 }
162
163 if ( data->state )
164 {
165 // we're executing synchronously, tell the waiting thread
166 // that the process finished
167 data->state = 0;
168 }
169 else
170 {
171 // asynchronous execution - we should do the clean up
172 delete data;
173 }
174
175 return 0;
176 }
177 else
178 {
179 return DefWindowProc(hWnd, message, wParam, lParam);
180 }
181 }
182 #endif // Win32
183
184 long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
185 {
186 wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
187
188 #if wxUSE_IPC
189 // DDE hack: this is really not pretty, but we need to allow this for
190 // transparent handling of DDE servers in wxMimeTypesManager. Usually it
191 // returns the command which should be run to view/open/... a file of the
192 // given type. Sometimes, however, this command just launches the server
193 // and an additional DDE request must be made to really open the file. To
194 // keep all this well hidden from the application, we allow a special form
195 // of command: WX_DDE:<command>:DDE_SERVER:DDE_TOPIC:DDE_COMMAND in which
196 // case we execute just <command> and process the rest below
197 wxString command, ddeServer, ddeTopic, ddeCommand;
198 static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
199 if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
200 {
201 const wxChar *p = cmd.c_str() + 7;
202 while ( *p && *p != _T('#') )
203 {
204 command += *p++;
205 }
206
207 if ( *p )
208 {
209 // skip '#'
210 p++;
211 }
212 else
213 {
214 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
215 }
216
217 while ( *p && *p != _T('#') )
218 {
219 ddeServer += *p++;
220 }
221
222 if ( *p )
223 {
224 // skip '#'
225 p++;
226 }
227 else
228 {
229 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
230 }
231
232 while ( *p && *p != _T('#') )
233 {
234 ddeTopic += *p++;
235 }
236
237 if ( *p )
238 {
239 // skip '#'
240 p++;
241 }
242 else
243 {
244 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
245 }
246
247 while ( *p )
248 {
249 ddeCommand += *p++;
250 }
251 }
252 else
253 #endif // wxUSE_IPC
254 {
255 // no DDE
256 command = cmd;
257 }
258
259 #if defined(__WIN32__) && !defined(__TWIN32__)
260 // the old code is disabled because we really need a process handle
261 // if we want to execute it asynchronously or even just get its
262 // return code and for this we must use CreateProcess() and not
263 // ShellExecute()
264 #if 0
265 // isolate command and arguments
266 wxString commandName;
267 bool insideQuotes = FALSE;
268 const char *pc;
269 for ( pc = command.c_str(); *pc != '\0'; pc++ )
270 {
271 switch ( *pc )
272 {
273 case ' ':
274 case '\t':
275 if ( !insideQuotes )
276 break;
277 // fall through
278
279 case '"':
280 insideQuotes = !insideQuotes;
281 // fall through
282
283 default:
284 commandName += *pc;
285 continue; // skip the next break
286 }
287
288 // only reached for space not inside quotes
289 break;
290 }
291
292 wxString commandArgs = pc;
293
294 wxWindow *winTop = wxTheApp->GetTopWindow();
295 HWND hwndTop = (HWND)(winTop ? winTop->GetHWND() : 0);
296
297 HANDLE result;
298 #ifdef __GNUWIN32__
299 result = ShellExecute(hwndTop,
300 (const wchar_t)"open",
301 (const wchar_t)commandName,
302 (const wchar_t)commandArgs,
303 (const wchar_t)NULL,
304 SW_SHOWNORMAL);
305 #else // !GNUWIN32
306 result = ShellExecute(hwndTop, "open", commandName,
307 commandArgs, NULL, SW_SHOWNORMAL);
308 #endif // GNUWIN32
309
310 if ( ((long)result) <= 32 )
311 wxLogSysError(_("Can't execute command '%s'"), command.c_str());
312
313 return result;
314 #else // 1
315
316 // create the process
317 STARTUPINFO si;
318 wxZeroMemory(si);
319
320 si.cb = sizeof(si);
321
322 PROCESS_INFORMATION pi;
323
324 if ( ::CreateProcess(
325 NULL, // application name (use only cmd line)
326 (wxChar *)command.c_str(), // full command line
327 NULL, // security attributes: defaults for both
328 NULL, // the process and its main thread
329 FALSE, // don't inherit handles
330 CREATE_DEFAULT_ERROR_MODE |
331 CREATE_SUSPENDED, // flags
332 NULL, // environment (use the same)
333 NULL, // current directory (use the same)
334 &si, // startup info (unused here)
335 &pi // process info
336 ) == 0 )
337 {
338 wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
339
340 return 0;
341 }
342
343 // register the class for the hidden window used for the notifications
344 if ( !gs_classForHiddenWindow )
345 {
346 gs_classForHiddenWindow = _T("wxHiddenWindow");
347
348 WNDCLASS wndclass;
349 wxZeroMemory(wndclass);
350 wndclass.lpfnWndProc = (WNDPROC)wxExecuteWindowCbk;
351 wndclass.hInstance = wxGetInstance();
352 wndclass.lpszClassName = gs_classForHiddenWindow;
353
354 if ( !::RegisterClass(&wndclass) )
355 {
356 wxLogLastError("RegisterClass(hidden window)");
357 }
358 }
359
360 // create a hidden window to receive notification about process
361 // termination
362 HWND hwnd = ::CreateWindow(gs_classForHiddenWindow, NULL,
363 WS_OVERLAPPEDWINDOW,
364 0, 0, 0, 0, NULL,
365 (HMENU)NULL, wxGetInstance(), 0);
366 wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
367
368 // Alloc data
369 wxExecuteData *data = new wxExecuteData;
370 data->hProcess = pi.hProcess;
371 data->dwProcessId = pi.dwProcessId;
372 data->hWnd = hwnd;
373 data->state = sync;
374 if ( sync )
375 {
376 wxASSERT_MSG( !handler, wxT("wxProcess param ignored for sync execution") );
377
378 data->handler = NULL;
379 }
380 else
381 {
382 // may be NULL or not
383 data->handler = handler;
384 }
385
386 DWORD tid;
387 HANDLE hThread = ::CreateThread(NULL,
388 0,
389 (LPTHREAD_START_ROUTINE)wxExecuteThread,
390 (void *)data,
391 0,
392 &tid);
393
394 // resume process we created now - whether the thread creation succeeded or
395 // not
396 if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
397 {
398 // ignore it - what can we do?
399 wxLogLastError("ResumeThread in wxExecute");
400 }
401
402 // close unneeded handle
403 if ( !::CloseHandle(pi.hThread) )
404 wxLogLastError("CloseHandle(hThread)");
405
406 if ( !hThread )
407 {
408 wxLogLastError("CreateThread in wxExecute");
409
410 DestroyWindow(hwnd);
411 delete data;
412
413 // the process still started up successfully...
414 return pi.dwProcessId;
415 }
416
417 #if wxUSE_IPC
418 // second part of DDE hack: now establish the DDE conversation with the
419 // just launched process
420 if ( !!ddeServer )
421 {
422 wxDDEClient client;
423 wxConnectionBase *conn = client.MakeConnection(_T(""),
424 ddeServer,
425 ddeTopic);
426 if ( !conn || !conn->Execute(ddeCommand) )
427 {
428 wxLogError(_("Couldn't launch DDE server '%s'."), command.c_str());
429 }
430 }
431 #endif // wxUSE_IPC
432
433 if ( !sync )
434 {
435 // clean up will be done when the process terminates
436
437 // return the pid
438 return pi.dwProcessId;
439 }
440
441 // waiting until command executed (disable everything while doing it)
442 #if wxUSE_GUI
443 wxBeginBusyCursor();
444 wxEnableTopLevelWindows(FALSE);
445 #endif // wxUSE_GUI
446
447 while ( data->state )
448 wxYield();
449
450 #if wxUSE_GUI
451 wxEnableTopLevelWindows(TRUE);
452 wxEndBusyCursor();
453 #endif // wxUSE_GUI
454
455 DWORD dwExitCode = data->dwExitCode;
456 delete data;
457
458 // return the exit code
459 return dwExitCode;
460 #endif // 0/1
461 #else // Win16
462 long instanceID = WinExec((LPCSTR) WXSTRINGCAST command, SW_SHOW);
463 if (instanceID < 32) return(0);
464
465 if (sync) {
466 int running;
467 do {
468 wxYield();
469 running = GetModuleUsage((HINSTANCE)instanceID);
470 } while (running);
471 }
472
473 return(instanceID);
474 #endif // Win16/32
475 }
476
477 long wxExecute(char **argv, bool sync, wxProcess *handler)
478 {
479 wxString command;
480
481 while ( *argv != NULL )
482 {
483 command << *argv++ << ' ';
484 }
485
486 command.RemoveLast();
487
488 return wxExecute(command, sync, handler);
489 }
490
491 // ----------------------------------------------------------------------------
492 // Metafile helpers
493 // ----------------------------------------------------------------------------
494
495 extern void PixelToHIMETRIC(LONG *x, LONG *y)
496 {
497 ScreenHDC hdcRef;
498
499 int iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE),
500 iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE),
501 iWidthPels = GetDeviceCaps(hdcRef, HORZRES),
502 iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
503
504 *x *= (iWidthMM * 100);
505 *x /= iWidthPels;
506 *y *= (iHeightMM * 100);
507 *y /= iHeightPels;
508 }
509
510 extern void HIMETRICToPixel(LONG *x, LONG *y)
511 {
512 ScreenHDC hdcRef;
513
514 int iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE),
515 iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE),
516 iWidthPels = GetDeviceCaps(hdcRef, HORZRES),
517 iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
518
519 *x *= iWidthPels;
520 *x /= (iWidthMM * 100);
521 *y *= iHeightPels;
522 *y /= (iHeightMM * 100);
523 }