//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995 // // File: proccomm.cxx // // Contents: Implementation of the CProcessComm class // //---------------------------------------------------------------------------- #include "headers.hxx" DeclareTag(tagProcComm, "MTScript", "IScriptedProcess communication"); CProcessComm::CProcessComm(CMTScript *pMT) { _ulRefs = 1; _pMT = pMT; Assert(_pSink == NULL); Assert(_pSH == NULL); Assert(_pProc == NULL); TraceTag((tagProcComm, "CProcessComm this(%x)", this)); } CProcessComm::~CProcessComm() { TraceTag((tagProcComm, "%p: Destroyed this(%x)", _pProc, this)); if (_pProc) { _pProc->SetProcComm(NULL); } ReleaseInterface(_pSink); ReleaseInterface(_pSH); ReleaseInterface(_pProc); } HRESULT CProcessComm::QueryInterface(REFIID iid, void **ppv) { if (iid == IID_IUnknown || iid == IID_IScriptedProcess) { *ppv = (IScriptedProcess *)this; } else { *ppv = NULL; return E_NOINTERFACE; } ((IUnknown *)*ppv)->AddRef(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CProcessComm::SetProcessID, public // // Synopsis: Called by the remote process to tell us who it is. We use // the information to match it with the CProcessThread and // CScriptHost object that created it. // // Arguments: [lProcessID] -- Process ID of the calling process // [pszEnvID] -- Value of the __MTSCRIPT_ENV_ID environment // variable. // // Returns: S_OK, E_INVALIDARG if a match could not be made. // //---------------------------------------------------------------------------- STDMETHODIMP CProcessComm::SetProcessID(long lProcessID, wchar_t *pszEnvID) { CProcessThread **ppProc; int cProcs; long lEnvID; wchar_t *pch; if (!pszEnvID) { return E_INVALIDARG; } if (_pProc) { TraceTag((tagProcComm, "%p: Got duplicate call to SetProcessID! (id=%d)", _pProc, lProcessID)); return E_UNEXPECTED; } lEnvID = wcstol(pszEnvID, &pch, 10); LOCK_LOCALS(_pMT); for (ppProc = _pMT->_aryProcesses, cProcs = _pMT->_aryProcesses.Size(); cProcs; ppProc++, cProcs--) { if ((*ppProc)->IsOwner(lProcessID, lEnvID)) { break; } } if (cProcs == 0) { return E_INVALIDARG; } // We don't allow more than one process to connect to a single // CProcessThread. This could happen if more than one child process of // the one we launched with RunLocalCommand tries to connect. All but the // first one will get an error back. if ((*ppProc)->GetProcComm()) { TraceTag((tagProcComm, "%p: Got duplicate call to SetProcessID! (id=%d)", *ppProc, lProcessID)); return E_UNEXPECTED; } _pProc = *ppProc; _pProc->AddRef(); _pSH = _pProc->ScriptHost(); _pSH->AddRef(); _pProc->SetProcComm(this); TraceTag((tagProcComm, "Proc:%p, this:%p: Received SetProcessID call: %d, %ls", _pProc, this, lProcessID, pszEnvID)); _pMT->PostToThread(_pSH, MD_PROCESSCONNECTED, &_pProc, sizeof(CProcessComm*)); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CProcessComm::SendData, public // // Synopsis: Called by the remote process when it wants to fire an event // into the script. // // Arguments: [pszType] -- String giving name of data // [pszData] -- String giving data // [plReturn] -- Return value from event handler // // Returns: HRESULT // // Notes: This uses the same data structures as CMachine::Exec and is // basically the same code. // //---------------------------------------------------------------------------- STDMETHODIMP CProcessComm::SendData(wchar_t * pszType, wchar_t * pszData, long * plReturn) { // We create an event object for each call on this method. While this // may have a cost, it makes this method thread-safe. If we cached an // event object then we would have to synchronize access to that event // object which could be even more expensive. MACHPROC_EVENT_DATA med; MACHPROC_EVENT_DATA * pmed; VARIANT vRet; VARIANT vLong; HRESULT hr = S_OK; TraceTag((tagProcComm, "%p: SendData call received: (%ls, %ls)", _pProc, pszType, pszData)); VariantInit(&vRet); VariantInit(&vLong); if (!_pSH) { return E_UNEXPECTED; } if (!plReturn) { return E_INVALIDARG; } med.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (med.hEvent == NULL) { return HRESULT_FROM_WIN32(GetLastError()); } med.bstrCmd = SysAllocString(pszType); med.bstrParams = SysAllocString(pszData); med.dwProcId = _pProc->ProcId(); med.pvReturn = &vRet; med.dwGITCookie = 0; med.hrReturn = S_OK; pmed = &med; _pMT->PostToThread(_pSH, MD_PROCESSDATA, (LPVOID)&pmed, sizeof(MACHPROC_EVENT_DATA*)); // We can do WaitForSingleObject because we are in OLE's multi-threaded // apartment and don't need to handle messages from our event loop. WaitForSingleObject(med.hEvent, INFINITE); if (med.hrReturn != S_OK) { hr = med.hrReturn; goto Cleanup; } if (VariantChangeType(&vLong, &vRet, 0, VT_I4) != S_OK) { *plReturn = -1; } else { *plReturn = V_I4(&vLong); } Cleanup: VariantClear(&vRet); VariantClear(&vLong); SysFreeString(med.bstrCmd); SysFreeString(med.bstrParams); CloseHandle(med.hEvent); TraceTag((tagProcComm, "%p: SendData is returning %x", _pProc, hr)); return hr; } //+--------------------------------------------------------------------------- // // Member: CProcessComm::SetExitCode, public // // Synopsis: Sets the exit code which will be given to the script for // this process. This will override the actual exit code of // the process. // // Arguments: [lExitCode] -- New exit code. // //---------------------------------------------------------------------------- STDMETHODIMP CProcessComm::SetExitCode(long lExitCode) { if (!_pProc) { return E_UNEXPECTED; } TraceTag((tagProcComm, "%p: Process set exit code of %d", _pProc, lExitCode)); _pProc->SetExitCode((DWORD)lExitCode); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CProcessComm::SetProcessSink, public // // Synopsis: Sets the sink interface so the script can call back into // the remote process. // // Arguments: [pSink] -- Sink interface // // Returns: HRESULT // // Notes: Clear the sink by calling SetProcessSink with NULL before // shutting down. // //---------------------------------------------------------------------------- STDMETHODIMP CProcessComm::SetProcessSink(IScriptedProcessSink *pSink) { ReleaseInterface(_pSink); _pSink = pSink; TraceTag((tagProcComm, "%p: Received new process sink (%p)", _pProc, pSink)); if (pSink) { pSink->AddRef(); } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CProcessComm::SendToProcess, public // // Synopsis: Sends a command to the remote process as requested by the // script engine. // // Arguments: [pmed] -- Pointer to cross-thread data structure. // //---------------------------------------------------------------------------- void CProcessComm::SendToProcess(MACHPROC_EVENT_DATA *pmed) { long lReturn = 0; HRESULT hr = S_OK; TraceTag((tagProcComm, "%p: Making call to ReceiveData. Params=(%ls, %ls)", _pProc, pmed->bstrCmd, pmed->bstrParams)); if (_pSink) { hr = _pSink->ReceiveData(pmed->bstrCmd, pmed->bstrParams, &lReturn); } V_VT(pmed->pvReturn) = VT_I4; V_I4(pmed->pvReturn) = lReturn; TraceTag((tagProcComm, "%p: Call to ReceiveData returned %x", _pProc, hr)); pmed->hrReturn = hr; SetEvent(pmed->hEvent); return; }