//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995 // // File: machine.cxx // // Contents: Implementation of the CMachine class // //---------------------------------------------------------------------------- #include "headers.hxx" DeclareTag(tagMachine, "MTScript", "Monitor IConnectedMachine"); // *********************************************************************** // // CMachConnectPoint // // *********************************************************************** CMachConnectPoint::CMachConnectPoint(CMachine *pMach) { _ulRefs = 1; _pMachine = pMach; _pMachine->AddRef(); } CMachConnectPoint::~CMachConnectPoint() { _pMachine->Release(); } HRESULT CMachConnectPoint::QueryInterface(REFIID iid, void **ppv) { if (iid == IID_IUnknown || iid == IID_IConnectionPoint) { *ppv = (IConnectionPoint *)this; } else { *ppv = NULL; return E_NOINTERFACE; } ((IUnknown *)*ppv)->AddRef(); return S_OK; } HRESULT CMachConnectPoint::GetConnectionInterface(IID * pIID) { *pIID = DIID_DRemoteMTScriptEvents; return S_OK; } HRESULT CMachConnectPoint::GetConnectionPointContainer(IConnectionPointContainer ** ppCPC) { *ppCPC = _pMachine; (*ppCPC)->AddRef(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CMachConnectPoint::Advise, public // // Synopsis: Remembers interface pointers that we want to fire events // through. // // Arguments: [pUnkSink] -- Pointer to remember // [pdwCookie] -- Place to put cookie for Unadvise // // Returns: HRESULT // //---------------------------------------------------------------------------- HRESULT CMachConnectPoint::Advise(IUnknown *pUnkSink, DWORD *pdwCookie) { IDispatch *pDisp; HRESULT hr; TraceTag((tagMachine, "Advising new machine sink: %p", pUnkSink)); hr = pUnkSink->QueryInterface(IID_IDispatch, (LPVOID*)&pDisp); if (hr) { TraceTag((tagMachine, "Could not get IDispatch pointer on sink! (%x)", hr)); return hr; } CMachine::LOCK_MACH_LOCALS(_pMachine); hr = _pMachine->_aryDispSink.Append(pDisp); if (hr) { TraceTag((tagMachine, "Error appending sink to array!")); RRETURN(hr); } *pdwCookie = (DWORD)pDisp; return S_OK; } //+--------------------------------------------------------------------------- // // Member: CMachConnectPoint::Unadvise, public // // Synopsis: Forgets a pointer we remembered during Advise. // // Arguments: [dwCookie] -- Cookie returned from Advise // // Returns: HRESULT // //---------------------------------------------------------------------------- HRESULT CMachConnectPoint::Unadvise(DWORD dwCookie) { int i; TraceTag((tagMachine, "Unadvising machine sink: %p", dwCookie)); CMachine::LOCK_MACH_LOCALS(_pMachine); i = _pMachine->_aryDispSink.Find((IDispatch*)dwCookie); if (i != -1) { _pMachine->_aryDispSink.ReleaseAndDelete(i); } else return E_INVALIDARG; return S_OK; } HRESULT CMachConnectPoint::EnumConnections(LPENUMCONNECTIONS * ppEnum) { *ppEnum = NULL; RRETURN(E_NOTIMPL); } // *********************************************************************** // // CMachine // // *********************************************************************** CMachine::CMachine(CMTScript *pMT, ITypeInfo *pTIMachine) { _ulRefs = 1; _pMT = pMT; TraceTag((tagMachine, "%p: CMachine object being constructed", this)); Assert(pTIMachine); _pTypeInfoIMachine = pTIMachine; _pTypeInfoIMachine->AddRef(); InitializeCriticalSection(&_cs); } CMachine::~CMachine() { ReleaseInterface(_pTypeInfoIMachine); DeleteCriticalSection(&_cs); } HRESULT CMachine::QueryInterface(REFIID iid, void **ppvObj) { if (iid == IID_IConnectedMachine || iid == IID_IUnknown || iid == IID_IDispatch) { *ppvObj = (IConnectedMachine *)this; } else if (iid == IID_IConnectionPointContainer) { *ppvObj = (IConnectionPointContainer *)this; } else { *ppvObj = NULL; return E_NOINTERFACE; } ((IUnknown *)*ppvObj)->AddRef(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CMachine::Init, public // // Synopsis: Used to do initialization that may fail // //---------------------------------------------------------------------------- BOOL CMachine::Init() { return CThreadComm::Init(); } //+--------------------------------------------------------------------------- // // Member: CMachine::ThreadMain, public // // Synopsis: Main loop for this thread. Handles messages coming from other // threads. // //---------------------------------------------------------------------------- DWORD CMachine::ThreadMain() { DWORD dwRet; BOOL fExit = FALSE; SetName("CMachine"); ThreadStarted(S_OK); TraceTag((tagMachine, "CMachine thread started")); while (!fExit) { dwRet = WaitForSingleObject(_hCommEvent, INFINITE); if (dwRet == WAIT_OBJECT_0) { fExit = HandleThreadMessage(); } else { AssertSz(FALSE, "FATAL: WaitForSingleObject failed!"); fExit = TRUE; } } TraceTag((tagMachine, "CMachine thread exiting")); return 0; } //+--------------------------------------------------------------------------- // // Member: CMachine::HandleThreadMessage, public // // Synopsis: Handles messages from other threads. // //---------------------------------------------------------------------------- BOOL CMachine::HandleThreadMessage() { VERIFY_THREAD(); THREADMSG tm; BYTE bData[MSGDATABUFSIZE]; DWORD cbData; BOOL fRet = FALSE; while (GetNextMsg(&tm, (void *)bData, &cbData)) { switch (tm) { case MD_NOTIFYSCRIPT: { VARIANT *pvar = *(VARIANT**)bData; Assert(V_VT(&pvar[0]) == VT_BSTR); FireScriptNotify(V_BSTR(&pvar[0]), pvar[1]); VariantClear(&pvar[0]); VariantClear(&pvar[1]); delete [] pvar; } break; case MD_PLEASEEXIT: fRet = TRUE; break; default: AssertSz(FALSE, "CMachine got a message it couldn't handle!"); break; } } return fRet; } //--------------------------------------------------------------------------- // // Member: CMachine::EnumConnectionPoints, IConnectionPointContainer // //--------------------------------------------------------------------------- HRESULT CMachine::EnumConnectionPoints(LPENUMCONNECTIONPOINTS *) { return E_NOTIMPL; } //--------------------------------------------------------------------------- // // Member: CMachine::FindConnectionPoint, IConnectionPointContainer // //--------------------------------------------------------------------------- HRESULT CMachine::FindConnectionPoint(REFIID iid, LPCONNECTIONPOINT* ppCpOut) { HRESULT hr; if (iid == DIID_DRemoteMTScriptEvents || iid == IID_IDispatch) { *ppCpOut = new CMachConnectPoint(this); hr = *ppCpOut ? S_OK : E_OUTOFMEMORY; } else { hr = E_NOINTERFACE; } RRETURN(hr); } //--------------------------------------------------------------------------- // // Member: CMachine::GetTypeInfo, IDispatch // //--------------------------------------------------------------------------- HRESULT CMachine::GetTypeInfo(UINT itinfo, ULONG lcid, ITypeInfo ** pptinfo) { *pptinfo = _pTypeInfoIMachine; (*pptinfo)->AddRef(); return S_OK; } //--------------------------------------------------------------------------- // // Member: CMachine::GetTypeInfoCount, IDispatch // //--------------------------------------------------------------------------- HRESULT CMachine::GetTypeInfoCount(UINT * pctinfo) { *pctinfo = 1; return S_OK; } //--------------------------------------------------------------------------- // // Member: CMachine::GetIDsOfNames, IDispatch // //--------------------------------------------------------------------------- HRESULT CMachine::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return _pTypeInfoIMachine->GetIDsOfNames(rgszNames, cNames, rgdispid); } //--------------------------------------------------------------------------- // // Member: CMachine::Invoke, IDispatch // //--------------------------------------------------------------------------- HRESULT CMachine::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { return _pTypeInfoIMachine->Invoke((IConnectedMachine *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } // ************************************************************************* //+--------------------------------------------------------------------------- // // Member: CMachine::FireScriptNotify, public // // Synopsis: Fires the script notify event on all objects connected to // our IConnectedMachine object that have requested to receive // events (through IConnectionPoint::Advise). // // Arguments: [bstrIdent] -- Parameter of event // [vInfo] -- Parameter of event // // Returns: HRESULT // // Notes: This method is thread-safe and can be called from any thread. // //---------------------------------------------------------------------------- HRESULT CMachine::FireScriptNotify(BSTR bstrIdent, VARIANT vInfo) { HRESULT hr; IDispatch **ppDisp; int i; DISPPARAMS dp; EXCEPINFO ei; UINT uArgErr = 0; VARIANT varg[2]; CStackPtrAry arySinks; // Since it may take some time to fire the events, and we don't want // to keep the array locked that whole time, we make a copy of the array. // This will also allow a sink to unadvise itself while handling the event // without deadlocking. { LOCK_MACH_LOCALS(this); // Check for no sinks. No use going to all this work if there's no one // listening. if (_aryDispSink.Size() == 0) return S_OK; hr = arySinks.Copy(_aryDispSink, TRUE); if (hr) RRETURN(hr); } // Set up the event parameters VariantInit(&varg[0]); VariantInit(&varg[1]); // Params are in order from last to first hr = VariantCopy(&varg[0], &vInfo); if (hr) return hr; V_VT(&varg[1]) = VT_BSTR; V_BSTR(&varg[1]) = bstrIdent; dp.rgvarg = varg; dp.cArgs = 2; dp.rgdispidNamedArgs = NULL; dp.cNamedArgs = 0; // We don't use the same critical section here so _aryDispSink can be // manipulated while we're firing events. However, we still don't want // more than one thread firing events at the same time. TraceTag((tagMachine, "About to fire OnScriptNotify(%ls) on %d sinks...", bstrIdent, arySinks.Size())); for (i = arySinks.Size(), ppDisp = arySinks; i > 0; i--, ppDisp++) { hr = (*ppDisp)->Invoke( DISPID_RemoteMTScript_OnScriptNotify, IID_NULL, 0, DISPATCH_METHOD, &dp, NULL, &ei, &uArgErr); if (hr) { // If the call failed, unadvise so we don't keep trying. TraceTag((tagError, "OnScriptNotify event call returned %x! Unadvising...", hr)); // If the connection went down temporarily, don't unadvise. if (hr != HRESULT_FROM_WIN32(RPC_X_BAD_STUB_DATA) && hr != HRESULT_FROM_WIN32(RPC_S_COMM_FAILURE)) { LOCK_MACH_LOCALS(this); int index = _aryDispSink.Find(*ppDisp); Assert(index != -1); _aryDispSink.ReleaseAndDelete(index); } } } TraceTag((tagMachine, "Done firing OnScriptNotify(%ls).", bstrIdent)); return S_OK; } // ************************************************************************* STDMETHODIMP CMachine::Exec(BSTR bstrCmd, BSTR bstrParams, VARIANT *pvData) { // 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; HRESULT hr = S_OK; if (!pvData) { return E_INVALIDARG; } TraceTag((tagMachine, "Exec call received: (%ls, %ls)", bstrCmd, bstrParams)); VariantInit(pvData); med.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (med.hEvent == NULL) { return HRESULT_FROM_WIN32(GetLastError()); } med.bstrCmd = bstrCmd; med.bstrParams = bstrParams; med.dwProcId = 0; med.pvReturn = pvData; med.dwGITCookie = 0; med.hrReturn = S_OK; pmed = &med; HANDLE ahEvents[2]; ahEvents[0] = med.hEvent; CScriptHost *pScript = _pMT->GetPrimaryScript(); if (!pScript) { med.hrReturn = E_FAIL; goto Cleanup; } ahEvents[1] = pScript->hThread(); _pMT->PostToThread(pScript, MD_MACHEVENTCALL, (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. DWORD dwWait; dwWait = WaitForMultipleObjects(2, ahEvents, FALSE, INFINITE); if (dwWait != WAIT_OBJECT_0) // Thread exit { med.hrReturn = E_FAIL; goto Cleanup; } if (med.hrReturn != S_OK) { hr = med.hrReturn; goto Cleanup; } // See if the return value was an IDispatch ptr. If so, grab the pointer // out of the GlobalInterfaceTable. if (V_VT(pvData) == VT_DISPATCH) { IDispatch *pDisp; AssertSz(med.dwGITCookie != 0, "FATAL: Itf pointer improperly marshalled"); hr = _pMT->_pGIT->GetInterfaceFromGlobal(med.dwGITCookie, IID_IDispatch, (LPVOID*)&pDisp); if (!hr) { V_VT(pvData) = VT_DISPATCH; V_DISPATCH(pvData) = pDisp; } _pMT->_pGIT->RevokeInterfaceFromGlobal(med.dwGITCookie); } Cleanup: CloseHandle(med.hEvent); TraceTag((tagMachine, "Exec call returning %x", hr)); return hr; } STDMETHODIMP CMachine::get_PublicData(VARIANT *pvData) { HRESULT hr = S_OK; VariantInit(pvData); TraceTag((tagMachine, "Remote machine asking for PublicData")); LOCK_LOCALS(_pMT); if (V_VT(&_pMT->_vPublicData) == VT_DISPATCH) { IDispatch *pDisp; Assert(_pMT->_dwPublicDataCookie != 0); hr = _pMT->_pGIT->GetInterfaceFromGlobal(_pMT->_dwPublicDataCookie, IID_IDispatch, (LPVOID*)&pDisp); if (!hr) { V_VT(pvData) = VT_DISPATCH; V_DISPATCH(pvData) = pDisp; } } else { hr = VariantCopy(pvData, &_pMT->_vPublicData); } return hr; } STDMETHODIMP CMachine::get_Name(BSTR *pbstrName) { TCHAR achBuf[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwLength = ARRAY_SIZE(achBuf); GetComputerName(achBuf, &dwLength); *pbstrName = SysAllocString(achBuf); return (pbstrName) ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP CMachine::get_Platform(BSTR *pbstrPlatform) { SYSTEM_INFO si; GetSystemInfo(&si); TCHAR *pchPlatform; if (!pbstrPlatform) return E_POINTER; switch (si.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: pchPlatform = L"x86"; break; case PROCESSOR_ARCHITECTURE_ALPHA: pchPlatform = L"alpha"; break; case PROCESSOR_ARCHITECTURE_MIPS: pchPlatform = L"mips"; break; case PROCESSOR_ARCHITECTURE_IA64: pchPlatform = L"ia64"; break; case PROCESSOR_ARCHITECTURE_ALPHA64: pchPlatform = L"alpha64"; break; case PROCESSOR_ARCHITECTURE_PPC: pchPlatform = L"powerpc"; break; case PROCESSOR_ARCHITECTURE_UNKNOWN: default: pchPlatform = L"unknown"; break; } *pbstrPlatform = SysAllocString(pchPlatform); if (!*pbstrPlatform) return E_OUTOFMEMORY; return S_OK; } STDMETHODIMP CMachine::get_OS(BSTR *pbstrOS) { OSVERSIONINFO os; WCHAR * pchOS = L"Unknown"; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!pbstrOS) return E_POINTER; GetVersionEx(&os); switch (os.dwPlatformId) { case VER_PLATFORM_WIN32_WINDOWS: if ( os.dwMajorVersion > 4 || ( os.dwMajorVersion == 4 && os.dwMinorVersion > 0)) { pchOS = L"Win98"; } else { pchOS = L"Win95"; } break; case VER_PLATFORM_WIN32_NT: if (os.dwMajorVersion == 4) { pchOS = L"NT4"; } else { pchOS = L"Win2000"; } break; } *pbstrOS = SysAllocString(pchOS); if (!*pbstrOS) return E_OUTOFMEMORY; return S_OK; } STDMETHODIMP CMachine::get_MajorVer(long *plMajorVer) { if (!plMajorVer) return E_POINTER; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&os); *plMajorVer = os.dwMajorVersion; return S_OK; } STDMETHODIMP CMachine::get_MinorVer(long *plMinorVer) { if (!plMinorVer) return E_POINTER; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&os); *plMinorVer = os.dwMinorVersion; return S_OK; } STDMETHODIMP CMachine::get_BuildNum(long *plBuildNum) { if (!plBuildNum) return E_POINTER; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&os); *plBuildNum = os.dwBuildNumber; return S_OK; } STDMETHODIMP CMachine::get_PlatformIsNT(VARIANT_BOOL *pfIsNT) { if (!pfIsNT) return E_POINTER; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&os); *pfIsNT = (os.dwPlatformId == VER_PLATFORM_WIN32_NT) ? VB_TRUE : VB_FALSE; return S_OK; } STDMETHODIMP CMachine::get_ServicePack(BSTR *pbstrServicePack) { if (!pbstrServicePack) return E_POINTER; OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&os); *pbstrServicePack = SysAllocString(os.szCSDVersion); return (pbstrServicePack) ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP CMachine::get_HostMajorVer(long *plMajorVer) { if (!plMajorVer) return E_POINTER; *plMajorVer = IConnectedMachine_lVersionMajor; return S_OK; } STDMETHODIMP CMachine::get_HostMinorVer(long *plMinorVer) { if (!plMinorVer) return E_POINTER; *plMinorVer = IConnectedMachine_lVersionMinor; return S_OK; } STDMETHODIMP CMachine::get_StatusValue(long nIndex, long *pnStatus) { if (!pnStatus) return E_POINTER; return _pMT->get_StatusValue(nIndex, pnStatus); }