//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995 // // File: buildscr.cpp // // Contents: Implementation of the code which talks to the MTScript engine // when doing a distributed build using the build console. // //---------------------------------------------------------------------------- #include "scrproc.h" #include "build.h" #include "buildscr.h" #define INITGUID #include DEFINE_GUID(CLSID_LocalScriptedProcess, 0x854c316f,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b); DEFINE_GUID(IID_IScriptedProcess, 0x854c3171,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b); DEFINE_GUID(IID_IScriptedProcessSink, 0x854c3172,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b); DEFINE_GUID(CLSID_ObjectDaemon,0x854c3184,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b); DEFINE_GUID(IID_IConnectedMachine,0x854c316c,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b); DEFINE_GUID(IID_IObjectDaemon,0x854c3183,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b); #define MAX_RETRIES 2 HANDLE g_hMTEvent = NULL; HANDLE g_hMTThread = NULL; DWORD g_dwMTThreadId = 0; //+--------------------------------------------------------------------------- // // Function: WaitForResume // // Synopsis: Sends a "phase complete" message to the script engine and then // waits for it to tell us to resume (if specified). // // Arguments: [fPause] -- If TRUE, we wait for a resume command // [pe] -- Message to send to the script engine // //---------------------------------------------------------------------------- void WaitForResume(BOOL fPause, PROC_EVENTS pe) { if (g_hMTEvent) { HANDLE aHandles[2] = { g_hMTEvent, g_hMTThread }; ResetEvent(g_hMTEvent); PostThreadMessage(g_dwMTThreadId, pe, 0, 0); if (fPause) { // Wait until either the event object is signaled or the thread dies WaitForMultipleObjects(2, aHandles, FALSE, INFINITE); } } } //+--------------------------------------------------------------------------- // // Function: ExitMTScriptThread // // Synopsis: Tells the thread talking to the MTScript engine to exit. // //---------------------------------------------------------------------------- void ExitMTScriptThread() { if (g_hMTEvent) { PostThreadMessage(g_dwMTThreadId, PE_EXIT, 0, 0); WaitForSingleObject(g_hMTThread, INFINITE); CloseHandle(g_hMTThread); CloseHandle(g_hMTEvent); } } //+--------------------------------------------------------------------------- // // Function: SendStatus // // Synopsis: Sends a status message to the MTScript engine with the // current number of errors, warnings, and completed files. // // Arguments: [pSP] -- Pointer to MTScript engine interface // //---------------------------------------------------------------------------- void SendStatus(IScriptedProcess *pSP) { wchar_t achBuf[300]; long lRet; static ULONG cErrorsPrev = MAXDWORD; static ULONG cWarnPrev = MAXDWORD; static ULONG cFilesPrev = MAXDWORD; ULONG cErrors = NumberCompileErrors + NumberLibraryErrors + NumberLinkErrors + NumberBinplaceErrors; ULONG cWarn = NumberCompileWarnings + NumberLibraryWarnings + NumberLinkWarnings + NumberBinplaceWarnings; ULONG cFiles = NumberCompiles + NumberLibraries + NumberLinks /* + NumberBinplaces */; // Only send status if it's changed since last time we did it. if ( cErrors != cErrorsPrev || cWarn != cWarnPrev || cFiles != cFilesPrev) { cErrorsPrev = cErrors; cWarnPrev = cWarn; cFilesPrev = cFiles; wsprintfW(achBuf, L"errors=%d,warnings=%d,files=%d", cErrors, cWarn, cFiles); pSP->SendData(L"status", achBuf, &lRet); } } //+--------------------------------------------------------------------------- // // Function: HandleMessage // // Synopsis: Handles a message that has come across our message queue. // // Arguments: [pmsg] -- Message // [pSP] -- Pointer to MTScript engine interface // //---------------------------------------------------------------------------- BOOL HandleMessage(MSG *pmsg, IScriptedProcess *pSP) { long lRet; HRESULT hr = S_OK; switch (pmsg->message) { case PE_PASS0_COMPLETE: SendStatus(pSP); hr = pSP->SendData(L"pass 0 complete", L"", &lRet); break; case PE_PASS1_COMPLETE: SendStatus(pSP); hr = pSP->SendData(L"pass 1 complete", L"", &lRet); break; case PE_PASS2_COMPLETE: SendStatus(pSP); hr = pSP->SendData(L"pass 2 complete", L"", &lRet); break; case PE_EXIT: SendStatus(pSP); hr = pSP->SendData(L"build complete", L"", &lRet); return TRUE; break; } if (hr) { BuildErrorRaw("\nBUILD: Communication with script engine failed: %x", hr); } return (hr) ? TRUE : FALSE; } const DWORD UPDATE_INTERVAL = 2 * 1000; // Update every 2 seconds //+--------------------------------------------------------------------------- // // Function: MTScriptThread // // Synopsis: Thread entrypoint. Initializes and then sits around // handling various events. // // Arguments: [pv] -- Not used. // //---------------------------------------------------------------------------- DWORD WINAPI MTScriptThread(LPVOID pv) { HRESULT hr; IScriptedProcess * pSP = NULL; wchar_t achBuf[100]; MSG msg; DWORD dwRet; CProcessSink cps; BOOL fExit = FALSE; int cRetries = 0; BuildMsg("Establishing connection with Script engine...\n"); LogMsg("Establishing connection with Script engine...\n"); // Force Windows to create a message queue for this thread, since we will // be communicated to via PostThreadMessage. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); // If anything fails we just quit this thread and communication with // the MTScript engine won't happen. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); if (hr) { BuildErrorRaw("BUILD: CoInitializeEx failed with %x\n", hr); goto Cleanup; } hr = S_FALSE; while (hr) { pSP = NULL; IObjectDaemon *pIObjectDaemon; hr = CoCreateInstance(CLSID_ObjectDaemon, NULL, CLSCTX_SERVER, IID_IObjectDaemon, (LPVOID*)&pIObjectDaemon); if (!hr) { IDispatch *pIDispatch; BSTR bstrProgID = SysAllocString(L"MTScript.Remote"); BSTR bstrIdentity = SysAllocString(_wgetenv(L"__MTSCRIPT_ENV_IDENTITY")); hr = pIObjectDaemon->OpenInterface(bstrIdentity, bstrProgID, (BOOL)FALSE, (IDispatch**)&pIDispatch); if (!hr) { IConnectedMachine *pIConnectedMachine; hr = pIDispatch->QueryInterface(IID_IConnectedMachine, (LPVOID*)&pIConnectedMachine); if (!hr) { hr = pIConnectedMachine->CreateIScriptedProcess(GetCurrentProcessId(), (wchar_t *)_wgetenv(L"__MTSCRIPT_ENV_ID"), (IScriptedProcess **)&pSP); pIConnectedMachine->Release(); } else { BuildMsg("CreateIScriptedProcess failed with %x.\n", hr); LogMsg("CreateIScriptedProcess failed with %x.\n", hr); } pIDispatch->Release(); } else { BuildMsg("OpenInterface failed with %x.\n", hr); LogMsg("OpenInterface failed with %x.\n", hr); } SysFreeString(bstrProgID); SysFreeString(bstrIdentity); pIObjectDaemon->Release(); } else { BuildMsg("CoCreateInstance failed with %x.\n", hr); LogMsg("CoCreateInstance failed with %x.\n", hr); } if (!hr) { hr = pSP->SetProcessSink(&cps); if (hr) { BuildMsg("SetProcessSink failed with %x.\n", hr); LogMsg("SetProcessSink failed with %x.\n", hr); } } if (hr) { if (cRetries >= MAX_RETRIES) { BuildErrorRaw("BUILD: FATAL: Connection to script engine could not be established. (%x)\n", hr); goto Cleanup; } if (pSP) { pSP->Release(); pSP = NULL; } BuildMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries); LogMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries); Sleep(500); cRetries++; } } BuildMsg("Connection to script engine established...\n"); LogMsg("Connection to script engine established...\r\n"); // Tell build.c that it can continue SetEvent(g_hMTEvent); while (TRUE) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (HandleMessage(&msg, pSP)) { fExit = TRUE; } } if (fExit) { break; } dwRet = MsgWaitForMultipleObjects(0, NULL, FALSE, UPDATE_INTERVAL, QS_ALLINPUT); if (dwRet == WAIT_OBJECT_0) { // A message is coming through on our message queue. Just loop // around. } else if (dwRet == WAIT_TIMEOUT) { SendStatus(pSP); } else { // MWFMO failed. Just bail out. break; } } Cleanup: if (pSP) { pSP->SetProcessSink(NULL); pSP->Release(); } CoUninitialize(); if (hr) { g_hMTThread = NULL; } SetEvent(g_hMTEvent); return 0; } // *********************************************************************** // // CProcessSink implementation // // We hand this class to the MTScript engine so it can communicate back // to us. // // *********************************************************************** CProcessSink::CProcessSink() { _ulRefs = 1; } HRESULT CProcessSink::QueryInterface(REFIID riid, LPVOID *ppv) { if (riid == IID_IUnknown || riid == IID_IScriptedProcessSink) { *ppv = (IScriptedProcessSink*)this; } else { *ppv = NULL; return E_NOINTERFACE; } ((IUnknown *)*ppv)->AddRef(); return S_OK; } ULONG CProcessSink::AddRef() { return InterlockedIncrement((long*)&_ulRefs); } ULONG CProcessSink::Release() { if (InterlockedDecrement((long*)&_ulRefs) == 0) { _ulRefs = 0xFF; delete this; return 0; } return _ulRefs; } //+--------------------------------------------------------------------------- // // Member: CProcessSink::RequestExit, public // // Synopsis: Called when the MTScript engine wants us to quit. If we don't, // it will terminate us. // //---------------------------------------------------------------------------- HRESULT CProcessSink::RequestExit() { // There is no easy way to tell build.exe to abort. We'll just let // MTScript terminate us. return S_OK; } //+--------------------------------------------------------------------------- // // Member: CProcessSink::ReceiveData, public // // Synopsis: Called when the MTScript engine wants to send us a message. // // Arguments: [pszType] -- String giving the message // [pszData] -- String giving data associated with the message. // [plReturn] -- A place we can return a value back. // //---------------------------------------------------------------------------- HRESULT CProcessSink::ReceiveData(wchar_t *pszType, wchar_t *pszData, long *plReturn) { *plReturn = 0; if (wcscmp(pszType, L"resume") == 0) { SetEvent(g_hMTEvent); } else { *plReturn = -1; // Signals an error } return S_OK; }