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