Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

946 lines
31 KiB

#include "precomp.h"
#include "wia.h"
#include "stirpc.h"
#define INIT_GUID
// {8144B6F5-20A8-444a-B8EE-19DF0BB84BDB}
DEFINE_GUID( CLSID_StiEventHandler, 0x8144b6f5, 0x20a8, 0x444a, 0xb8, 0xee, 0x19, 0xdf, 0xb, 0xb8, 0x4b, 0xdb );
#define WIA_SERVICE_STARTING_EVENT_NAME TEXT("Global\\WiaServiceStarted")
//
// (A;;GA;;;SY) becomes (A;;0x1f003;;;SY) when converted back to string
// 0x1f0000 is SYNCHORNIZE | STANDARD_RIGHTS_REQUIRED
// 0x000003 is specific rigts for ???
//
#define WIA_SERVICE_STARTING_EVENT_DACL \
TEXT("D:(A;;0x1f0003;;;SY)(A;;0x1f0003;;;LS)(A;;0x1f0003;;;LA)")
const TCHAR g_szWiaServiceStartedEventName[] = WIA_SERVICE_STARTING_EVENT_NAME;
const TCHAR g_WiaESDString[] = WIA_SERVICE_STARTING_EVENT_DACL;
// event and event wait handles
HANDLE g_hWiaServiceStarted = NULL; // event
HANDLE g_hWaitForWiaServiceStarted = NULL; // wait
HANDLE g_hWiaEventArrived = NULL; // event
HANDLE g_hWaitForWiaEventArrived = NULL; // wait
// async RPC request
RPC_BINDING_HANDLE g_AsyncRpcBinding = NULL;
RPC_STATUS g_LastRpcCallStatus = RPC_S_OK;
RPC_ASYNC_STATE g_Async = { 0 };
PRPC_ASYNC_STATE g_pAsync = &g_Async;
// event structure filled by async RPC call
WIA_ASYNC_EVENT_NOTIFY_DATA g_Event = { 0 };
#ifdef DEBUG
#define DBG_TRACE(x) DebugTrace x
#else
#define DBG_TRACE(x)
#endif
void DebugTrace(LPCSTR fmt, ...)
{
char buffer[1024] = "WIARPC:";
const blen = 7;
size_t len = (sizeof(buffer) / sizeof(buffer[0])) - blen;
va_list marker;
va_start(marker, fmt);
_vsnprintf(buffer + blen, len - 3, fmt, marker);
buffer[len - 3] = 0;
len = strlen(buffer);
if(len > 0) {
if(buffer[len - 1] != '\n') {
buffer[len++] = '\n';
buffer[len] = '\0';
}
OutputDebugStringA(buffer);
}
va_end(marker);
}
// aux function to call LocalFree() safely on a pointer and zero it
template <typename t>
void WiaELocalFree(t& ptr) {
if(ptr) {
LocalFree(static_cast<HLOCAL>(ptr));
ptr = NULL;
}
}
// aux function to call CloseHanlde() on a valid handle and zero it
void WiaECloseHandle(HANDLE& h)
{
if(h && h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
h = NULL;
}
}
//
// Returns TRUE if event's security descriptor is exactly the one we'd
// set it to be, FALSE otherwise
//
BOOL WiaECheckEventSecurity(HANDLE hEvent)
{
BOOL success = FALSE;
PACL pDacl;
PSECURITY_DESCRIPTOR pDescriptor = NULL;
LPTSTR stringDescriptor = NULL;
ULONG stringLength;
if(ERROR_SUCCESS != GetSecurityInfo(hEvent, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, &pDacl, NULL, &pDescriptor))
{
//
// failed to retrieve event's security descriptor -- this is a
// failure
//
DBG_TRACE(("Failed to retrieve event security descriptor (Error %d)", GetLastError()));
goto Done;
}
if(!ConvertSecurityDescriptorToStringSecurityDescriptor(pDescriptor,
SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &stringDescriptor, &stringLength))
{
//
// failed to convert event's security descriptor to string --
// this is also a failure
//
DBG_TRACE(("Failed to convert security descriptor to string (Error %d)", GetLastError()));
goto Done;
}
if(lstrcmp(g_WiaESDString, stringDescriptor) != 0)
{
//
// descriptors are different, this is a failure
//
DBG_TRACE(("String security descriptor of WIA event is unexpected: \r\n'%S'\r\n instead of \r\n'%S'\r\n)",
stringDescriptor, g_WiaESDString));
goto Done;
}
success = TRUE;
Done:
WiaELocalFree(pDescriptor);
WiaELocalFree(stringDescriptor);
return success;
}
//
//
//
RPC_STATUS WiaEPrepareAsyncCall(PRPC_ASYNC_STATE pAsync)
{
RPC_STATUS status;
LPTSTR binding = NULL;
status = RpcStringBindingCompose(NULL, STI_LRPC_SEQ, NULL, STI_LRPC_ENDPOINT, NULL, &binding);
if(status) {
DBG_TRACE(("Failed to compose string binding (Error %d)", status));
goto Done;
}
status = RpcBindingFromStringBinding(binding, &g_AsyncRpcBinding);
if(status) {
DBG_TRACE(("Failed to build async RPC binding (Error %d)", status));
goto Done;
}
status = RpcAsyncInitializeHandle(pAsync, sizeof(RPC_ASYNC_STATE));
if(status) {
DBG_TRACE(("Failed to initialize RPC handle (Error %d)", status));
goto Done;
}
pAsync->UserInfo = NULL;
pAsync->NotificationType = RpcNotificationTypeEvent;
pAsync->u.hEvent = g_hWiaEventArrived;
// store the pointer to async into global, so that if
// the result of R_WiaGetEventDataAsync() arrives soon, it would
// not land without it
InterlockedExchangePointer((PVOID*)&g_pAsync, pAsync);
RpcTryExcept {
R_WiaGetEventDataAsync(pAsync, g_AsyncRpcBinding, &g_Event);
} RpcExcept(1) {
status = RpcExceptionCode();
DBG_TRACE(("Exception 0x%x calling WIA RPC server", status));
} RpcEndExcept;
Done:
if(status && g_AsyncRpcBinding) {
RpcTryExcept {
RpcBindingFree(&g_AsyncRpcBinding);
} RpcExcept(1) {
status = RpcExceptionCode();
DBG_TRACE(("Exception 0x%x while freeing binding handle", status));
} RpcEndExcept;
g_AsyncRpcBinding = NULL;
}
if(binding) RpcStringFree(&binding);
return status;
}
#define SESSION_MONIKER TEXT("Session:Console!clsid:")
#include <initguid.h>
DEFINE_GUID(CLSID_DefWiaHandler,
0xD13E3F25, 0x1688, 0x45A0,
0x97, 0x43, 0x75, 0x9E, 0xB3, 0x5C, 0xDF, 0x9A);
#include "sticfunc.h"
/**************************************************************************\
* _CoCreateInstanceInConsoleSession
*
* This helper function acts the same as CoCreateInstance, but will launch
* a out-of-process COM server on the correct user's desktop, taking
* fast user switching into account. (Normal CoCreateInstance will
* launch it on the first logged on user's desktop, instead of the currently
* logged on one).
*
* This code was taken with permission from the Shell's Hardware
* Notification service, on behalf of StephStm.
*
* Arguments:
*
* rclsid, // Class identifier (CLSID) of the object
* pUnkOuter, // Pointer to controlling IUnknown
* dwClsContext // Context for running executable code
* riid, // Reference to the identifier of the interface
* ppv // Address of output variable that receives
* // the interface pointer requested in riid
*
* Return Value:
*
* Status
*
* History:
*
* 03/01/2001 Original Version
*
\**************************************************************************/
HRESULT _CoCreateInstanceInConsoleSession(REFCLSID rclsid,
IUnknown* punkOuter,
DWORD dwClsContext,
REFIID riid,
void** ppv)
{
IBindCtx *pbc = NULL;
HRESULT hr = CreateBindCtx(0, &pbc); // Create a bind context for use with Moniker
//
// Set the return
//
*ppv = NULL;
if (SUCCEEDED(hr)) {
WCHAR wszCLSID[39];
//
// Convert the riid to GUID string for use in binding to moniker
//
if (StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]))) {
ULONG ulEaten = 0;
IMoniker* pmoniker = NULL;
WCHAR wszDisplayName[sizeof(SESSION_MONIKER)/sizeof(WCHAR) + sizeof(wszCLSID)/sizeof(wszCLSID[0]) + 2] = SESSION_MONIKER;
//
// We want something like: "Session:Console!clsid:760befd0-5b0b-44d7-957e-969af35ce954"
// Notice that we don't want the leading and trailing brackets {..} around the GUID.
// So, first get rid of trailing bracket by overwriting it with termintaing '\0'
//
wszCLSID[lstrlenW(wszCLSID) - 1] = L'\0';
//
// Form display name string. To get rid of the leading bracket, we pass in the
// address of the next character as the start of the string.
//
if (lstrcatW(wszDisplayName, &(wszCLSID[1]))) {
//
// Parse the name and get a moniker:
//
hr = MkParseDisplayName(pbc, wszDisplayName, &ulEaten, &pmoniker);
if (SUCCEEDED(hr)) {
IClassFactory *pcf = NULL;
//
// Attempt to get the class factory
//
hr = pmoniker->BindToObject(pbc, NULL, IID_IClassFactory, (void**)&pcf);
if (SUCCEEDED(hr))
{
//
// Attempt to create the object
//
hr = pcf->CreateInstance(punkOuter, riid, ppv);
DBG_TRACE(("_CoCreateInstanceInConsoleSession, pcf->CreateInstance returned: hr = 0x%08X", hr));
pcf->Release();
} else {
DBG_TRACE(("_CoCreateInstanceInConsoleSession, pmoniker->BindToObject returned: hr = 0x%08X", hr));
}
pmoniker->Release();
} else {
DBG_TRACE(("_CoCreateInstanceInConsoleSession, MkParseDisplayName returned: hr = 0x%08X", hr));
}
} else {
DBG_TRACE(("_CoCreateInstanceInConsoleSession, string concatenation failed"));
hr = E_INVALIDARG;
}
} else {
DBG_TRACE(("_CoCreateInstanceInConsoleSession, StringFromGUID2 failed"));
hr = E_INVALIDARG;
}
pbc->Release();
} else {
DBG_TRACE(("_CoCreateInstanceInConsoleSession, CreateBindCtxt returned: hr = 0x%08X", hr));
}
return hr;
}
/**************************************************************************\
* GetUserTokenForConsoleSession
*
* This helper function will grab the currently logged on interactive
* user's token, which can be used in a call to CreateProcessAsUser.
* Caller is responsible for closing this Token handle.
*
* It first grabs the impersontaed token from the current session (our
* service runs in session 0, but with Fast User Switching, the currently
* active user may be in a different session). It then creates a
* primary token from the impersonated one.
*
* Arguments:
*
* None
*
* Return Value:
*
* HANDLE to Token for logged on user in the currently active session.
* NULL otherwise.
*
* History:
*
* 03/05/2001 Original Version
*
\**************************************************************************/
HANDLE GetUserTokenForConsoleSession()
{
HANDLE hImpersonationToken = NULL;
HANDLE hTokenUser = NULL;
//
// Get interactive user's token
//
if (GetWinStationUserToken(GetCurrentSessionID(), &hImpersonationToken)) {
//
// Maybe nobody is logged on, so do a check first.
//
if (hImpersonationToken) {
//
// We duplicate the token, since the returned token is an
// impersonated one, and we need it to be primary for
// use in CreateProcessAsUser.
//
if (!DuplicateTokenEx(hImpersonationToken,
0,
NULL,
SecurityImpersonation,
TokenPrimary,
&hTokenUser)) {
DBG_TRC(("CEventNotifier::StartCallbackProgram, DuplicateTokenEx failed! GetLastError() = 0x%08X", GetLastError()));
}
} else {
DBG_PRT(("CEventNotifier::StartCallbackProgram, No user appears to be logged on..."));
}
} else {
DBG_TRACE(("CEventNotifier::StartCallbackProgram, GetWinStationUserToken failed! GetLastError() = 0x%08X", GetLastError()));
}
//
// Close the impersonated token, since we no longer need it.
//
if (hImpersonationToken) {
CloseHandle(hImpersonationToken);
}
return hTokenUser;
}
/**************************************************************************\
* PrepareCommandline
*
* This helper function will prepare the commandline for apps not registered
* as local out-of-process COM servers. We place the event guid and device
* id in the command line.
*
*
* Arguments:
*
* CSimpleStringWide &cswDeviceID - the device which generated this event
* GUID &guidEvent - the GUID indicating which event occured.
* CSimpleStringWide &cswRegisteredCOmmandline - the commandline this handler
* registered with. This commandline
* contains the tokens that must
* be substituted.
*
* Return Value:
*
* CSimpleStringWide - contians the parsed commandline which has the
* device id and event guid substituted.
*
* History:
*
* 03/05/2001 Original Version
*
\**************************************************************************/
CSimpleStringWide PrepareCommandline(
const CSimpleStringWide &cswDeviceID,
const GUID &guidEvent,
const CSimpleStringWide &cswRegisteredCommandline)
{
WCHAR wszGUIDStr[40];
WCHAR wszCommandline[MAX_PATH];
WCHAR *pPercentSign;
WCHAR *pTest = NULL;
CSimpleStringWide cswCommandLine;
//
// ISSUE: This code could be written better. For now, we're not touching it
// and keeping it the same as the WinXP code base.
//
//
// Fix up the commandline. First check that it has at least two %
//
pTest = wcschr(cswRegisteredCommandline.String(), '%');
if (pTest) {
pTest = wcschr(pTest + 1, '%');
}
if (!pTest) {
_snwprintf(
wszCommandline,
sizeof(wszCommandline) / sizeof( wszCommandline[0] ),
L"%s /StiDevice:%%1 /StiEvent:%%2",
cswRegisteredCommandline.String());
} else {
wcsncpy(wszCommandline, cswRegisteredCommandline.String(), sizeof(wszCommandline) / sizeof( wszCommandline[0] ));
}
//
// enforce null termination
//
wszCommandline[ (sizeof(wszCommandline) / sizeof(wszCommandline[0])) - 1 ] = 0;
//
// Change the number {1|2} into s
//
pPercentSign = wcschr(wszCommandline, L'%');
*(pPercentSign + 1) = L's';
pPercentSign = wcschr(pPercentSign + 1, L'%');
*(pPercentSign + 1) = L's';
//
// Convert the GUID into string
//
StringFromGUID2(guidEvent, wszGUIDStr, 40);
//
// Final comand line
//
//swprintf(pwszResCmdline, wszCommandline, bstrDeviceID, wszGUIDStr);
cswCommandLine.Format(wszCommandline, cswDeviceID.String(), wszGUIDStr);
return cswCommandLine;
}
void FireStiEvent()
{
StiEventHandlerLookup stiLookup;
//
// Get the STI handler list for this device event. This will be returned as a double-NULL
// terminated BSTR.
//
BSTR bstrAppList = stiLookup.getStiAppListForDeviceEvent(g_Event.bstrDeviceID, g_Event.EventGuid);
if (bstrAppList)
{
HRESULT hr = S_OK;
IWiaEventCallback *pIEventCB = NULL;
ULONG ulEventType = WIA_ACTION_EVENT;
//
// CoCreate our event UI handler. Note that it will not display any UI
// if there is only one application.
//
hr = CoCreateInstance(
CLSID_StiEventHandler,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IWiaEventCallback,
(void**)&pIEventCB);
if (SUCCEEDED(hr)) {
//
// Make the callback. This will display a prompt if our AppList contains more
// than one application.
//
pIEventCB->ImageEventCallback(&g_Event.EventGuid,
g_Event.bstrEventDescription,
g_Event.bstrDeviceID,
g_Event.bstrDeviceDescription,
g_Event.dwDeviceType,
bstrAppList,
&g_Event.ulEventType,
0);
pIEventCB->Release();
}
SysFreeString(bstrAppList);
bstrAppList = NULL;
}
}
void WiaEFireEvent()
{
HRESULT hr;
IWiaEventCallback *pIEventCB;
//
// ISSUE: For now, we assume this is a WIA event. Really, it could
// be either WIA or STI. STI events need special handling.
//
if (g_Event.ulEventType & STI_DEVICE_EVENT)
{
FireStiEvent();
}
else
{
//
// Find the persistent event handler for this device event
//
EventHandlerInfo *pEventHandlerInfo = NULL;
WiaEventHandlerLookup eventLookup;
pEventHandlerInfo = eventLookup.getPersistentHandlerForDeviceEvent(g_Event.bstrDeviceID, g_Event.EventGuid);
if (pEventHandlerInfo)
{
//
// Check whether this is a out-of-process COM server registed handler or
// a commandline registered handler.
//
if (pEventHandlerInfo->getCommandline().Length() < 1)
{
//
// This is a COM registered handler
//
hr = _CoCreateInstanceInConsoleSession(pEventHandlerInfo->getCLSID(),
NULL,
CLSCTX_LOCAL_SERVER,
IID_IWiaEventCallback,
(void**)&pIEventCB);
if (SUCCEEDED(hr)) {
pIEventCB->ImageEventCallback(&g_Event.EventGuid,
g_Event.bstrEventDescription,
g_Event.bstrDeviceID,
g_Event.bstrDeviceDescription,
g_Event.dwDeviceType,
g_Event.bstrFullItemName,
&g_Event.ulEventType,
0);
//
// Release the callback interface
//
pIEventCB->Release();
} else {
DBG_ERR(("NotifySTIEvent:CoCreateInstance of event callback failed (0x%X)", hr));
}
}
else
{
//
// This is a commandline registered handler
//
HANDLE hTokenUser = NULL;
STARTUPINFO startupInfo = {0};
PROCESS_INFORMATION processInfo = {0};
LPVOID pEnvBlock = NULL;
BOOL bRet = FALSE;
//
// Get interactive user's token
//
hTokenUser = GetUserTokenForConsoleSession();
//
// Check that somebody is logged in
//
if (hTokenUser)
{
//
// Set up start up info
//
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.lpDesktop = L"WinSta0\\Default";
startupInfo.cb = sizeof(startupInfo);
startupInfo.wShowWindow = SW_SHOWNORMAL;
//
// Create the user's environment block
//
bRet = CreateEnvironmentBlock(
&pEnvBlock,
hTokenUser,
FALSE);
if (bRet)
{
//
// Prepare the command line. Make sure we pass in the EVENT guid, not the STI proxy guid.
//
CSimpleStringWide cswCommandLine;
cswCommandLine = PrepareCommandline(g_Event.bstrDeviceID,
g_Event.EventGuid,
pEventHandlerInfo->getCommandline());
//
// Create the process in user's context
//
bRet = CreateProcessAsUserW(
hTokenUser,
NULL, // Application name
(LPWSTR)cswCommandLine.String(),
NULL, // Process attributes
NULL, // Thread attributes
FALSE, // Handle inheritance
NORMAL_PRIORITY_CLASS |
CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
pEnvBlock, // Environment
NULL, // Current directory
&startupInfo,
&processInfo);
if (! bRet) {
DBG_ERR(("CreateProcessAsUser failed! GetLastError() = 0x%08X", GetLastError()));
}
}
else
{
DBG_ERR(("CreateEnvironmentBlock failed! GetLastError() = 0x%08X", GetLastError()));
}
}
//
// Garbage collection
//
if (processInfo.hProcess)
{
CloseHandle(processInfo.hProcess);
}
if (processInfo.hThread)
{
CloseHandle(processInfo.hThread);
}
if (hTokenUser)
{
CloseHandle(hTokenUser);
}
if (pEnvBlock)
{
DestroyEnvironmentBlock(pEnvBlock);
}
}
pEventHandlerInfo->Release();
pEventHandlerInfo = NULL;
}
}
}
void WiaEProcessAsyncCallResults(PRPC_ASYNC_STATE pAsync)
{
RPC_STATUS callstatus, status;
int nStatus;
if(g_LastRpcCallStatus) {
DBG_TRACE(("Last RPC call was not successful (error 0x%x, therefore we are not going to look at the results",
g_LastRpcCallStatus));
return;
}
callstatus = RpcAsyncGetCallStatus(pAsync);
RpcTryExcept {
status = RpcAsyncCompleteCall(pAsync, &nStatus);
} RpcExcept(1) {
status = RpcExceptionCode();
} RpcEndExcept;
if(callstatus == RPC_S_OK && status == RPC_S_OK) {
DBG_TRACE(("\r\n\r\n#### Event arrived on '%S', firing it\r\n\r\n", g_Event.bstrDeviceID));
WiaEFireEvent();
} else {
DBG_TRACE(("Failed to complete async RPC call, error 0x%x", status));
}
}
//
//
//
void WiaECleanupAsyncCall(PRPC_ASYNC_STATE pAsync)
{
RPC_STATUS status;
int nReply;
status = RpcAsyncGetCallStatus(pAsync);
switch(status) {
case RPC_S_ASYNC_CALL_PENDING:
DBG_TRACE(("Cancelling pending async RPC call."));
RpcTryExcept {
status = RpcAsyncCancelCall(pAsync, TRUE);
} RpcExcept(1) {
status = RpcExceptionCode();
DBG_TRACE(("Exception 0x%x cancelling outstanding async RPC call", status));
} RpcEndExcept;
break;
case RPC_S_OK:
// already completed, don't do anything with it
break;
default:
DBG_TRACE(("Cleaning up async RPC call status is 0x%x.", status));
RpcTryExcept {
status = RpcAsyncCompleteCall(pAsync, &nReply);
} RpcExcept(1) {
status = RpcExceptionCode();
DBG_TRACE(("Exception 0x%x cancelling outstanding async RPC call", status));
} RpcEndExcept;
}
if(g_AsyncRpcBinding) {
RpcTryExcept {
RpcBindingFree(&g_AsyncRpcBinding);
} RpcExcept(1) {
status = RpcExceptionCode();
DBG_TRACE(("Exception 0x%x while freeing binding handle", status));
} RpcEndExcept;
g_AsyncRpcBinding = NULL;
}
//
// cleanup any BSTRs in g_Event
//
SysFreeString(g_Event.bstrEventDescription);
g_Event.bstrEventDescription = NULL;
SysFreeString(g_Event.bstrDeviceID);
g_Event.bstrDeviceID = NULL;
SysFreeString(g_Event.bstrDeviceDescription);
g_Event.bstrDeviceDescription = NULL;
SysFreeString(g_Event.bstrFullItemName);
g_Event.bstrFullItemName = NULL;
}
VOID CALLBACK
WiaEServiceStartedCallback(LPVOID, BOOLEAN)
{
PRPC_ASYNC_STATE pAsync;
DBG_TRACE(("WIA service is starting"));
pAsync = (PRPC_ASYNC_STATE) InterlockedExchangePointer((PVOID*)&g_pAsync, NULL);
if(pAsync) {
// at this point we are garanteed that
// WiaERpcCallBack will not get to g_Async
// abort any pending RPC calls
WiaECleanupAsyncCall(pAsync);
// initiate new async call
g_LastRpcCallStatus = WiaEPrepareAsyncCall(pAsync);
} else {
DBG_TRACE(("No async pointer"));
}
}
VOID CALLBACK
WiaERpcCallback(PVOID, BOOLEAN)
{
PRPC_ASYNC_STATE pAsync;
DBG_TRACE(("Async RPC event arrived"));
pAsync = (PRPC_ASYNC_STATE) InterlockedExchangePointer((PVOID*)&g_pAsync, NULL);
if(pAsync) {
// at this point we are garanteed that
// WiaEServiceStartedCallback will not get to g_Async
WiaEProcessAsyncCallResults(pAsync);
// cleanup the call
WiaECleanupAsyncCall(pAsync);
// initiate new async call
g_LastRpcCallStatus = WiaEPrepareAsyncCall(pAsync);
} else {
DBG_TRACE(("No async pointer"));
}
}
HRESULT WINAPI WiaEventsInitialize()
{
HRESULT hr = S_OK;
SECURITY_ATTRIBUTES sa = { sizeof(sa), FALSE, NULL };
HANDLE hEvent = NULL;
if(g_hWiaServiceStarted) {
return S_OK;
}
// allocate appropriate security attributes for the named event we
// use to learn about WIA service startup
if(!ConvertStringSecurityDescriptorToSecurityDescriptor(g_WiaESDString,
SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL))
{
DBG_TRACE(("WiaEventsInitialize failed to produce event security descriptor (Error %d)", GetLastError()));
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
hEvent = CreateEvent(&sa, FALSE, FALSE, WIA_SERVICE_STARTING_EVENT_NAME);
if(hEvent == NULL) {
DBG_TRACE(("WiaEventsInitialize failed to create named event (Error %d)", GetLastError()));
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
if(GetLastError() == ERROR_ALREADY_EXISTS) {
// interrogate the security descriptor on this event -- does it
// look like ours or is it squatted by a bad guy?
if(!WiaECheckEventSecurity(hEvent)) {
// we don't like how it looks, bail out
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
goto Cleanup;
}
}
// we already have the event, try to mark our initialization
// complete
hEvent = InterlockedCompareExchangePointer(&g_hWiaServiceStarted, hEvent, NULL);
if(hEvent != NULL) {
//
// oops, another thread beat us to this!
//
// we only allocated our security descriptor, free it
WiaELocalFree(sa.lpSecurityDescriptor);
//
// return right away, don't do any more cleanup
//
// please, note that we did not really complete our
// initialization yet, so we still may fail
//
return S_FALSE;
}
//
// only one thread can make it to this point
//
g_hWiaEventArrived = CreateEvent(NULL, FALSE, FALSE, NULL);
if(g_hWiaEventArrived == NULL) {
DBG_TRACE(("WiaEventsInitialize failed to create async RPC event (Error %d)", GetLastError()));
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
// register for g_hWiaServiceStarted notification
if(!RegisterWaitForSingleObject(&g_hWaitForWiaServiceStarted,
g_hWiaServiceStarted,
WiaEServiceStartedCallback,
NULL,
INFINITE,
WT_EXECUTEDEFAULT))
{
DBG_TRACE(("WiaEventsInitialize failed to register wait for ServiceStarted event event (Error %d)", GetLastError()));
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
if(!RegisterWaitForSingleObject(&g_hWaitForWiaEventArrived,
g_hWiaEventArrived,
WiaERpcCallback,
NULL,
INFINITE,
WT_EXECUTEDEFAULT))
{
DBG_TRACE(("WiaEventsInitialize failed to register wait for RPC result event (Error %d)", GetLastError()));
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
//
// attempt to issue first async RPC call
//
g_LastRpcCallStatus = WiaEPrepareAsyncCall(g_pAsync);
Cleanup:
if(FAILED(hr)) {
if(g_hWaitForWiaServiceStarted) {
UnregisterWaitEx(g_hWaitForWiaServiceStarted, INVALID_HANDLE_VALUE);
g_hWaitForWiaServiceStarted = NULL;
}
if(g_hWaitForWiaEventArrived) {
UnregisterWaitEx(g_hWaitForWiaEventArrived, INVALID_HANDLE_VALUE);
g_hWaitForWiaEventArrived = NULL;
}
WiaECloseHandle(g_hWiaServiceStarted);
WiaECloseHandle(g_hWiaEventArrived);
}
WiaELocalFree(sa.lpSecurityDescriptor);
return hr;
}
void WINAPI WiaEventsTerminate()
{
// TBD
}