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.

546 lines
18 KiB

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