336 lines
8.4 KiB
C++
336 lines
8.4 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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;
|
||
|
}
|