|
|
//+---------------------------------------------------------------------------
//
// 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 <guiddef.h>
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 = RunningTotals.NumberCompileErrors + RunningTotals.NumberLibraryErrors + RunningTotals.NumberLinkErrors + RunningTotals.NumberBinplaceErrors; ULONG cWarn = RunningTotals.NumberCompileWarnings + RunningTotals.NumberLibraryWarnings + RunningTotals.NumberLinkWarnings + RunningTotals.NumberBinplaceWarnings; ULONG cFiles = RunningTotals.NumberCompiles + RunningTotals.NumberLibraries + RunningTotals.NumberLinks; /* + RunningTotals.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 != S_OK) { BuildErrorRaw("\nBUILD: Communication with script engine failed: %x", hr); }
return (hr != S_OK) ? 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 != S_OK) { BuildErrorRaw("BUILD: CoInitializeEx failed with %x\n", hr); goto Cleanup; }
hr = S_FALSE;
while (hr != S_OK) { pSP = NULL; IObjectDaemon *pIObjectDaemon; hr = CoCreateInstance(CLSID_ObjectDaemon, NULL, CLSCTX_SERVER, IID_IObjectDaemon, (LPVOID*)&pIObjectDaemon);
if (hr == S_OK) { 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 == S_OK) { IConnectedMachine *pIConnectedMachine; hr = pIDispatch->QueryInterface(IID_IConnectedMachine, (LPVOID*)&pIConnectedMachine); if (hr == S_OK) { 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 == S_OK) { hr = pSP->SetProcessSink(&cps); if (hr != S_OK) { BuildMsg("SetProcessSink failed with %x.\n", hr); LogMsg("SetProcessSink failed with %x.\n", hr); } }
if (hr != S_OK) { 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 != S_OK) { 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; }
|