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.

331 lines
10 KiB

  1. #include "precomp.h"
  2. //
  3. // watcher.cpp
  4. //
  5. // This file is a big ugly hack to make sure that NetMeeting
  6. // will cleanup is display driver (nmndd.sys) properly. This is
  7. // needed because it uses a mirrored driver which will prevent DX
  8. // games from running if NetMeeting is not shut down cleanly.
  9. //
  10. // Copyright(c) Microsoft 1997-
  11. //
  12. //
  13. // This function actually disables the "NetMeeting driver" display driver
  14. //
  15. BOOL DisableNetMeetingDriver()
  16. {
  17. BOOL bRet = TRUE; // assume success
  18. DISPLAY_DEVICE dd = {0};
  19. int i;
  20. dd.cb = sizeof(dd);
  21. for (i = 0; EnumDisplayDevices(NULL, i, &dd, 0); i++)
  22. {
  23. if ((dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
  24. (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
  25. !lstrcmpi(dd.DeviceString, TEXT("NetMeeting driver")))
  26. {
  27. DEVMODE devmode = {0};
  28. devmode.dmSize = sizeof(devmode);
  29. devmode.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
  30. if ((ChangeDisplaySettingsEx(dd.DeviceName,
  31. &devmode,
  32. NULL,
  33. CDS_UPDATEREGISTRY | CDS_NORESET,
  34. NULL) != DISP_CHANGE_SUCCESSFUL) ||
  35. (ChangeDisplaySettings(NULL, 0) != DISP_CHANGE_SUCCESSFUL))
  36. {
  37. // we failed for some unknown reason
  38. bRet = FALSE;
  39. }
  40. // we found the driver, no need to look further
  41. break;
  42. }
  43. }
  44. if (i == 0)
  45. {
  46. // this means that EnumDisplayDevices failed, which we consider a
  47. // failure case
  48. bRet = FALSE;
  49. }
  50. return bRet;
  51. }
  52. //
  53. // Constructs the proper ""C:\windows\system32\rundll32.exe" nmasnt.dll,CleanupNetMeetingDispDriver 0"
  54. // commandline to put into the registry in case the machine is rebooted while netmeeting is
  55. // running.
  56. //
  57. BOOL GetCleanupCmdLine(LPTSTR pszCmdLine)
  58. {
  59. BOOL bRet = FALSE;
  60. TCHAR szWindir[MAX_PATH];
  61. if (GetSystemDirectory(szWindir, sizeof(szWindir)/sizeof(szWindir[0])))
  62. {
  63. wsprintf(pszCmdLine, TEXT("\"%s\\rundll32.exe\" msconf.dll,CleanupNetMeetingDispDriver 0"), szWindir);
  64. bRet = TRUE;
  65. }
  66. return bRet;
  67. }
  68. //
  69. // This will either add or remove ourself from the runonce section of the registry
  70. //
  71. BOOL SetCleanupInRunone(BOOL bAdd)
  72. {
  73. BOOL bRet = FALSE;
  74. TCHAR szCleanupCmdLine[MAX_PATH * 2];
  75. if (GetCleanupCmdLine(szCleanupCmdLine))
  76. {
  77. HKEY hk;
  78. // first try HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
  79. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  80. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
  81. 0,
  82. KEY_QUERY_VALUE | KEY_SET_VALUE,
  83. &hk) == ERROR_SUCCESS)
  84. {
  85. if (bAdd)
  86. {
  87. if (RegSetValueEx(hk,
  88. TEXT("!CleanupNetMeetingDispDriver"),
  89. 0,
  90. REG_SZ,
  91. (LPBYTE)szCleanupCmdLine,
  92. (lstrlen(szCleanupCmdLine) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
  93. {
  94. bRet = TRUE;
  95. }
  96. }
  97. else
  98. {
  99. if (RegDeleteValue(hk, TEXT("!CleanupNetMeetingDispDriver")) == ERROR_SUCCESS)
  100. {
  101. bRet = TRUE;
  102. }
  103. }
  104. RegCloseKey(hk);
  105. }
  106. if (!bRet)
  107. {
  108. // if we failed, then try HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
  109. if (RegCreateKeyEx(HKEY_CURRENT_USER,
  110. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
  111. 0,
  112. NULL,
  113. REG_OPTION_NON_VOLATILE,
  114. KEY_QUERY_VALUE | KEY_SET_VALUE,
  115. NULL,
  116. &hk,
  117. NULL) == ERROR_SUCCESS)
  118. {
  119. if (bAdd)
  120. {
  121. if (RegSetValueEx(hk,
  122. TEXT("!CleanupNetMeetingDispDriver"),
  123. 0,
  124. REG_SZ,
  125. (LPBYTE)szCleanupCmdLine,
  126. (lstrlen(szCleanupCmdLine) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
  127. {
  128. bRet = TRUE;
  129. }
  130. }
  131. else
  132. {
  133. if (RegDeleteValue(hk, TEXT("!CleanupNetMeetingDispDriver")) == ERROR_SUCCESS)
  134. {
  135. bRet = TRUE;
  136. }
  137. }
  138. RegCloseKey(hk);
  139. }
  140. }
  141. }
  142. return bRet;
  143. }
  144. // on retail builds we don't have CRT's so we need this
  145. #ifndef DBG
  146. int _wchartodigit(WCHAR ch)
  147. {
  148. #define DIGIT_RANGE_TEST(zero) \
  149. if (ch < zero) \
  150. return -1; \
  151. if (ch < zero + 10) \
  152. { \
  153. return ch - zero; \
  154. }
  155. DIGIT_RANGE_TEST(0x0030) // 0030;DIGIT ZERO
  156. if (ch < 0xFF10) // FF10;FULLWIDTH DIGIT ZERO
  157. {
  158. DIGIT_RANGE_TEST(0x0660) // 0660;ARABIC-INDIC DIGIT ZERO
  159. DIGIT_RANGE_TEST(0x06F0) // 06F0;EXTENDED ARABIC-INDIC DIGIT ZERO
  160. DIGIT_RANGE_TEST(0x0966) // 0966;DEVANAGARI DIGIT ZERO
  161. DIGIT_RANGE_TEST(0x09E6) // 09E6;BENGALI DIGIT ZERO
  162. DIGIT_RANGE_TEST(0x0A66) // 0A66;GURMUKHI DIGIT ZERO
  163. DIGIT_RANGE_TEST(0x0AE6) // 0AE6;GUJARATI DIGIT ZERO
  164. DIGIT_RANGE_TEST(0x0B66) // 0B66;ORIYA DIGIT ZERO
  165. DIGIT_RANGE_TEST(0x0C66) // 0C66;TELUGU DIGIT ZERO
  166. DIGIT_RANGE_TEST(0x0CE6) // 0CE6;KANNADA DIGIT ZERO
  167. DIGIT_RANGE_TEST(0x0D66) // 0D66;MALAYALAM DIGIT ZERO
  168. DIGIT_RANGE_TEST(0x0E50) // 0E50;THAI DIGIT ZERO
  169. DIGIT_RANGE_TEST(0x0ED0) // 0ED0;LAO DIGIT ZERO
  170. DIGIT_RANGE_TEST(0x0F20) // 0F20;TIBETAN DIGIT ZERO
  171. DIGIT_RANGE_TEST(0x1040) // 1040;MYANMAR DIGIT ZERO
  172. DIGIT_RANGE_TEST(0x17E0) // 17E0;KHMER DIGIT ZERO
  173. DIGIT_RANGE_TEST(0x1810) // 1810;MONGOLIAN DIGIT ZERO
  174. return -1;
  175. }
  176. #undef DIGIT_RANGE_TEST
  177. if (ch < 0xFF10 + 10)
  178. {
  179. return ch - 0xFF10;
  180. }
  181. return -1;
  182. }
  183. __int64 __cdecl _wtoi64(const WCHAR *nptr)
  184. {
  185. int c; /* current char */
  186. __int64 total; /* current total */
  187. int sign; /* if '-', then negative, otherwise positive */
  188. if (!nptr)
  189. return 0i64;
  190. c = (int)(WCHAR)*nptr++;
  191. total = 0;
  192. while ((c = _wchartodigit((WCHAR)c)) != -1)
  193. {
  194. total = 10 * total + c; /* accumulate digit */
  195. c = (WCHAR)*nptr++; /* get next char */
  196. }
  197. return total;
  198. }
  199. #endif //!DBG
  200. //
  201. // The purpose of this function is two-fold:
  202. //
  203. // 1. It is passed the decimal value of the NetMeeting process handle on the cmdline
  204. // so that it can wait for NetMeeting to terminate and make sure that the mnmdd
  205. // driver is disabled.
  206. //
  207. // 2. We also add ourselves to the runonce in case the machine is rebooted or bugchecks
  208. // while NetMeeting is running so we can disable the driver at next logon.
  209. //
  210. STDAPI_(void) CleanupNetMeetingDispDriverW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR pswszCmdLine, int nCmdShow)
  211. {
  212. HANDLE hNetMeetingProcess = NULL;
  213. HANDLE hEvent;
  214. BOOL bAddedToRunOnce = FALSE;
  215. BOOL bNetMeetingStillRunning = FALSE;
  216. // the handle of the process to watch is passed to us on the cmdline as a decimal string value
  217. if (pswszCmdLine && *pswszCmdLine)
  218. {
  219. hNetMeetingProcess = (HANDLE)_wtoi64(pswszCmdLine);
  220. }
  221. if (hNetMeetingProcess)
  222. {
  223. // add ourselves to the runonce in case the machine bugchecks or is rebooted, we can still disable the
  224. // mnmdd driver at next logon
  225. bAddedToRunOnce = SetCleanupInRunone(TRUE);
  226. for (;;)
  227. {
  228. MSG msg;
  229. DWORD dwReturn = MsgWaitForMultipleObjects(1, &hNetMeetingProcess, FALSE, INFINITE, QS_ALLINPUT);
  230. if (dwReturn != (WAIT_OBJECT_0 + 1))
  231. {
  232. // something other than a message (either our event is signaled or MsgWait failed)
  233. break;
  234. }
  235. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  236. {
  237. TranslateMessage(&msg);
  238. DispatchMessage(&msg);
  239. }
  240. }
  241. }
  242. else
  243. {
  244. // If hNetMeetingProcess is NULL, this means we are running as part of RunOnce due to a reboot
  245. // or a bugcheck. We have to signal the "msgina: ShellReadyEvent" early so that the desktop switch
  246. // will take place or else our call to ChangeDisplaySettingsEx will fail becuase the input desktop
  247. // will still be winlogon's secure desktop
  248. hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("msgina: ShellReadyEvent"));
  249. if (hEvent)
  250. {
  251. SetEvent(hEvent);
  252. CloseHandle(hEvent);
  253. }
  254. // we also wait 10 seconds just to be sure that the desktop switch has taken place
  255. Sleep(10 * 1000);
  256. }
  257. // Only disable the driver if conf.exe/mnmsrvc.exe is not running. We need this extra check since the service (mnmsrvc.exe)
  258. // can stop and start but conf.exe might still be running, and we don't want to disable the driver while the app
  259. // is still using it. Conversely, we dont want to detach the driver if mnmsrvc.exe is still running.
  260. hEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("CONF:Init"));
  261. if (hEvent)
  262. {
  263. bNetMeetingStillRunning = TRUE;
  264. CloseHandle(hEvent);
  265. }
  266. if (FindWindow(TEXT("MnmSrvcHiddenWindow"), NULL))
  267. {
  268. bNetMeetingStillRunning = TRUE;
  269. }
  270. if (bNetMeetingStillRunning)
  271. {
  272. // make sure we are in the runonce
  273. bAddedToRunOnce = SetCleanupInRunone(TRUE);
  274. }
  275. else
  276. {
  277. // this will detach the mnmdd driver from the hw, allowing DX games to run
  278. if (DisableNetMeetingDriver() && bAddedToRunOnce)
  279. {
  280. // remove ourselves from the runonce
  281. SetCleanupInRunone(FALSE);
  282. }
  283. }
  284. }