+ NULL, // security attributes: defaults for both
+ NULL, // the process and its main thread
+ redirect, // inherit handles if we use pipes
+ dwFlags, // process creation flags
+ envBuffer.data(), // environment (may be NULL which is fine)
+ useCwd // initial working directory
+ ? wxMSW_CONV_LPTSTR(env->cwd)
+ : NULL, // (or use the same)
+ &si, // startup info (unused here)
+ &pi // process info
+ ) != 0;
+
+#if wxUSE_STREAMS && !defined(__WXWINCE__)
+ // we can close the pipe ends used by child anyhow
+ if ( redirect )
+ {
+ ::CloseHandle(pipeIn.Detach(wxPipe::Read));
+ ::CloseHandle(pipeOut.Detach(wxPipe::Write));
+ ::CloseHandle(pipeErr.Detach(wxPipe::Write));
+ }
+#endif // wxUSE_STREAMS
+
+ if ( !ok )
+ {
+#if wxUSE_STREAMS && !defined(__WXWINCE__)
+ // close the other handles too
+ if ( redirect )
+ {
+ ::CloseHandle(pipeOut.Detach(wxPipe::Read));
+ ::CloseHandle(pipeErr.Detach(wxPipe::Read));
+ }
+#endif // wxUSE_STREAMS
+
+ wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
+
+ return flags & wxEXEC_SYNC ? -1 : 0;
+ }
+
+#if wxUSE_STREAMS && !defined(__WXWINCE__)
+ // the input buffer bufOut is connected to stdout, this is why it is
+ // called bufOut and not bufIn
+ wxStreamTempInputBuffer bufOut,
+ bufErr;
+
+ if ( redirect )
+ {
+ // We can now initialize the wxStreams
+ wxPipeInputStream *
+ outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
+ wxPipeInputStream *
+ errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
+ wxPipeOutputStream *
+ inStream = new wxPipeOutputStream(hpipeStdinWrite);
+
+ handler->SetPipeStreams(outStream, inStream, errStream);
+
+ bufOut.Init(outStream);
+ bufErr.Init(errStream);
+ }
+#endif // wxUSE_STREAMS
+
+ // create a hidden window to receive notification about process
+ // termination
+ HWND hwnd = wxCreateHiddenWindow
+ (
+ &gs_classForHiddenWindow,
+ wxMSWEXEC_WNDCLASSNAME,
+ (WNDPROC)wxExecuteWindowCbk
+ );
+
+ wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
+
+ // Alloc data
+ wxExecuteData *data = new wxExecuteData;
+ data->hProcess = pi.hProcess;
+ data->dwProcessId = pi.dwProcessId;
+ data->hWnd = hwnd;
+ data->state = (flags & wxEXEC_SYNC) != 0;
+ if ( flags & wxEXEC_SYNC )
+ {
+ // handler may be !NULL for capturing program output, but we don't use
+ // it wxExecuteData struct in this case
+ data->handler = NULL;
+ }
+ else
+ {
+ // may be NULL or not
+ data->handler = handler;
+
+ if (handler)
+ handler->SetPid(pi.dwProcessId);
+ }
+
+ DWORD tid;
+ HANDLE hThread = ::CreateThread(NULL,
+ 0,
+ wxExecuteThread,
+ (void *)data,
+ 0,
+ &tid);
+
+ // resume process we created now - whether the thread creation succeeded or
+ // not
+ if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
+ {
+ // ignore it - what can we do?
+ wxLogLastError(wxT("ResumeThread in wxExecute"));
+ }
+
+ // close unneeded handle
+ if ( !::CloseHandle(pi.hThread) )
+ {
+ wxLogLastError(wxT("CloseHandle(hThread)"));
+ }
+
+ if ( !hThread )
+ {
+ wxLogLastError(wxT("CreateThread in wxExecute"));
+
+ DestroyWindow(hwnd);
+ delete data;
+
+ // the process still started up successfully...
+ return pi.dwProcessId;
+ }
+
+ gs_asyncThreads.push_back(hThread);
+
+#if wxUSE_IPC && !defined(__WXWINCE__)
+ // second part of DDE hack: now establish the DDE conversation with the
+ // just launched process
+ if ( !ddeServer.empty() )
+ {
+ bool ddeOK;
+
+ // give the process the time to init itself
+ //
+ // we use a very big timeout hoping that WaitForInputIdle() will return
+ // much sooner, but not INFINITE just in case the process hangs
+ // completely - like this we will regain control sooner or later
+ switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
+ {
+ default:
+ wxFAIL_MSG( wxT("unexpected WaitForInputIdle() return code") );
+ // fall through
+
+ case WAIT_FAILED:
+ wxLogLastError(wxT("WaitForInputIdle() in wxExecute"));
+
+ case WAIT_TIMEOUT:
+ wxLogDebug(wxT("Timeout too small in WaitForInputIdle"));
+
+ ddeOK = false;
+ break;
+
+ case 0:
+ // ok, process ready to accept DDE requests
+ ddeOK = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
+ }
+
+ if ( !ddeOK )
+ {
+ wxLogDebug(wxT("Failed to send DDE request to the process \"%s\"."),
+ cmd.c_str());
+ }
+ }
+#endif // wxUSE_IPC
+
+ if ( !(flags & wxEXEC_SYNC) )
+ {
+ // clean up will be done when the process terminates
+
+ // return the pid
+ return pi.dwProcessId;
+ }
+
+ wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
+ wxCHECK_MSG( traits, -1, wxT("no wxAppTraits in wxExecute()?") );
+
+ void *cookie = NULL;
+ if ( !(flags & wxEXEC_NODISABLE) )
+ {
+ // disable all app windows while waiting for the child process to finish
+ cookie = traits->BeforeChildWaitLoop();
+ }
+
+ // wait until the child process terminates
+ while ( data->state )
+ {
+#if wxUSE_STREAMS && !defined(__WXWINCE__)
+ if ( !bufOut.Update() && !bufErr.Update() )
+#endif // wxUSE_STREAMS
+ {
+ // don't eat 100% of the CPU -- ugly but anything else requires
+ // real async IO which we don't have for the moment
+ ::Sleep(50);
+ }
+
+ // we must always process messages for our hidden window or we'd never
+ // get wxWM_PROC_TERMINATED and so this loop would never terminate
+ MSG msg;
+ ::PeekMessage(&msg, data->hWnd, 0, 0, PM_REMOVE);
+
+ // we may also need to process messages for all the other application
+ // windows
+ if ( !(flags & wxEXEC_NOEVENTS) )
+ {
+ wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
+ if ( loop )
+ loop->Yield();
+ }
+ }
+
+ if ( !(flags & wxEXEC_NODISABLE) )
+ {
+ // reenable disabled windows back
+ traits->AfterChildWaitLoop(cookie);
+ }
+
+ DWORD dwExitCode = data->dwExitCode;
+ delete data;
+
+ // return the exit code
+ return dwExitCode;
+}
+
+template <typename CharType>
+long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler,
+ const wxExecuteEnv *env)
+{
+ wxString command;
+ command.reserve(1024);
+
+ wxString arg;
+ for ( ;; )
+ {
+ arg = *argv++;
+
+ bool quote;
+ if ( arg.empty() )
+ {
+ // we need to quote empty arguments, otherwise they'd just
+ // disappear
+ quote = true;
+ }
+ else // non-empty
+ {
+ // escape any quotes present in the string to avoid interfering
+ // with the command line parsing in the child process
+ arg.Replace("\"", "\\\"", true /* replace all */);
+
+ // and quote any arguments containing the spaces to prevent them from
+ // being broken down
+ quote = arg.find_first_of(" \t") != wxString::npos;
+ }
+
+ if ( quote )
+ command += '\"' + arg + '\"';
+ else
+ command += arg;
+
+ if ( !*argv )
+ break;
+
+ command += ' ';
+ }
+
+ return wxExecute(command, flags, handler, env);
+}
+
+long wxExecute(char **argv, int flags, wxProcess *handler,
+ const wxExecuteEnv *env)
+{
+ return wxExecuteImpl(argv, flags, handler, env);