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

  1. #include "precomp.h"
  2. #include "wia.h"
  3. #include "stirpc.h"
  4. #define INIT_GUID
  5. // {8144B6F5-20A8-444a-B8EE-19DF0BB84BDB}
  6. DEFINE_GUID( CLSID_StiEventHandler, 0x8144b6f5, 0x20a8, 0x444a, 0xb8, 0xee, 0x19, 0xdf, 0xb, 0xb8, 0x4b, 0xdb );
  7. #define WIA_SERVICE_STARTING_EVENT_NAME TEXT("Global\\WiaServiceStarted")
  8. //
  9. // (A;;GA;;;SY) becomes (A;;0x1f003;;;SY) when converted back to string
  10. // 0x1f0000 is SYNCHORNIZE | STANDARD_RIGHTS_REQUIRED
  11. // 0x000003 is specific rigts for ???
  12. //
  13. #define WIA_SERVICE_STARTING_EVENT_DACL \
  14. TEXT("D:(A;;0x1f0003;;;SY)(A;;0x1f0003;;;LS)(A;;0x1f0003;;;LA)")
  15. const TCHAR g_szWiaServiceStartedEventName[] = WIA_SERVICE_STARTING_EVENT_NAME;
  16. const TCHAR g_WiaESDString[] = WIA_SERVICE_STARTING_EVENT_DACL;
  17. // event and event wait handles
  18. HANDLE g_hWiaServiceStarted = NULL; // event
  19. HANDLE g_hWaitForWiaServiceStarted = NULL; // wait
  20. HANDLE g_hWiaEventArrived = NULL; // event
  21. HANDLE g_hWaitForWiaEventArrived = NULL; // wait
  22. // async RPC request
  23. RPC_BINDING_HANDLE g_AsyncRpcBinding = NULL;
  24. RPC_STATUS g_LastRpcCallStatus = RPC_S_OK;
  25. RPC_ASYNC_STATE g_Async = { 0 };
  26. PRPC_ASYNC_STATE g_pAsync = &g_Async;
  27. // event structure filled by async RPC call
  28. WIA_ASYNC_EVENT_NOTIFY_DATA g_Event = { 0 };
  29. #ifdef DEBUG
  30. #define DBG_TRACE(x) DebugTrace x
  31. #else
  32. #define DBG_TRACE(x)
  33. #endif
  34. void DebugTrace(LPCSTR fmt, ...)
  35. {
  36. char buffer[1024] = "WIARPC:";
  37. const blen = 7;
  38. size_t len = (sizeof(buffer) / sizeof(buffer[0])) - blen;
  39. va_list marker;
  40. va_start(marker, fmt);
  41. _vsnprintf(buffer + blen, len - 3, fmt, marker);
  42. buffer[len - 3] = 0;
  43. len = strlen(buffer);
  44. if(len > 0) {
  45. if(buffer[len - 1] != '\n') {
  46. buffer[len++] = '\n';
  47. buffer[len] = '\0';
  48. }
  49. OutputDebugStringA(buffer);
  50. }
  51. va_end(marker);
  52. }
  53. // aux function to call LocalFree() safely on a pointer and zero it
  54. template <typename t>
  55. void WiaELocalFree(t& ptr) {
  56. if(ptr) {
  57. LocalFree(static_cast<HLOCAL>(ptr));
  58. ptr = NULL;
  59. }
  60. }
  61. // aux function to call CloseHanlde() on a valid handle and zero it
  62. void WiaECloseHandle(HANDLE& h)
  63. {
  64. if(h && h != INVALID_HANDLE_VALUE) {
  65. CloseHandle(h);
  66. h = NULL;
  67. }
  68. }
  69. //
  70. // Returns TRUE if event's security descriptor is exactly the one we'd
  71. // set it to be, FALSE otherwise
  72. //
  73. BOOL WiaECheckEventSecurity(HANDLE hEvent)
  74. {
  75. BOOL success = FALSE;
  76. PACL pDacl;
  77. PSECURITY_DESCRIPTOR pDescriptor = NULL;
  78. LPTSTR stringDescriptor = NULL;
  79. ULONG stringLength;
  80. if(ERROR_SUCCESS != GetSecurityInfo(hEvent, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
  81. NULL, NULL, &pDacl, NULL, &pDescriptor))
  82. {
  83. //
  84. // failed to retrieve event's security descriptor -- this is a
  85. // failure
  86. //
  87. DBG_TRACE(("Failed to retrieve event security descriptor (Error %d)", GetLastError()));
  88. goto Done;
  89. }
  90. if(!ConvertSecurityDescriptorToStringSecurityDescriptor(pDescriptor,
  91. SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &stringDescriptor, &stringLength))
  92. {
  93. //
  94. // failed to convert event's security descriptor to string --
  95. // this is also a failure
  96. //
  97. DBG_TRACE(("Failed to convert security descriptor to string (Error %d)", GetLastError()));
  98. goto Done;
  99. }
  100. if(lstrcmp(g_WiaESDString, stringDescriptor) != 0)
  101. {
  102. //
  103. // descriptors are different, this is a failure
  104. //
  105. DBG_TRACE(("String security descriptor of WIA event is unexpected: \r\n'%S'\r\n instead of \r\n'%S'\r\n)",
  106. stringDescriptor, g_WiaESDString));
  107. goto Done;
  108. }
  109. success = TRUE;
  110. Done:
  111. WiaELocalFree(pDescriptor);
  112. WiaELocalFree(stringDescriptor);
  113. return success;
  114. }
  115. //
  116. //
  117. //
  118. RPC_STATUS WiaEPrepareAsyncCall(PRPC_ASYNC_STATE pAsync)
  119. {
  120. RPC_STATUS status;
  121. LPTSTR binding = NULL;
  122. status = RpcStringBindingCompose(NULL, STI_LRPC_SEQ, NULL, STI_LRPC_ENDPOINT, NULL, &binding);
  123. if(status) {
  124. DBG_TRACE(("Failed to compose string binding (Error %d)", status));
  125. goto Done;
  126. }
  127. status = RpcBindingFromStringBinding(binding, &g_AsyncRpcBinding);
  128. if(status) {
  129. DBG_TRACE(("Failed to build async RPC binding (Error %d)", status));
  130. goto Done;
  131. }
  132. status = RpcAsyncInitializeHandle(pAsync, sizeof(RPC_ASYNC_STATE));
  133. if(status) {
  134. DBG_TRACE(("Failed to initialize RPC handle (Error %d)", status));
  135. goto Done;
  136. }
  137. pAsync->UserInfo = NULL;
  138. pAsync->NotificationType = RpcNotificationTypeEvent;
  139. pAsync->u.hEvent = g_hWiaEventArrived;
  140. // store the pointer to async into global, so that if
  141. // the result of R_WiaGetEventDataAsync() arrives soon, it would
  142. // not land without it
  143. InterlockedExchangePointer((PVOID*)&g_pAsync, pAsync);
  144. RpcTryExcept {
  145. R_WiaGetEventDataAsync(pAsync, g_AsyncRpcBinding, &g_Event);
  146. } RpcExcept(1) {
  147. status = RpcExceptionCode();
  148. DBG_TRACE(("Exception 0x%x calling WIA RPC server", status));
  149. } RpcEndExcept;
  150. Done:
  151. if(status && g_AsyncRpcBinding) {
  152. RpcTryExcept {
  153. RpcBindingFree(&g_AsyncRpcBinding);
  154. } RpcExcept(1) {
  155. status = RpcExceptionCode();
  156. DBG_TRACE(("Exception 0x%x while freeing binding handle", status));
  157. } RpcEndExcept;
  158. g_AsyncRpcBinding = NULL;
  159. }
  160. if(binding) RpcStringFree(&binding);
  161. return status;
  162. }
  163. #define SESSION_MONIKER TEXT("Session:Console!clsid:")
  164. #include <initguid.h>
  165. DEFINE_GUID(CLSID_DefWiaHandler,
  166. 0xD13E3F25, 0x1688, 0x45A0,
  167. 0x97, 0x43, 0x75, 0x9E, 0xB3, 0x5C, 0xDF, 0x9A);
  168. #include "sticfunc.h"
  169. /**************************************************************************\
  170. * _CoCreateInstanceInConsoleSession
  171. *
  172. * This helper function acts the same as CoCreateInstance, but will launch
  173. * a out-of-process COM server on the correct user's desktop, taking
  174. * fast user switching into account. (Normal CoCreateInstance will
  175. * launch it on the first logged on user's desktop, instead of the currently
  176. * logged on one).
  177. *
  178. * This code was taken with permission from the Shell's Hardware
  179. * Notification service, on behalf of StephStm.
  180. *
  181. * Arguments:
  182. *
  183. * rclsid, // Class identifier (CLSID) of the object
  184. * pUnkOuter, // Pointer to controlling IUnknown
  185. * dwClsContext // Context for running executable code
  186. * riid, // Reference to the identifier of the interface
  187. * ppv // Address of output variable that receives
  188. * // the interface pointer requested in riid
  189. *
  190. * Return Value:
  191. *
  192. * Status
  193. *
  194. * History:
  195. *
  196. * 03/01/2001 Original Version
  197. *
  198. \**************************************************************************/
  199. HRESULT _CoCreateInstanceInConsoleSession(REFCLSID rclsid,
  200. IUnknown* punkOuter,
  201. DWORD dwClsContext,
  202. REFIID riid,
  203. void** ppv)
  204. {
  205. IBindCtx *pbc = NULL;
  206. HRESULT hr = CreateBindCtx(0, &pbc); // Create a bind context for use with Moniker
  207. //
  208. // Set the return
  209. //
  210. *ppv = NULL;
  211. if (SUCCEEDED(hr)) {
  212. WCHAR wszCLSID[39];
  213. //
  214. // Convert the riid to GUID string for use in binding to moniker
  215. //
  216. if (StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]))) {
  217. ULONG ulEaten = 0;
  218. IMoniker* pmoniker = NULL;
  219. WCHAR wszDisplayName[sizeof(SESSION_MONIKER)/sizeof(WCHAR) + sizeof(wszCLSID)/sizeof(wszCLSID[0]) + 2] = SESSION_MONIKER;
  220. //
  221. // We want something like: "Session:Console!clsid:760befd0-5b0b-44d7-957e-969af35ce954"
  222. // Notice that we don't want the leading and trailing brackets {..} around the GUID.
  223. // So, first get rid of trailing bracket by overwriting it with termintaing '\0'
  224. //
  225. wszCLSID[lstrlenW(wszCLSID) - 1] = L'\0';
  226. //
  227. // Form display name string. To get rid of the leading bracket, we pass in the
  228. // address of the next character as the start of the string.
  229. //
  230. if (lstrcatW(wszDisplayName, &(wszCLSID[1]))) {
  231. //
  232. // Parse the name and get a moniker:
  233. //
  234. hr = MkParseDisplayName(pbc, wszDisplayName, &ulEaten, &pmoniker);
  235. if (SUCCEEDED(hr)) {
  236. IClassFactory *pcf = NULL;
  237. //
  238. // Attempt to get the class factory
  239. //
  240. hr = pmoniker->BindToObject(pbc, NULL, IID_IClassFactory, (void**)&pcf);
  241. if (SUCCEEDED(hr))
  242. {
  243. //
  244. // Attempt to create the object
  245. //
  246. hr = pcf->CreateInstance(punkOuter, riid, ppv);
  247. DBG_TRACE(("_CoCreateInstanceInConsoleSession, pcf->CreateInstance returned: hr = 0x%08X", hr));
  248. pcf->Release();
  249. } else {
  250. DBG_TRACE(("_CoCreateInstanceInConsoleSession, pmoniker->BindToObject returned: hr = 0x%08X", hr));
  251. }
  252. pmoniker->Release();
  253. } else {
  254. DBG_TRACE(("_CoCreateInstanceInConsoleSession, MkParseDisplayName returned: hr = 0x%08X", hr));
  255. }
  256. } else {
  257. DBG_TRACE(("_CoCreateInstanceInConsoleSession, string concatenation failed"));
  258. hr = E_INVALIDARG;
  259. }
  260. } else {
  261. DBG_TRACE(("_CoCreateInstanceInConsoleSession, StringFromGUID2 failed"));
  262. hr = E_INVALIDARG;
  263. }
  264. pbc->Release();
  265. } else {
  266. DBG_TRACE(("_CoCreateInstanceInConsoleSession, CreateBindCtxt returned: hr = 0x%08X", hr));
  267. }
  268. return hr;
  269. }
  270. /**************************************************************************\
  271. * GetUserTokenForConsoleSession
  272. *
  273. * This helper function will grab the currently logged on interactive
  274. * user's token, which can be used in a call to CreateProcessAsUser.
  275. * Caller is responsible for closing this Token handle.
  276. *
  277. * It first grabs the impersontaed token from the current session (our
  278. * service runs in session 0, but with Fast User Switching, the currently
  279. * active user may be in a different session). It then creates a
  280. * primary token from the impersonated one.
  281. *
  282. * Arguments:
  283. *
  284. * None
  285. *
  286. * Return Value:
  287. *
  288. * HANDLE to Token for logged on user in the currently active session.
  289. * NULL otherwise.
  290. *
  291. * History:
  292. *
  293. * 03/05/2001 Original Version
  294. *
  295. \**************************************************************************/
  296. HANDLE GetUserTokenForConsoleSession()
  297. {
  298. HANDLE hImpersonationToken = NULL;
  299. HANDLE hTokenUser = NULL;
  300. //
  301. // Get interactive user's token
  302. //
  303. if (GetWinStationUserToken(GetCurrentSessionID(), &hImpersonationToken)) {
  304. //
  305. // Maybe nobody is logged on, so do a check first.
  306. //
  307. if (hImpersonationToken) {
  308. //
  309. // We duplicate the token, since the returned token is an
  310. // impersonated one, and we need it to be primary for
  311. // use in CreateProcessAsUser.
  312. //
  313. if (!DuplicateTokenEx(hImpersonationToken,
  314. 0,
  315. NULL,
  316. SecurityImpersonation,
  317. TokenPrimary,
  318. &hTokenUser)) {
  319. DBG_TRC(("CEventNotifier::StartCallbackProgram, DuplicateTokenEx failed! GetLastError() = 0x%08X", GetLastError()));
  320. }
  321. } else {
  322. DBG_PRT(("CEventNotifier::StartCallbackProgram, No user appears to be logged on..."));
  323. }
  324. } else {
  325. DBG_TRACE(("CEventNotifier::StartCallbackProgram, GetWinStationUserToken failed! GetLastError() = 0x%08X", GetLastError()));
  326. }
  327. //
  328. // Close the impersonated token, since we no longer need it.
  329. //
  330. if (hImpersonationToken) {
  331. CloseHandle(hImpersonationToken);
  332. }
  333. return hTokenUser;
  334. }
  335. /**************************************************************************\
  336. * PrepareCommandline
  337. *
  338. * This helper function will prepare the commandline for apps not registered
  339. * as local out-of-process COM servers. We place the event guid and device
  340. * id in the command line.
  341. *
  342. *
  343. * Arguments:
  344. *
  345. * CSimpleStringWide &cswDeviceID - the device which generated this event
  346. * GUID &guidEvent - the GUID indicating which event occured.
  347. * CSimpleStringWide &cswRegisteredCOmmandline - the commandline this handler
  348. * registered with. This commandline
  349. * contains the tokens that must
  350. * be substituted.
  351. *
  352. * Return Value:
  353. *
  354. * CSimpleStringWide - contians the parsed commandline which has the
  355. * device id and event guid substituted.
  356. *
  357. * History:
  358. *
  359. * 03/05/2001 Original Version
  360. *
  361. \**************************************************************************/
  362. CSimpleStringWide PrepareCommandline(
  363. const CSimpleStringWide &cswDeviceID,
  364. const GUID &guidEvent,
  365. const CSimpleStringWide &cswRegisteredCommandline)
  366. {
  367. WCHAR wszGUIDStr[40];
  368. WCHAR wszCommandline[MAX_PATH];
  369. WCHAR *pPercentSign;
  370. WCHAR *pTest = NULL;
  371. CSimpleStringWide cswCommandLine;
  372. //
  373. // ISSUE: This code could be written better. For now, we're not touching it
  374. // and keeping it the same as the WinXP code base.
  375. //
  376. //
  377. // Fix up the commandline. First check that it has at least two %
  378. //
  379. pTest = wcschr(cswRegisteredCommandline.String(), '%');
  380. if (pTest) {
  381. pTest = wcschr(pTest + 1, '%');
  382. }
  383. if (!pTest) {
  384. _snwprintf(
  385. wszCommandline,
  386. sizeof(wszCommandline) / sizeof( wszCommandline[0] ),
  387. L"%s /StiDevice:%%1 /StiEvent:%%2",
  388. cswRegisteredCommandline.String());
  389. } else {
  390. wcsncpy(wszCommandline, cswRegisteredCommandline.String(), sizeof(wszCommandline) / sizeof( wszCommandline[0] ));
  391. }
  392. //
  393. // enforce null termination
  394. //
  395. wszCommandline[ (sizeof(wszCommandline) / sizeof(wszCommandline[0])) - 1 ] = 0;
  396. //
  397. // Change the number {1|2} into s
  398. //
  399. pPercentSign = wcschr(wszCommandline, L'%');
  400. *(pPercentSign + 1) = L's';
  401. pPercentSign = wcschr(pPercentSign + 1, L'%');
  402. *(pPercentSign + 1) = L's';
  403. //
  404. // Convert the GUID into string
  405. //
  406. StringFromGUID2(guidEvent, wszGUIDStr, 40);
  407. //
  408. // Final comand line
  409. //
  410. //swprintf(pwszResCmdline, wszCommandline, bstrDeviceID, wszGUIDStr);
  411. cswCommandLine.Format(wszCommandline, cswDeviceID.String(), wszGUIDStr);
  412. return cswCommandLine;
  413. }
  414. void FireStiEvent()
  415. {
  416. StiEventHandlerLookup stiLookup;
  417. //
  418. // Get the STI handler list for this device event. This will be returned as a double-NULL
  419. // terminated BSTR.
  420. //
  421. BSTR bstrAppList = stiLookup.getStiAppListForDeviceEvent(g_Event.bstrDeviceID, g_Event.EventGuid);
  422. if (bstrAppList)
  423. {
  424. HRESULT hr = S_OK;
  425. IWiaEventCallback *pIEventCB = NULL;
  426. ULONG ulEventType = WIA_ACTION_EVENT;
  427. //
  428. // CoCreate our event UI handler. Note that it will not display any UI
  429. // if there is only one application.
  430. //
  431. hr = CoCreateInstance(
  432. CLSID_StiEventHandler,
  433. NULL,
  434. CLSCTX_LOCAL_SERVER,
  435. IID_IWiaEventCallback,
  436. (void**)&pIEventCB);
  437. if (SUCCEEDED(hr)) {
  438. //
  439. // Make the callback. This will display a prompt if our AppList contains more
  440. // than one application.
  441. //
  442. pIEventCB->ImageEventCallback(&g_Event.EventGuid,
  443. g_Event.bstrEventDescription,
  444. g_Event.bstrDeviceID,
  445. g_Event.bstrDeviceDescription,
  446. g_Event.dwDeviceType,
  447. bstrAppList,
  448. &g_Event.ulEventType,
  449. 0);
  450. pIEventCB->Release();
  451. }
  452. SysFreeString(bstrAppList);
  453. bstrAppList = NULL;
  454. }
  455. }
  456. void WiaEFireEvent()
  457. {
  458. HRESULT hr;
  459. IWiaEventCallback *pIEventCB;
  460. //
  461. // ISSUE: For now, we assume this is a WIA event. Really, it could
  462. // be either WIA or STI. STI events need special handling.
  463. //
  464. if (g_Event.ulEventType & STI_DEVICE_EVENT)
  465. {
  466. FireStiEvent();
  467. }
  468. else
  469. {
  470. //
  471. // Find the persistent event handler for this device event
  472. //
  473. EventHandlerInfo *pEventHandlerInfo = NULL;
  474. WiaEventHandlerLookup eventLookup;
  475. pEventHandlerInfo = eventLookup.getPersistentHandlerForDeviceEvent(g_Event.bstrDeviceID, g_Event.EventGuid);
  476. if (pEventHandlerInfo)
  477. {
  478. //
  479. // Check whether this is a out-of-process COM server registed handler or
  480. // a commandline registered handler.
  481. //
  482. if (pEventHandlerInfo->getCommandline().Length() < 1)
  483. {
  484. //
  485. // This is a COM registered handler
  486. //
  487. hr = _CoCreateInstanceInConsoleSession(pEventHandlerInfo->getCLSID(),
  488. NULL,
  489. CLSCTX_LOCAL_SERVER,
  490. IID_IWiaEventCallback,
  491. (void**)&pIEventCB);
  492. if (SUCCEEDED(hr)) {
  493. pIEventCB->ImageEventCallback(&g_Event.EventGuid,
  494. g_Event.bstrEventDescription,
  495. g_Event.bstrDeviceID,
  496. g_Event.bstrDeviceDescription,
  497. g_Event.dwDeviceType,
  498. g_Event.bstrFullItemName,
  499. &g_Event.ulEventType,
  500. 0);
  501. //
  502. // Release the callback interface
  503. //
  504. pIEventCB->Release();
  505. } else {
  506. DBG_ERR(("NotifySTIEvent:CoCreateInstance of event callback failed (0x%X)", hr));
  507. }
  508. }
  509. else
  510. {
  511. //
  512. // This is a commandline registered handler
  513. //
  514. HANDLE hTokenUser = NULL;
  515. STARTUPINFO startupInfo = {0};
  516. PROCESS_INFORMATION processInfo = {0};
  517. LPVOID pEnvBlock = NULL;
  518. BOOL bRet = FALSE;
  519. //
  520. // Get interactive user's token
  521. //
  522. hTokenUser = GetUserTokenForConsoleSession();
  523. //
  524. // Check that somebody is logged in
  525. //
  526. if (hTokenUser)
  527. {
  528. //
  529. // Set up start up info
  530. //
  531. ZeroMemory(&startupInfo, sizeof(startupInfo));
  532. startupInfo.lpDesktop = L"WinSta0\\Default";
  533. startupInfo.cb = sizeof(startupInfo);
  534. startupInfo.wShowWindow = SW_SHOWNORMAL;
  535. //
  536. // Create the user's environment block
  537. //
  538. bRet = CreateEnvironmentBlock(
  539. &pEnvBlock,
  540. hTokenUser,
  541. FALSE);
  542. if (bRet)
  543. {
  544. //
  545. // Prepare the command line. Make sure we pass in the EVENT guid, not the STI proxy guid.
  546. //
  547. CSimpleStringWide cswCommandLine;
  548. cswCommandLine = PrepareCommandline(g_Event.bstrDeviceID,
  549. g_Event.EventGuid,
  550. pEventHandlerInfo->getCommandline());
  551. //
  552. // Create the process in user's context
  553. //
  554. bRet = CreateProcessAsUserW(
  555. hTokenUser,
  556. NULL, // Application name
  557. (LPWSTR)cswCommandLine.String(),
  558. NULL, // Process attributes
  559. NULL, // Thread attributes
  560. FALSE, // Handle inheritance
  561. NORMAL_PRIORITY_CLASS |
  562. CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
  563. pEnvBlock, // Environment
  564. NULL, // Current directory
  565. &startupInfo,
  566. &processInfo);
  567. if (! bRet) {
  568. DBG_ERR(("CreateProcessAsUser failed! GetLastError() = 0x%08X", GetLastError()));
  569. }
  570. }
  571. else
  572. {
  573. DBG_ERR(("CreateEnvironmentBlock failed! GetLastError() = 0x%08X", GetLastError()));
  574. }
  575. }
  576. //
  577. // Garbage collection
  578. //
  579. if (processInfo.hProcess)
  580. {
  581. CloseHandle(processInfo.hProcess);
  582. }
  583. if (processInfo.hThread)
  584. {
  585. CloseHandle(processInfo.hThread);
  586. }
  587. if (hTokenUser)
  588. {
  589. CloseHandle(hTokenUser);
  590. }
  591. if (pEnvBlock)
  592. {
  593. DestroyEnvironmentBlock(pEnvBlock);
  594. }
  595. }
  596. pEventHandlerInfo->Release();
  597. pEventHandlerInfo = NULL;
  598. }
  599. }
  600. }
  601. void WiaEProcessAsyncCallResults(PRPC_ASYNC_STATE pAsync)
  602. {
  603. RPC_STATUS callstatus, status;
  604. int nStatus;
  605. if(g_LastRpcCallStatus) {
  606. DBG_TRACE(("Last RPC call was not successful (error 0x%x, therefore we are not going to look at the results",
  607. g_LastRpcCallStatus));
  608. return;
  609. }
  610. callstatus = RpcAsyncGetCallStatus(pAsync);
  611. RpcTryExcept {
  612. status = RpcAsyncCompleteCall(pAsync, &nStatus);
  613. } RpcExcept(1) {
  614. status = RpcExceptionCode();
  615. } RpcEndExcept;
  616. if(callstatus == RPC_S_OK && status == RPC_S_OK) {
  617. DBG_TRACE(("\r\n\r\n#### Event arrived on '%S', firing it\r\n\r\n", g_Event.bstrDeviceID));
  618. WiaEFireEvent();
  619. } else {
  620. DBG_TRACE(("Failed to complete async RPC call, error 0x%x", status));
  621. }
  622. }
  623. //
  624. //
  625. //
  626. void WiaECleanupAsyncCall(PRPC_ASYNC_STATE pAsync)
  627. {
  628. RPC_STATUS status;
  629. int nReply;
  630. status = RpcAsyncGetCallStatus(pAsync);
  631. switch(status) {
  632. case RPC_S_ASYNC_CALL_PENDING:
  633. DBG_TRACE(("Cancelling pending async RPC call."));
  634. RpcTryExcept {
  635. status = RpcAsyncCancelCall(pAsync, TRUE);
  636. } RpcExcept(1) {
  637. status = RpcExceptionCode();
  638. DBG_TRACE(("Exception 0x%x cancelling outstanding async RPC call", status));
  639. } RpcEndExcept;
  640. break;
  641. case RPC_S_OK:
  642. // already completed, don't do anything with it
  643. break;
  644. default:
  645. DBG_TRACE(("Cleaning up async RPC call status is 0x%x.", status));
  646. RpcTryExcept {
  647. status = RpcAsyncCompleteCall(pAsync, &nReply);
  648. } RpcExcept(1) {
  649. status = RpcExceptionCode();
  650. DBG_TRACE(("Exception 0x%x cancelling outstanding async RPC call", status));
  651. } RpcEndExcept;
  652. }
  653. if(g_AsyncRpcBinding) {
  654. RpcTryExcept {
  655. RpcBindingFree(&g_AsyncRpcBinding);
  656. } RpcExcept(1) {
  657. status = RpcExceptionCode();
  658. DBG_TRACE(("Exception 0x%x while freeing binding handle", status));
  659. } RpcEndExcept;
  660. g_AsyncRpcBinding = NULL;
  661. }
  662. //
  663. // cleanup any BSTRs in g_Event
  664. //
  665. SysFreeString(g_Event.bstrEventDescription);
  666. g_Event.bstrEventDescription = NULL;
  667. SysFreeString(g_Event.bstrDeviceID);
  668. g_Event.bstrDeviceID = NULL;
  669. SysFreeString(g_Event.bstrDeviceDescription);
  670. g_Event.bstrDeviceDescription = NULL;
  671. SysFreeString(g_Event.bstrFullItemName);
  672. g_Event.bstrFullItemName = NULL;
  673. }
  674. VOID CALLBACK
  675. WiaEServiceStartedCallback(LPVOID, BOOLEAN)
  676. {
  677. PRPC_ASYNC_STATE pAsync;
  678. DBG_TRACE(("WIA service is starting"));
  679. pAsync = (PRPC_ASYNC_STATE) InterlockedExchangePointer((PVOID*)&g_pAsync, NULL);
  680. if(pAsync) {
  681. // at this point we are garanteed that
  682. // WiaERpcCallBack will not get to g_Async
  683. // abort any pending RPC calls
  684. WiaECleanupAsyncCall(pAsync);
  685. // initiate new async call
  686. g_LastRpcCallStatus = WiaEPrepareAsyncCall(pAsync);
  687. } else {
  688. DBG_TRACE(("No async pointer"));
  689. }
  690. }
  691. VOID CALLBACK
  692. WiaERpcCallback(PVOID, BOOLEAN)
  693. {
  694. PRPC_ASYNC_STATE pAsync;
  695. DBG_TRACE(("Async RPC event arrived"));
  696. pAsync = (PRPC_ASYNC_STATE) InterlockedExchangePointer((PVOID*)&g_pAsync, NULL);
  697. if(pAsync) {
  698. // at this point we are garanteed that
  699. // WiaEServiceStartedCallback will not get to g_Async
  700. WiaEProcessAsyncCallResults(pAsync);
  701. // cleanup the call
  702. WiaECleanupAsyncCall(pAsync);
  703. // initiate new async call
  704. g_LastRpcCallStatus = WiaEPrepareAsyncCall(pAsync);
  705. } else {
  706. DBG_TRACE(("No async pointer"));
  707. }
  708. }
  709. HRESULT WINAPI WiaEventsInitialize()
  710. {
  711. HRESULT hr = S_OK;
  712. SECURITY_ATTRIBUTES sa = { sizeof(sa), FALSE, NULL };
  713. HANDLE hEvent = NULL;
  714. if(g_hWiaServiceStarted) {
  715. return S_OK;
  716. }
  717. // allocate appropriate security attributes for the named event we
  718. // use to learn about WIA service startup
  719. if(!ConvertStringSecurityDescriptorToSecurityDescriptor(g_WiaESDString,
  720. SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL))
  721. {
  722. DBG_TRACE(("WiaEventsInitialize failed to produce event security descriptor (Error %d)", GetLastError()));
  723. hr = HRESULT_FROM_WIN32(GetLastError());
  724. goto Cleanup;
  725. }
  726. hEvent = CreateEvent(&sa, FALSE, FALSE, WIA_SERVICE_STARTING_EVENT_NAME);
  727. if(hEvent == NULL) {
  728. DBG_TRACE(("WiaEventsInitialize failed to create named event (Error %d)", GetLastError()));
  729. hr = HRESULT_FROM_WIN32(GetLastError());
  730. goto Cleanup;
  731. }
  732. if(GetLastError() == ERROR_ALREADY_EXISTS) {
  733. // interrogate the security descriptor on this event -- does it
  734. // look like ours or is it squatted by a bad guy?
  735. if(!WiaECheckEventSecurity(hEvent)) {
  736. // we don't like how it looks, bail out
  737. hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
  738. goto Cleanup;
  739. }
  740. }
  741. // we already have the event, try to mark our initialization
  742. // complete
  743. hEvent = InterlockedCompareExchangePointer(&g_hWiaServiceStarted, hEvent, NULL);
  744. if(hEvent != NULL) {
  745. //
  746. // oops, another thread beat us to this!
  747. //
  748. // we only allocated our security descriptor, free it
  749. WiaELocalFree(sa.lpSecurityDescriptor);
  750. //
  751. // return right away, don't do any more cleanup
  752. //
  753. // please, note that we did not really complete our
  754. // initialization yet, so we still may fail
  755. //
  756. return S_FALSE;
  757. }
  758. //
  759. // only one thread can make it to this point
  760. //
  761. g_hWiaEventArrived = CreateEvent(NULL, FALSE, FALSE, NULL);
  762. if(g_hWiaEventArrived == NULL) {
  763. DBG_TRACE(("WiaEventsInitialize failed to create async RPC event (Error %d)", GetLastError()));
  764. hr = HRESULT_FROM_WIN32(GetLastError());
  765. goto Cleanup;
  766. }
  767. // register for g_hWiaServiceStarted notification
  768. if(!RegisterWaitForSingleObject(&g_hWaitForWiaServiceStarted,
  769. g_hWiaServiceStarted,
  770. WiaEServiceStartedCallback,
  771. NULL,
  772. INFINITE,
  773. WT_EXECUTEDEFAULT))
  774. {
  775. DBG_TRACE(("WiaEventsInitialize failed to register wait for ServiceStarted event event (Error %d)", GetLastError()));
  776. hr = HRESULT_FROM_WIN32(GetLastError());
  777. goto Cleanup;
  778. }
  779. if(!RegisterWaitForSingleObject(&g_hWaitForWiaEventArrived,
  780. g_hWiaEventArrived,
  781. WiaERpcCallback,
  782. NULL,
  783. INFINITE,
  784. WT_EXECUTEDEFAULT))
  785. {
  786. DBG_TRACE(("WiaEventsInitialize failed to register wait for RPC result event (Error %d)", GetLastError()));
  787. hr = HRESULT_FROM_WIN32(GetLastError());
  788. goto Cleanup;
  789. }
  790. //
  791. // attempt to issue first async RPC call
  792. //
  793. g_LastRpcCallStatus = WiaEPrepareAsyncCall(g_pAsync);
  794. Cleanup:
  795. if(FAILED(hr)) {
  796. if(g_hWaitForWiaServiceStarted) {
  797. UnregisterWaitEx(g_hWaitForWiaServiceStarted, INVALID_HANDLE_VALUE);
  798. g_hWaitForWiaServiceStarted = NULL;
  799. }
  800. if(g_hWaitForWiaEventArrived) {
  801. UnregisterWaitEx(g_hWaitForWiaEventArrived, INVALID_HANDLE_VALUE);
  802. g_hWaitForWiaEventArrived = NULL;
  803. }
  804. WiaECloseHandle(g_hWiaServiceStarted);
  805. WiaECloseHandle(g_hWiaEventArrived);
  806. }
  807. WiaELocalFree(sa.lpSecurityDescriptor);
  808. return hr;
  809. }
  810. void WINAPI WiaEventsTerminate()
  811. {
  812. // TBD
  813. }