|
|
//--------------------------------------------------------------------
// HWProv.cpp - sample code
// Copyright (C) Microsoft Corporation, 2001
//
// Created by: Duncan Bryce (duncanb), 9-13-2001
//
// A sample hardware provider
//
#include "pch.h"
//
// This provider will read from hardware clocks which can be configured purely
// through serial port parametners. The clocks must periodically push data
// onto the serial cable, in a format specified by the "Format" reg value
// Furthermore, we will only accept input using ASCII character values.
//
// TODO: unicode support?
// need polling character?
// verify which clocks we work with
// dynamically allocate format string based on calculated length?
// why is 32seconds the irregular interval??
// ensure that w32time calls into providers in a single-threaded fashion
// should support placement of "time marker"?
//
struct HWProvState { TimeProvSysCallbacks tpsc;
// Configuration information
DCB dcb; HANDLE hComPort; WCHAR *wszCommPort; char cSampleInputBuffer[256]; // BUGBUG: add assert to ensure we cant overrun this buffer
WCHAR *wszRefID;
// Parser information
HANDLE hParser; WCHAR *wszFormat; DWORD dwSampleSize;
// Synchronization
HANDLE hStopEvent; HANDLE hProvThread_EnterOrLeaveThreadTrapEvent; HANDLE hProvThread_ThreadTrapTransitionCompleteEvent; HANDLE hProvThread; CRITICAL_SECTION csProv; bool bIsCsProvInitialized;
// Time sample information
bool bSampleIsValid; NtTimeEpoch teSampleReceived; TimeSample tsCurrent; };
struct HWProvConfig { DWORD dwBaudRate; //
DWORD dwByteSize; //
DWORD dwParity; // 0-4=no,odd,even,mark,space
DWORD dwStopBits; //
WCHAR *wszCommPort; //
WCHAR *wszFormat; //
WCHAR *wszRefID; //
};
HWProvState *g_phpstate = NULL;
void __cdecl SeTransFunc(unsigned int u, EXCEPTION_POINTERS* pExp) { throw SeException(u); }
HRESULT TrapThreads(bool bEnter) { DWORD dwWaitResult; HRESULT hr;
// BUGBUG: need critsec to serialize TrapThreads?
// BUGBUG: should the trap threads events be manual?
// if auto, we'll try only once, but code simpler/
// if manual, we'll keep trying on failure, but might be expensive!
if (!SetEvent(g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent)) { _JumpLastError(hr, error, "SetEvent"); } dwWaitResult = WaitForSingleObject(g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent, INFINITE); if (WAIT_FAILED == dwWaitResult) { _JumpLastError(hr, error, "WaitForSingleObject"); } hr = S_OK; error: return hr; }
HRESULT ThreadTrap() { DWORD dwWaitResult; HRESULT hr; if (!SetEvent(g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent)) { _JumpLastError(hr, error, "SetEvent"); }
dwWaitResult = WaitForSingleObject(g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent, INFINITE); if (WAIT_FAILED == dwWaitResult) { _JumpLastError(hr, error, "WaitForSingleObject"); }
if (!SetEvent(g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent)) { _JumpLastError(hr, error, "SetEvent"); }
hr = S_OK; error: return hr; }
//--------------------------------------------------------------------
VOID CALLBACK HandleDataAvail(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED *po) { bool bEnteredCriticalSection = false; HRESULT hr; unsigned __int64 nSysCurrentTime; unsigned __int64 nSysPhaseOffset; unsigned __int64 nSysTickCount;
// Parse the returned data based on the format string:
if (ERROR_SUCCESS != dwErrorCode) { hr = HRESULT_FROM_WIN32(dwErrorCode); _JumpError(hr, error, "HandleDataAvail: ReadFileEx failed"); }
// Get the required timestamp information:
hr = g_phpstate->tpsc.pfnGetTimeSysInfo(TSI_CurrentTime, &nSysCurrentTime); _JumpIfError(hr, error, "g_phpstate->tpsc.pfnGetTimeSysInfo");
hr = g_phpstate->tpsc.pfnGetTimeSysInfo(TSI_TickCount, &nSysTickCount); _JumpIfError(hr, error, "g_pnpstate->tpsc.pfnGetTimeSysInfo");
hr = g_phpstate->tpsc.pfnGetTimeSysInfo(TSI_PhaseOffset, &nSysPhaseOffset); _JumpIfError(hr, error, "g_pnpstate->tpsc.pfnGetTimeSysInfo");
_EnterCriticalSectionOrFail(&g_phpstate->csProv, bEnteredCriticalSection, hr, error);
// Convert the data retrieved from the time hardware into a time sample:
hr = ParseSample(g_phpstate->hParser, g_phpstate->cSampleInputBuffer, nSysCurrentTime, nSysPhaseOffset, nSysTickCount, &g_phpstate->tsCurrent); _JumpIfError(hr, error, "ParseFormatString");
// Indicate that we now have a valid sample
g_phpstate->bSampleIsValid = true; hr = g_phpstate->tpsc.pfnAlertSamplesAvail(); _JumpIfError(hr, error, "g_pnpstate->tpsc.pfnAlertSamplesAvail");
hr = S_OK; error:; _LeaveCriticalSection(&g_phpstate->csProv, bEnteredCriticalSection, hr);
// BUGBUG: do we want to sleep on error?
// return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE DWORD WINAPI HwProvThread(void * pvIgnored) { DWORD dwLength; DWORD dwWaitResult; HRESULT hr; OVERLAPPED o; HANDLE rghWait[] = { g_phpstate->hStopEvent, g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent };
ZeroMemory(&o, sizeof(o)); if (!ReadFileEx(g_phpstate->hComPort, g_phpstate->cSampleInputBuffer, g_phpstate->dwSampleSize, &o, HandleDataAvail)) { _JumpLastError(hr, error, "ReadFileEx"); } while (true) { dwWaitResult = WaitForMultipleObjectsEx(ARRAYSIZE(rghWait), rghWait, FALSE, INFINITE, TRUE); if (WAIT_OBJECT_0 == dwWaitResult) { // stop event
goto done; } else if (WAIT_OBJECT_0+1 == dwWaitResult) { // thread trap notification. Trap this thread:
hr = ThreadTrap(); _JumpIfError(hr, error, "ThreadTrap");
} else if (WAIT_IO_COMPLETION == dwWaitResult) { // we read some data. Queue up another read.
ZeroMemory(&o, sizeof(o)); ZeroMemory(g_phpstate->cSampleInputBuffer, sizeof(g_phpstate->cSampleInputBuffer)); if (!ReadFileEx(g_phpstate->hComPort, g_phpstate->cSampleInputBuffer, g_phpstate->dwSampleSize, &o, HandleDataAvail)) { _JumpLastError(hr, error, "ReadFileEx"); } } else { /*failed*/ _JumpLastError(hr, error, "WaitForMultipleObjects"); } }
done: hr = S_OK; error: // BUGBUG: is CancelIo called implicitly?
return hr; }
//--------------------------------------------------------------------
HRESULT StopHwProv() { DWORD dwWaitResult; HRESULT hr = S_OK;
if (NULL != g_phpstate) { // Shut down the HW prov thread:
if (NULL != g_phpstate->hStopEvent) { SetEvent(g_phpstate->hStopEvent); }
dwWaitResult = WaitForSingleObject(g_phpstate->hProvThread, INFINITE); if (WAIT_FAILED == dwWaitResult) { _IgnoreLastError("WaitForSingleObject"); } if (!GetExitCodeThread(g_phpstate->hProvThread, (DWORD *)&hr)) { _IgnoreLastError("GetExitCodeThread"); } else if (FAILED(hr)) { _IgnoreError(hr, "(HwProvThread)"); } CloseHandle(g_phpstate->hProvThread);
if (NULL != g_phpstate->hStopEvent) { CloseHandle(g_phpstate->hStopEvent); }
if (NULL != g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent) { CloseHandle(g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent); }
if (NULL != g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent) { CloseHandle(g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent); }
if (NULL != g_phpstate->hComPort && INVALID_HANDLE_VALUE != g_phpstate->hComPort) { CloseHandle(g_phpstate->hComPort); }
if (NULL != g_phpstate->wszCommPort) { LocalFree(g_phpstate->wszCommPort); } if (NULL != g_phpstate->wszFormat) { LocalFree(g_phpstate->wszFormat); }
if (NULL != g_phpstate->hParser) { FreeParser(g_phpstate->hParser); }
if (g_phpstate->bIsCsProvInitialized) { DeleteCriticalSection(&g_phpstate->csProv); g_phpstate->bIsCsProvInitialized = false; }
LocalFree(g_phpstate); ZeroMemory(g_phpstate, sizeof(HWProvState)); }
return hr; }
//--------------------------------------------------------------------
void FreeHwProvConfig(HWProvConfig *phwpConfig) { if (NULL != phwpConfig) { if (NULL != phwpConfig->wszCommPort) { LocalFree(phwpConfig->wszCommPort); } if (NULL != phwpConfig->wszFormat) { LocalFree(phwpConfig->wszFormat); } if (NULL != phwpConfig->wszRefID) { LocalFree(phwpConfig->wszRefID); } LocalFree(phwpConfig); } }
//--------------------------------------------------------------------
HRESULT ReadHwProvConfig(HWProvConfig **pphwpConfig) { DWORD dwResult; HKEY hkConfig = NULL; HRESULT hr; HWProvConfig *phwpConfig = NULL; WCHAR wszBuf[256];
dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wszHwProvRegKeyConfig, 0, KEY_READ, &hkConfig); if (ERROR_SUCCESS != dwResult) { hr=HRESULT_FROM_WIN32(dwResult); _JumpErrorStr(hr, error, "RegOpenKeyEx", wszHwProvRegKeyConfig); }
phwpConfig = (HWProvConfig *)LocalAlloc(LPTR, sizeof(HWProvConfig)); _JumpIfOutOfMemory(hr, error, phwpConfig);
struct { WCHAR *wszRegValue; DWORD *pdwValue; } rgRegParams[] = { { wszHwProvRegValueBaudRate, &phwpConfig->dwBaudRate }, { wszHwProvRegValueByteSize, &phwpConfig->dwByteSize }, { wszHwProvRegValueParity, &phwpConfig->dwParity }, { wszHwProvRegValueStopBits, &phwpConfig->dwStopBits } };
for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(rgRegParams); dwIndex++) { DWORD dwSize = sizeof(DWORD); DWORD dwType; dwResult = RegQueryValueEx(hkConfig, rgRegParams[dwIndex].wszRegValue, NULL, &dwType, (BYTE *)rgRegParams[dwIndex].pdwValue, &dwSize); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(GetLastError()); _JumpErrorStr(hr, error, "RegQueryValue", rgRegParams[dwIndex].wszRegValue); } _Verify(REG_DWORD == dwType, hr, error); }
struct { WCHAR *wszRegValue; WCHAR **ppwszValue; } rgRegSzParams[] = { { wszHwProvRegValueComPort, &phwpConfig->wszCommPort }, { wszHwProvRegValueFormat, &phwpConfig->wszFormat }, { wszHwProvRegValueRefID, &phwpConfig->wszRefID } };
for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(rgRegSzParams); dwIndex++) { DWORD dwSize = sizeof(wszBuf); DWORD dwType; dwResult = RegQueryValueEx(hkConfig, rgRegSzParams[dwIndex].wszRegValue, NULL, &dwType, (BYTE *)wszBuf, &dwSize); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); _JumpErrorStr(hr, error, "RegQueryValueEx", rgRegSzParams[dwIndex].wszRegValue); } _Verify(REG_SZ == dwType, hr, error);
*(rgRegSzParams[dwIndex].ppwszValue) = (WCHAR *)LocalAlloc(LPTR, dwSize); _JumpIfOutOfMemory(hr, error, *(rgRegSzParams[dwIndex].ppwszValue)); wcscpy(*(rgRegSzParams[dwIndex].ppwszValue), wszBuf); }
*pphwpConfig = phwpConfig; phwpConfig = NULL; hr = S_OK; error: if (NULL != hkConfig) { RegCloseKey(hkConfig); } if (NULL != phwpConfig) { FreeHwProvConfig(phwpConfig); } return hr; }
//--------------------------------------------------------------------
HRESULT HandleUpdateConfig(void) { bool bComConfigUpdated = false; bool bTrappedThreads = false; HRESULT hr; HWProvConfig *phwpConfig = NULL;
hr = TrapThreads(true); _JumpIfError(hr, error, "TrapThreads"); bTrappedThreads = true;
hr = ReadHwProvConfig(&phwpConfig); _JumpIfError(hr, error, "ReadHwProvConfig");
// BUGBUG: need to update when com config changes!!
//
if (g_phpstate->dcb.BaudRate != phwpConfig->dwBaudRate) { g_phpstate->dcb.BaudRate = phwpConfig->dwBaudRate; bComConfigUpdated = true; }
if (g_phpstate->dcb.ByteSize != (BYTE)phwpConfig->dwByteSize) { g_phpstate->dcb.ByteSize = (BYTE)phwpConfig->dwByteSize; bComConfigUpdated = true; }
if (g_phpstate->dcb.Parity != (BYTE)phwpConfig->dwParity) { g_phpstate->dcb.Parity = (BYTE)phwpConfig->dwParity; bComConfigUpdated = true; } if (g_phpstate->dcb.StopBits != (BYTE)phwpConfig->dwStopBits) { g_phpstate->dcb.StopBits = (BYTE)phwpConfig->dwStopBits; bComConfigUpdated = true; }
if (0 != wcscmp(g_phpstate->wszFormat, phwpConfig->wszFormat)) { LocalFree(g_phpstate->wszFormat); g_phpstate->wszFormat = phwpConfig->wszFormat; phwpConfig->wszFormat = NULL;
FreeParser(g_phpstate->hParser); g_phpstate->hParser = NULL; hr = MakeParser(g_phpstate->wszFormat, &g_phpstate->hParser); _JumpIfError(hr, error, "MakeParser"); g_phpstate->dwSampleSize = GetSampleSize(g_phpstate->hParser); }
if (bComConfigUpdated) { if (!SetCommState(g_phpstate->hComPort, &g_phpstate->dcb)) { _JumpLastError(hr, error, "SetCommState"); } // BUGBUG: PurgeComm()?
}
hr = S_OK; error: if (bTrappedThreads) { HRESULT hr2 = TrapThreads(false); _TeardownError(hr, hr2, "TrapThreads"); } if (NULL != phwpConfig) { FreeHwProvConfig(phwpConfig); } if (FAILED(hr)) { HRESULT hr2 = StopHwProv(); _IgnoreIfError(hr2, "StopHwProv"); } return hr; }
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT HandleTimeJump(TpcTimeJumpedArgs *ptjArgs) { bool bEnteredCriticalSection = false; HRESULT hr;
_EnterCriticalSectionOrFail(&g_phpstate->csProv, bEnteredCriticalSection, hr, error); g_phpstate->bSampleIsValid = false;
hr = S_OK; error: _LeaveCriticalSection(&g_phpstate->csProv, bEnteredCriticalSection, hr); return hr; }
//--------------------------------------------------------------------
HRESULT HandleGetSamples(TpcGetSamplesArgs * ptgsa) { bool bEnteredCriticalSection = false; DWORD dwBytesRemaining; HRESULT hr; TimeSample *pts = NULL;
pts = (TimeSample *)ptgsa->pbSampleBuf; dwBytesRemaining = ptgsa->cbSampleBuf; ptgsa->dwSamplesAvailable = 0; ptgsa->dwSamplesReturned = 0;
_EnterCriticalSectionOrFail(&g_phpstate->csProv, bEnteredCriticalSection, hr, error);
if (g_phpstate->bSampleIsValid) { ptgsa->dwSamplesAvailable++; if (dwBytesRemaining < sizeof(TimeSample)) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); _JumpError(hr, error, "HandleGetSamples: filling in sample buffer"); } // Copy our current time sample over to the output buffer:
memcpy(pts, &g_phpstate->tsCurrent, sizeof(TimeSample)); ptgsa->dwSamplesReturned++; // calculate the dispersion - add skew dispersion due to time
// since the sample's dispersion was last updated, and clamp to max dispersion.
NtTimePeriod tpDispersionTemp; NtTimeEpoch teNow;
hr = g_phpstate->tpsc.pfnGetTimeSysInfo(TSI_CurrentTime, &teNow.qw); _JumpIfError(hr, error, "g_phpstate->tpsc.pfnGetTimeSysInfo");
// see how long it's been since we received the sample:
if (teNow > g_phpstate->teSampleReceived) { tpDispersionTemp = abs(teNow - g_phpstate->teSampleReceived); tpDispersionTemp = NtpConst::timesMaxSkewRate(tpDispersionTemp); }
tpDispersionTemp.qw += g_phpstate->tsCurrent.tpDispersion; if (tpDispersionTemp > NtpConst::tpMaxDispersion) { tpDispersionTemp = NtpConst::tpMaxDispersion; } pts->tpDispersion = tpDispersionTemp.qw; } hr = S_OK; error: _LeaveCriticalSection(&g_phpstate->csProv, bEnteredCriticalSection, hr); return hr; }
//--------------------------------------------------------------------
HRESULT StartHwProv(TimeProvSysCallbacks * pSysCallbacks) { DWORD dwThreadID; HRESULT hr; HWProvConfig *phpc = NULL;
g_phpstate = (HWProvState *)LocalAlloc(LPTR, sizeof(HWProvState)); _JumpIfOutOfMemory(hr, error, g_phpstate); hr = myInitializeCriticalSection(&g_phpstate->csProv); _JumpIfError(hr, error, "myInitializeCriticalSection"); g_phpstate->bIsCsProvInitialized = true;
// save the callbacks table
if (sizeof(g_phpstate->tpsc) != pSysCallbacks->dwSize) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); _JumpIfError(hr, error, "StartHwProv: save the callbacks table"); } memcpy(&g_phpstate->tpsc, pSysCallbacks, sizeof(g_phpstate->tpsc)); // read the configuration
hr = ReadHwProvConfig(&phpc); _JumpIfError(hr, error, "ReadHwProvConfig"); // Copy over the string config info:
g_phpstate->wszCommPort = phpc->wszCommPort; phpc->wszCommPort = NULL; // prevent wszCommPort from being double-freed
g_phpstate->wszFormat = phpc->wszFormat; phpc->wszFormat = NULL; // prevent wszFormat from being double-freed
g_phpstate->wszRefID = phpc->wszRefID; phpc->wszRefID = NULL; // prevent wszRefID from being double-freed
// Create a parser from the specified format string:
hr = MakeParser(g_phpstate->wszFormat, &g_phpstate->hParser); _JumpIfError(hr, error, "MakeParser"); g_phpstate->dwSampleSize = GetSampleSize(g_phpstate->hParser);
// The remaining info are comm configuration, which we'll add to the
// current comm configuration. Open the comm port so we can get the
// comm state.
g_phpstate->hComPort = CreateFile(g_phpstate->wszCommPort, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == g_phpstate->hComPort || NULL == g_phpstate->hComPort) { _JumpLastError(hr, error, "CreateFile"); }
// Set the Comm state based on
// a) the existing Comm state
// and b) Comm config found in the registry
if (!GetCommState(g_phpstate->hComPort, &g_phpstate->dcb)) { _JumpLastError(hr, error, "GetCommState"); }
g_phpstate->dcb.BaudRate = phpc->dwBaudRate; g_phpstate->dcb.ByteSize = (BYTE)phpc->dwByteSize; g_phpstate->dcb.Parity = (BYTE)phpc->dwParity; g_phpstate->dcb.StopBits = (BYTE)phpc->dwStopBits;
// BUGBUG: pulled these values from timeserv.c. Need to document
// why they work??
g_phpstate->dcb.fOutxCtsFlow = FALSE; g_phpstate->dcb.fDsrSensitivity = FALSE; g_phpstate->dcb.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(g_phpstate->hComPort, &g_phpstate->dcb)) { _JumpLastError(hr, error, "SetCommState"); }
// create the events used by the hw prov
g_phpstate->hStopEvent = CreateEvent(NULL/*security*/, TRUE/*manual*/, FALSE/*state*/, NULL/*name*/); if (NULL == g_phpstate->hStopEvent) { _JumpLastError(hr, error, "CreateEvent"); }
g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent = CreateEvent(NULL/*security*/, FALSE/*auto*/, FALSE/*state*/, NULL/*name*/); if (NULL == g_phpstate->hProvThread_EnterOrLeaveThreadTrapEvent) { _JumpLastError(hr, error, "CreateEvent"); }
g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent = CreateEvent(NULL/*security*/, FALSE/*auto*/, FALSE/*state*/, NULL/*name*/); if (NULL == g_phpstate->hProvThread_ThreadTrapTransitionCompleteEvent) { _JumpLastError(hr, error, "CreateEvent"); }
g_phpstate->hProvThread = CreateThread(NULL, NULL, HwProvThread, NULL, 0, &dwThreadID); if (NULL == g_phpstate->hProvThread) { _JumpLastError(hr, error, "CreateThread"); }
hr = S_OK; error: if (NULL != phpc) { FreeHwProvConfig(phpc); } if (FAILED(hr)) { StopHwProv(); } return hr; }
//--------------------------------------------------------------------------------
//
// PROVIDER INTERFACE IMPLEMENTATION
//
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------
HRESULT __stdcall TimeProvOpen(IN WCHAR * wszName, IN TimeProvSysCallbacks * pSysCallbacks, OUT TimeProvHandle * phTimeProv) { HRESULT hr;
if (NULL != g_phpstate) { hr=HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); _JumpError(hr, error, "(provider init)"); } hr = StartHwProv(pSysCallbacks); _JumpIfError(hr, error, "StartHwProv"); *phTimeProv = 0; /*ignored*/
hr=S_OK; error: return hr; }
//--------------------------------------------------------------------
HRESULT __stdcall TimeProvCommand(IN TimeProvHandle hTimeProv, IN TimeProvCmd eCmd, IN TimeProvArgs pvArgs) { HRESULT hr; LPCWSTR wszProv;
if (0 != hTimeProv) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); _JumpError(hr, error, "HwTimeProvCommand: provider handle verification"); }
switch (eCmd) { case TPC_TimeJumped: hr = HandleTimeJump((TpcTimeJumpedArgs *)pvArgs); _JumpIfError(hr, error, "HandleTimeJump"); break;
case TPC_UpdateConfig: hr = HandleUpdateConfig(); _JumpIfError(hr, error, "HandleUpdateConfig"); break;
case TPC_GetSamples: hr = HandleGetSamples((TpcGetSamplesArgs *)pvArgs); _JumpIfError(hr, error, "HandleGetSamples"); break;
case TPC_PollIntervalChanged: // BUGBUG: unnecessary until we add support for polling
case TPC_NetTopoChange: // unnecessary for HW prov
// Don't need to do anything here.
break;
case TPC_Query: hr = HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED); _JumpError(hr, error, "HwTimeProvCommand");
case TPC_Shutdown: hr=StopHwProv(); _JumpIfError(hr, error, "StopHwProv"); break; default: hr=HRESULT_FROM_WIN32(ERROR_BAD_COMMAND); _JumpError(hr, error, "(command dispatch)"); }
hr=S_OK; error: return hr; }
//--------------------------------------------------------------------
HRESULT __stdcall TimeProvClose(IN TimeProvHandle hTimeProv) { HRESULT hr; LPCWSTR wszProv;
if (0 != hTimeProv) { hr=HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); _JumpError(hr, error, "(provider handle verification)"); } hr=StopHwProv(); _JumpIfError(hr, error, "StopHwServer");
hr=S_OK; error: return hr; }
|