X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0d7ea902499bf302628cc12aa4705fb162b6fefd..5d25c05039a7c6ebbddfe16d99234dc756e75356:/src/msw/utilsexc.cpp diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp index 5cbd5a4957..4a5489086c 100644 --- a/src/msw/utilsexc.cpp +++ b/src/msw/utilsexc.cpp @@ -37,6 +37,7 @@ #include "wx/log.h" #ifdef __WIN32__ + #include "wx/stream.h" #include "wx/process.h" #endif @@ -74,6 +75,10 @@ #endif #include +#if wxUSE_IPC + #include "wx/dde.h" // for WX_DDE hack in wxExecute +#endif // wxUSE_IPC + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -116,6 +121,94 @@ public: bool state; // set to FALSE when the process finishes }; + +#ifdef __WIN32__ +// ---------------------------------------------------------------------------- +// wxPipeStreams +// ---------------------------------------------------------------------------- + +class wxPipeInputStream: public wxInputStream { +public: + wxPipeInputStream(HANDLE hInput); + ~wxPipeInputStream(); + +protected: + size_t OnSysRead(void *buffer, size_t len); + +protected: + HANDLE m_hInput; +}; + +class wxPipeOutputStream: public wxOutputStream { +public: + wxPipeOutputStream(HANDLE hOutput); + ~wxPipeOutputStream(); + +protected: + size_t OnSysWrite(const void *buffer, size_t len); + +protected: + HANDLE m_hOutput; +}; + +// ================== +// wxPipeInputStream +// ================== + +wxPipeInputStream::wxPipeInputStream(HANDLE hInput) +{ + m_hInput = hInput; +} + +wxPipeInputStream::~wxPipeInputStream() +{ + ::CloseHandle(m_hInput); +} + +size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len) +{ + DWORD bytesRead; + + m_lasterror = wxSTREAM_NOERROR; + if (! ::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) ) { + if (GetLastError() == ERROR_BROKEN_PIPE) + m_lasterror = wxSTREAM_EOF; + else + m_lasterror = wxSTREAM_READ_ERROR; + } + return bytesRead; +} + +// ================== +// wxPipeOutputStream +// ================== + +wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput) +{ + m_hOutput = hOutput; +} + +wxPipeOutputStream::~wxPipeOutputStream() +{ + ::CloseHandle(m_hOutput); +} + +size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len) +{ + DWORD bytesRead; + + m_lasterror = wxSTREAM_NOERROR; + if (! ::WriteFile(m_hOutput, buffer, len, &bytesRead, NULL) ) { + if (GetLastError() == ERROR_BROKEN_PIPE) + m_lasterror = wxSTREAM_EOF; + else + m_lasterror = wxSTREAM_READ_ERROR; + } + return bytesRead; +} + +#endif // __WIN32__ + // ============================================================================ // implementation // ============================================================================ @@ -175,11 +268,83 @@ LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message, return DefWindowProc(hWnd, message, wParam, lParam); } } -#endif +#endif // Win32 -long wxExecute(const wxString& command, bool sync, wxProcess *handler) +long wxExecute(const wxString& cmd, bool sync, wxProcess *handler) { - wxCHECK_MSG( !!command, 0, wxT("empty command in wxExecute") ); + wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") ); + + wxString command; +#if wxUSE_IPC + // DDE hack: this is really not pretty, but we need to allow this for + // transparent handling of DDE servers in wxMimeTypesManager. Usually it + // returns the command which should be run to view/open/... a file of the + // given type. Sometimes, however, this command just launches the server + // and an additional DDE request must be made to really open the file. To + // keep all this well hidden from the application, we allow a special form + // of command: WX_DDE::DDE_SERVER:DDE_TOPIC:DDE_COMMAND in which + // case we execute just and process the rest below + wxString ddeServer, ddeTopic, ddeCommand; + static const size_t lenDdePrefix = 7; // strlen("WX_DDE:") + if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") ) + { + const wxChar *p = cmd.c_str() + 7; + while ( *p && *p != _T('#') ) + { + command += *p++; + } + + if ( *p ) + { + // skip '#' + p++; + } + else + { + wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + } + + while ( *p && *p != _T('#') ) + { + ddeServer += *p++; + } + + if ( *p ) + { + // skip '#' + p++; + } + else + { + wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + } + + while ( *p && *p != _T('#') ) + { + ddeTopic += *p++; + } + + if ( *p ) + { + // skip '#' + p++; + } + else + { + wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + } + + while ( *p ) + { + ddeCommand += *p++; + } + } + else +#endif // wxUSE_IPC + { + // no DDE + command = cmd; + } #if defined(__WIN32__) && !defined(__TWIN32__) // the old code is disabled because we really need a process handle @@ -213,7 +378,6 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) // only reached for space not inside quotes break; } - wxString commandArgs = pc; wxWindow *winTop = wxTheApp->GetTopWindow(); @@ -237,6 +401,51 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) return result; #else // 1 + + HANDLE h_readPipe[2]; + HANDLE h_writePipe[2]; + HANDLE h_oldreadPipe; + HANDLE h_oldwritePipe; + BOOL inheritHandles; + + // open the pipes to which child process IO will be redirected if needed + inheritHandles = FALSE; + if ( handler && handler->IsRedirected() ) + { + SECURITY_ATTRIBUTES security; + + security.nLength = sizeof(security); + security.lpSecurityDescriptor = NULL; + security.bInheritHandle = TRUE; + + if (! ::CreatePipe(&h_readPipe[0], &h_readPipe[1], &security, 0) ) + { + wxLogSysError(_("Can't create the inter-process read pipe")); + + return 0; + } + + if (! ::CreatePipe(&h_writePipe[0], &h_writePipe[1], &security, 0) ) + { + ::CloseHandle(h_readPipe[0]); + ::CloseHandle(h_readPipe[1]); + + wxLogSysError(_("Can't create the inter-process write pipe")); + + return 0; + } + + // We need to save the old stdio handles to restore them after the call + // to CreateProcess + h_oldreadPipe = GetStdHandle(STD_INPUT_HANDLE); + h_oldwritePipe = GetStdHandle(STD_OUTPUT_HANDLE); + + SetStdHandle(STD_INPUT_HANDLE, h_readPipe[0]); + SetStdHandle(STD_OUTPUT_HANDLE, h_writePipe[1]); + + inheritHandles = TRUE; + } + // create the process STARTUPINFO si; wxZeroMemory(si); @@ -250,7 +459,7 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) (wxChar *)command.c_str(), // full command line NULL, // security attributes: defaults for both NULL, // the process and its main thread - FALSE, // don't inherit handles + inheritHandles, // inherit handles if we need pipes CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED, // flags NULL, // environment (use the same) @@ -259,11 +468,33 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) &pi // process info ) == 0 ) { + if ( inheritHandles ) + { + ::CloseHandle(h_writePipe[0]); + ::CloseHandle(h_writePipe[1]); + ::CloseHandle(h_readPipe[0]); + ::CloseHandle(h_readPipe[1]); + } + wxLogSysError(_("Execution of command '%s' failed"), command.c_str()); return 0; } + // Restore the old stdio handles + if (inheritHandles) { + SetStdHandle(STD_INPUT_HANDLE, h_oldreadPipe); + SetStdHandle(STD_OUTPUT_HANDLE, h_oldwritePipe); + + ::CloseHandle(h_writePipe[1]); + ::CloseHandle(h_readPipe[0]); + // We can now initialize the wxStreams + wxInputStream *processOutput = new wxPipeInputStream(h_writePipe[0]); + wxOutputStream *processInput = new wxPipeOutputStream(h_readPipe[1]); + + handler->SetPipeStreams(processOutput, processInput); + } + // register the class for the hidden window used for the notifications if ( !gs_classForHiddenWindow ) { @@ -338,6 +569,22 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) return pi.dwProcessId; } +#if wxUSE_IPC + // second part of DDE hack: now establish the DDE conversation with the + // just launched process + if ( !!ddeServer ) + { + wxDDEClient client; + wxConnectionBase *conn = client.MakeConnection(_T(""), + ddeServer, + ddeTopic); + if ( !conn || !conn->Execute(ddeCommand) ) + { + wxLogError(_("Couldn't launch DDE server '%s'."), command.c_str()); + } + } +#endif // wxUSE_IPC + if ( !sync ) { // clean up will be done when the process terminates @@ -348,16 +595,17 @@ long wxExecute(const wxString& command, bool sync, wxProcess *handler) // waiting until command executed (disable everything while doing it) #if wxUSE_GUI - wxBeginBusyCursor(); - wxEnableTopLevelWindows(FALSE); + { + wxBusyCursor bc; + + wxWindowDisabler wd; #endif // wxUSE_GUI while ( data->state ) wxYield(); #if wxUSE_GUI - wxEnableTopLevelWindows(TRUE); - wxEndBusyCursor(); + } #endif // wxUSE_GUI DWORD dwExitCode = data->dwExitCode;