Source code of Windows XP (NT5)
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.

519 lines
17 KiB

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. event.c
  5. Abstract:
  6. This module contains miscellaneous Configuration Manager API routines.
  7. CMP_RegisterNotification
  8. CMP_UnregisterNotification
  9. Author:
  10. Jim Cavalaris (jamesca) 05-05-2001
  11. Environment:
  12. User mode only.
  13. Revision History:
  14. 05-May-2001 jamesca
  15. Creation and initial implementation (moved from cfgmgr32\misc.c).
  16. --*/
  17. //
  18. // includes
  19. //
  20. #include "precomp.h"
  21. #include "cfgi.h"
  22. #include "winsvcp.h"
  23. //
  24. // global data
  25. //
  26. #ifndef _WIN64
  27. extern BOOL IsWow64; // set if we're running under WOW64, externed from setupapi\dll.c
  28. #endif // _WIN64
  29. //
  30. // GetModuleFileNameExW, dynamically loaded by CMP_RegisterNotification
  31. //
  32. typedef DWORD (WINAPI *PFN_GETMODULEFILENAMEEXW)(
  33. IN HANDLE hProcess,
  34. IN HMODULE hModule,
  35. OUT LPWSTR lpFilename,
  36. IN DWORD nSize
  37. );
  38. CONFIGRET
  39. CMP_RegisterNotification(
  40. IN HANDLE hRecipient,
  41. IN LPBYTE NotificationFilter,
  42. IN DWORD Flags,
  43. OUT PNP_NOTIFICATION_CONTEXT *Context
  44. )
  45. /*++
  46. Routine Description:
  47. This routine registers the specified handle for the type of Plug and Play
  48. device event notification specified by the NotificationFilter.
  49. Parameters:
  50. hRecipient - Handle to register as the notification recipient. May be a
  51. window handle or service status handle, and must be specified
  52. with the appropriate flags.
  53. NotificationFilter - Specifies a notification filter that specifies the type
  54. of events to register for. The Notification filter specifies a
  55. pointer to a DEV_BROADCAST_HEADER structure, whose
  56. dbch_devicetype member indicates the actual type of the
  57. NotificationFilter.
  58. Currently, may be one of the following:
  59. DEV_BROADCAST_HANDLE (DBT_DEVTYP_HANDLE type)
  60. DEV_BROADCAST_DEVICEINTERFACE (DBT_DEVTYP_DEVICEINTERFACE type)
  61. Flags - Specifies additional flags for the operation. The following flags
  62. are currently defined:
  63. DEVICE_NOTIFY_WINDOW_HANDLE -
  64. hRecipient specifies a window handle.
  65. DEVICE_NOTIFY_SERVICE_HANDLE -
  66. hRecipient specifies a service status handle.
  67. DEVICE_NOTIFY_COMPLETION_HANDLE -
  68. Not currently implemented.
  69. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES - Specifies that the
  70. notification request is for all device interface change
  71. events. Only valid with a DEV_BROADCAST_DEVICEINTERFACE
  72. NotificationFilter. If this flag is specified the
  73. dbcc_classguid field is ignored.
  74. Context - Receives a notification context. This context is supplied to the
  75. server via PNP_UnregisterNotification to unregister the
  76. corresponding notification handle.
  77. Return Value:
  78. Returns CR_SUCCESS if the component was successfully registered for
  79. notification. Returns CR_FAILURE otherwise.
  80. Notes:
  81. This CM API does not allow the client to specify a server name because the
  82. RPC call is always made to the local server. This routine will never call
  83. the corresponding RPC server interface (PNP_RegisterNotification)
  84. remotely. Additionally, this routine is private, and should only be called
  85. via user32!RegisterDeviceNotification.
  86. --*/
  87. {
  88. CONFIGRET Status = CR_SUCCESS;
  89. NTSTATUS ntStatus;
  90. handle_t hBinding = NULL;
  91. ULONG ulSize;
  92. PPNP_CLIENT_CONTEXT ClientContext;
  93. ULONG64 ClientContext64;
  94. WCHAR ClientName[MAX_SERVICE_NAME_LEN];
  95. try {
  96. //
  97. // validate parameters
  98. //
  99. if (!ARGUMENT_PRESENT(Context)) {
  100. Status = CR_INVALID_POINTER;
  101. goto Clean0;
  102. }
  103. *Context = NULL;
  104. if ((!ARGUMENT_PRESENT(NotificationFilter)) ||
  105. (hRecipient == NULL)) {
  106. Status = CR_INVALID_POINTER;
  107. goto Clean0;
  108. }
  109. //
  110. // DEVICE_NOTIFY_BITS is a private mask, defined specifically for
  111. // validation by the client and server. It contains the bitmask for all
  112. // handle types (DEVICE_NOTIFY_COMPLETION_HANDLE specifically excluded
  113. // by the server), and all other flags that are currently defined - both
  114. // public and reserved.
  115. //
  116. if (INVALID_FLAGS(Flags, DEVICE_NOTIFY_BITS)) {
  117. Status = CR_INVALID_FLAG;
  118. goto Clean0;
  119. }
  120. //
  121. // Make sure the caller didn't specify any private flags. Flags in this
  122. // range are currently reserved for use by CFGMGR32 and UMPNPMGR only!!
  123. //
  124. if ((Flags & DEVICE_NOTIFY_RESERVED_MASK) != 0) {
  125. Status = CR_INVALID_FLAG;
  126. goto Clean0;
  127. }
  128. //
  129. // validate the notification filter. UlSize is used as an explicit
  130. // parameter to let RPC know how much data to marshall, though the
  131. // server validates the size in the structure against it as well.
  132. //
  133. ulSize = ((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size;
  134. if (ulSize < sizeof(DEV_BROADCAST_HDR)) {
  135. Status = CR_INVALID_DATA;
  136. goto Clean0;
  137. }
  138. #ifndef _WIN64
  139. //
  140. // Determine if the 32 bit client is running on WOW64, and set the
  141. // reserved flags appropriately.
  142. //
  143. if (IsWow64) {
  144. Flags |= DEVICE_NOTIFY_WOW64_CLIENT;
  145. }
  146. #endif // _WIN64
  147. //
  148. // setup rpc binding handle (don't need string table handle)
  149. //
  150. if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) {
  151. Status = CR_FAILURE;
  152. goto Clean0;
  153. }
  154. //
  155. // Allocate client context handle from the local process heap.
  156. //
  157. ClientContext = LocalAlloc(0, sizeof(PNP_CLIENT_CONTEXT));
  158. if (ClientContext == NULL) {
  159. Status = CR_OUT_OF_MEMORY;
  160. goto Clean0;
  161. }
  162. //
  163. // Put a signature on the client context, to be checked (and
  164. // invalidated) at unregistration time.
  165. //
  166. ClientContext->PNP_CC_Signature = CLIENT_CONTEXT_SIGNATURE;
  167. ClientContext->PNP_CC_ContextHandle = 0;
  168. memset(ClientName, 0, sizeof(ClientName));
  169. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_WINDOW_HANDLE) {
  170. DWORD dwLength = 0;
  171. //
  172. // first, try to retrieve the window text of the window being
  173. // registered for device event notification. we'll pass this into
  174. // UMPNPMGR for use as an identifier when the window vetoes device
  175. // event notifications.
  176. //
  177. dwLength = GetWindowText(hRecipient,
  178. ClientName,
  179. MAX_SERVICE_NAME_LEN);
  180. if (dwLength == 0) {
  181. //
  182. // GetWindowText did not return any text. Attempt to retrieve
  183. // the process module name instead.
  184. //
  185. DWORD dwProcessId;
  186. HANDLE hProcess;
  187. HMODULE hPsApiDll;
  188. PFN_GETMODULEFILENAMEEXW pfnGetModuleFileNameExW;
  189. //
  190. // get the id of the process that this window handle is
  191. // associated with.
  192. //
  193. if (GetWindowThreadProcessId(hRecipient, &dwProcessId)) {
  194. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  195. FALSE,
  196. dwProcessId);
  197. if (hProcess) {
  198. //
  199. // load the psapi.dll library and find the the
  200. // GetModuleFileNameExW entry point.
  201. //
  202. hPsApiDll = LoadLibrary(TEXT("psapi.dll"));
  203. if (hPsApiDll) {
  204. pfnGetModuleFileNameExW =
  205. (PFN_GETMODULEFILENAMEEXW)GetProcAddress(hPsApiDll,
  206. "GetModuleFileNameExW");
  207. if (pfnGetModuleFileNameExW) {
  208. //
  209. // retrieve the module file name for the process
  210. // this window handle is associated with.
  211. //
  212. dwLength = pfnGetModuleFileNameExW(hProcess,
  213. NULL,
  214. ClientName,
  215. MAX_SERVICE_NAME_LEN);
  216. } else {
  217. KdPrintEx((DPFLTR_PNPMGR_ID,
  218. DBGF_ERRORS | DBGF_EVENT,
  219. "CFGMGR32: CMP_RegisterNotification: GetProcAddress returned error = %d\n",
  220. GetLastError()));
  221. }
  222. FreeLibrary(hPsApiDll);
  223. }
  224. CloseHandle(hProcess);
  225. } else {
  226. KdPrintEx((DPFLTR_PNPMGR_ID,
  227. DBGF_ERRORS | DBGF_EVENT,
  228. "CFGMGR32: CMP_RegisterNotification: OpenProcess returned error = %d\n",
  229. GetLastError()));
  230. }
  231. } else {
  232. KdPrintEx((DPFLTR_PNPMGR_ID,
  233. DBGF_ERRORS | DBGF_EVENT,
  234. "CFGMGR32: CMP_RegisterNotification: GetWindowThreadProcessId returned error = %d\n",
  235. GetLastError()));
  236. }
  237. }
  238. if (dwLength == 0) {
  239. //
  240. // could not retrieve any identifier for this window.
  241. //
  242. ClientName[0] = UNICODE_NULL;
  243. KdPrintEx((DPFLTR_PNPMGR_ID,
  244. DBGF_WARNINGS | DBGF_EVENT,
  245. "CFGMGR32: CMP_RegisterNotification: Could not retieve any name for window %d!!\n",
  246. hRecipient));
  247. }
  248. } else if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
  249. //
  250. // Get the name of the service corresponding to the service status
  251. // handle supplied.
  252. //
  253. if (NO_ERROR != I_ScPnPGetServiceName(hRecipient, ClientName, MAX_SERVICE_NAME_LEN)) {
  254. Status = CR_INVALID_DATA;
  255. LocalFree(ClientContext);
  256. goto Clean0;
  257. }
  258. //
  259. // Just set this to point to the buffer we use. PNP_RegisterNotification will unpack it.
  260. //
  261. hRecipient = ClientName;
  262. }
  263. //
  264. // The client context pointer is now always transmitted to the server as
  265. // a 64-bit value - which is large enough to hold the pointer in both
  266. // the 32-bit and 64-bit cases. This standardizes the RPC interface for
  267. // all clients, since RPC will always marshall a 64-bit value. The
  268. // server will also store the value internally as a 64-bit value, but
  269. // cast it to an HDEVNOTIFY of appropriate size for the client.
  270. //
  271. // Note that we have RPC transmit this parameter simply as a pointer to
  272. // a ULONG64 (which is actually a pointer itself). We don't transmit it
  273. // as a pointer to a PPNP_CLIENT_CONTEXT (which is also a pointer)
  274. // because RPC would instead allocate the memory to marshall the
  275. // contents of the structure to the server. The server would get a
  276. // pointer to RPC allocated memory, not the actual value of the client
  277. // pointer - which is all we really want to send in the first place.
  278. // The server does not actually use this value as a pointer to anything.
  279. //
  280. ClientContext64 = (ULONG64)ClientContext;
  281. RpcTryExcept {
  282. //
  283. // call rpc service entry point
  284. //
  285. Status = PNP_RegisterNotification(
  286. hBinding,
  287. (ULONG_PTR)hRecipient,
  288. ClientName,
  289. NotificationFilter,
  290. ulSize,
  291. Flags,
  292. &((PNP_NOTIFICATION_CONTEXT)(ClientContext->PNP_CC_ContextHandle)),
  293. GetCurrentProcessId(),
  294. &((ULONG64)ClientContext64));
  295. } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
  296. KdPrintEx((DPFLTR_PNPMGR_ID,
  297. DBGF_WARNINGS | DBGF_EVENT,
  298. "PNP_RegisterNotification caused an exception (%d)\n",
  299. RpcExceptionCode()));
  300. Status = MapRpcExceptionToCR(RpcExceptionCode());
  301. }
  302. RpcEndExcept
  303. if (Status != CR_SUCCESS) {
  304. //
  305. // Something went wrong. If we built a context handle
  306. // let it dangle; we can't tell RPC it's gone. (will get rundown)
  307. // If it's NULL, free the memory.
  308. // Don't tell the client we succeeded
  309. //
  310. if (ClientContext->PNP_CC_ContextHandle == 0) {
  311. LocalFree (ClientContext);
  312. }
  313. *Context = NULL;
  314. } else {
  315. *Context = (PNP_NOTIFICATION_CONTEXT)ClientContext;
  316. }
  317. Clean0:
  318. NOTHING;
  319. } except(EXCEPTION_EXECUTE_HANDLER) {
  320. Status = CR_FAILURE;
  321. }
  322. return Status;
  323. } // CMP_RegisterNotification
  324. CONFIGRET
  325. CMP_UnregisterNotification(
  326. IN ULONG_PTR Context
  327. )
  328. /*++
  329. Routine Description:
  330. This routine unregisters the Plug and Play device event notification entry
  331. represented by the specified notification context.
  332. Parameters:
  333. Context - Supplies a client notification context.
  334. Return Value:
  335. Returns CR_SUCCESS if the component was successfully unregistered for
  336. notification. If the function fails, the return value is one of the
  337. following:
  338. CR_FAILURE,
  339. CR_INVALID_POINTER
  340. Notes:
  341. This CM API does not allow the client to specify a server name because the
  342. RPC call is always made to the local server. This routine will never call
  343. the corresponding RPC server interface (PNP_UnregisterNotification)
  344. remotely. Additionally, this routine is private, and should only be called
  345. via user32!UnregisterDeviceNotification.
  346. --*/
  347. {
  348. CONFIGRET Status = CR_SUCCESS;
  349. handle_t hBinding = NULL;
  350. PPNP_CLIENT_CONTEXT ClientContext = (PPNP_CLIENT_CONTEXT)Context;
  351. try {
  352. //
  353. // validate parameters
  354. //
  355. if (Context == 0 || Context == (ULONG_PTR)(-1)) {
  356. Status = CR_INVALID_POINTER;
  357. goto Clean0;
  358. }
  359. //
  360. // make sure the client context signature is valid
  361. //
  362. if (ClientContext->PNP_CC_Signature != CLIENT_CONTEXT_SIGNATURE) {
  363. KdPrintEx((DPFLTR_PNPMGR_ID,
  364. DBGF_ERRORS,
  365. "CMP_UnregisterNotification: bad signature on client handle\n"));
  366. Status = CR_INVALID_POINTER;
  367. goto Clean0;
  368. }
  369. //
  370. // setup rpc binding handle (don't need string table handle)
  371. //
  372. if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) {
  373. Status = CR_FAILURE;
  374. goto Clean0;
  375. }
  376. RpcTryExcept {
  377. //
  378. // call rpc service entry point
  379. //
  380. Status = PNP_UnregisterNotification(
  381. hBinding,
  382. (PPNP_NOTIFICATION_CONTEXT)&(ClientContext->PNP_CC_ContextHandle));
  383. } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
  384. KdPrintEx((DPFLTR_PNPMGR_ID,
  385. DBGF_WARNINGS | DBGF_EVENT,
  386. "PNP_UnregisterNotification caused an exception (%d)\n",
  387. RpcExceptionCode()));
  388. Status = MapRpcExceptionToCR(RpcExceptionCode());
  389. }
  390. RpcEndExcept
  391. if (Status == CR_SUCCESS) {
  392. //
  393. // invalidate the client context signature and free the client
  394. // context structure.
  395. //
  396. ClientContext->PNP_CC_Signature = 0;
  397. LocalFree((PVOID)Context);
  398. }
  399. Clean0:
  400. NOTHING;
  401. } except(EXCEPTION_EXECUTE_HANDLER) {
  402. Status = CR_FAILURE;
  403. }
  404. return Status;
  405. } // CMP_UnregisterNotification