332 lines
10 KiB
C++
332 lines
10 KiB
C++
|
#include "precomp.h"
|
||
|
|
||
|
//
|
||
|
// watcher.cpp
|
||
|
//
|
||
|
// This file is a big ugly hack to make sure that NetMeeting
|
||
|
// will cleanup is display driver (nmndd.sys) properly. This is
|
||
|
// needed because it uses a mirrored driver which will prevent DX
|
||
|
// games from running if NetMeeting is not shut down cleanly.
|
||
|
//
|
||
|
// Copyright(c) Microsoft 1997-
|
||
|
//
|
||
|
|
||
|
|
||
|
//
|
||
|
// This function actually disables the "NetMeeting driver" display driver
|
||
|
//
|
||
|
BOOL DisableNetMeetingDriver()
|
||
|
{
|
||
|
BOOL bRet = TRUE; // assume success
|
||
|
DISPLAY_DEVICE dd = {0};
|
||
|
int i;
|
||
|
|
||
|
dd.cb = sizeof(dd);
|
||
|
|
||
|
for (i = 0; EnumDisplayDevices(NULL, i, &dd, 0); i++)
|
||
|
{
|
||
|
if ((dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
|
||
|
(dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
|
||
|
!lstrcmpi(dd.DeviceString, TEXT("NetMeeting driver")))
|
||
|
{
|
||
|
DEVMODE devmode = {0};
|
||
|
|
||
|
devmode.dmSize = sizeof(devmode);
|
||
|
devmode.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
|
||
|
|
||
|
if ((ChangeDisplaySettingsEx(dd.DeviceName,
|
||
|
&devmode,
|
||
|
NULL,
|
||
|
CDS_UPDATEREGISTRY | CDS_NORESET,
|
||
|
NULL) != DISP_CHANGE_SUCCESSFUL) ||
|
||
|
(ChangeDisplaySettings(NULL, 0) != DISP_CHANGE_SUCCESSFUL))
|
||
|
{
|
||
|
// we failed for some unknown reason
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
|
||
|
// we found the driver, no need to look further
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == 0)
|
||
|
{
|
||
|
// this means that EnumDisplayDevices failed, which we consider a
|
||
|
// failure case
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Constructs the proper ""C:\windows\system32\rundll32.exe" nmasnt.dll,CleanupNetMeetingDispDriver 0"
|
||
|
// commandline to put into the registry in case the machine is rebooted while netmeeting is
|
||
|
// running.
|
||
|
//
|
||
|
BOOL GetCleanupCmdLine(LPTSTR pszCmdLine)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
TCHAR szWindir[MAX_PATH];
|
||
|
|
||
|
if (GetSystemDirectory(szWindir, sizeof(szWindir)/sizeof(szWindir[0])))
|
||
|
{
|
||
|
wsprintf(pszCmdLine, TEXT("\"%s\\rundll32.exe\" msconf.dll,CleanupNetMeetingDispDriver 0"), szWindir);
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// This will either add or remove ourself from the runonce section of the registry
|
||
|
//
|
||
|
BOOL SetCleanupInRunone(BOOL bAdd)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
TCHAR szCleanupCmdLine[MAX_PATH * 2];
|
||
|
|
||
|
if (GetCleanupCmdLine(szCleanupCmdLine))
|
||
|
{
|
||
|
HKEY hk;
|
||
|
|
||
|
// first try HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
|
||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
|
||
|
0,
|
||
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
||
|
&hk) == ERROR_SUCCESS)
|
||
|
{
|
||
|
if (bAdd)
|
||
|
{
|
||
|
if (RegSetValueEx(hk,
|
||
|
TEXT("!CleanupNetMeetingDispDriver"),
|
||
|
0,
|
||
|
REG_SZ,
|
||
|
(LPBYTE)szCleanupCmdLine,
|
||
|
(lstrlen(szCleanupCmdLine) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (RegDeleteValue(hk, TEXT("!CleanupNetMeetingDispDriver")) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hk);
|
||
|
}
|
||
|
|
||
|
if (!bRet)
|
||
|
{
|
||
|
// if we failed, then try HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
|
||
|
if (RegCreateKeyEx(HKEY_CURRENT_USER,
|
||
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
|
||
|
0,
|
||
|
NULL,
|
||
|
REG_OPTION_NON_VOLATILE,
|
||
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
||
|
NULL,
|
||
|
&hk,
|
||
|
NULL) == ERROR_SUCCESS)
|
||
|
{
|
||
|
if (bAdd)
|
||
|
{
|
||
|
if (RegSetValueEx(hk,
|
||
|
TEXT("!CleanupNetMeetingDispDriver"),
|
||
|
0,
|
||
|
REG_SZ,
|
||
|
(LPBYTE)szCleanupCmdLine,
|
||
|
(lstrlen(szCleanupCmdLine) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (RegDeleteValue(hk, TEXT("!CleanupNetMeetingDispDriver")) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hk);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
// on retail builds we don't have CRT's so we need this
|
||
|
#ifndef DBG
|
||
|
|
||
|
int _wchartodigit(WCHAR ch)
|
||
|
{
|
||
|
#define DIGIT_RANGE_TEST(zero) \
|
||
|
if (ch < zero) \
|
||
|
return -1; \
|
||
|
if (ch < zero + 10) \
|
||
|
{ \
|
||
|
return ch - zero; \
|
||
|
}
|
||
|
|
||
|
DIGIT_RANGE_TEST(0x0030) // 0030;DIGIT ZERO
|
||
|
if (ch < 0xFF10) // FF10;FULLWIDTH DIGIT ZERO
|
||
|
{
|
||
|
DIGIT_RANGE_TEST(0x0660) // 0660;ARABIC-INDIC DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x06F0) // 06F0;EXTENDED ARABIC-INDIC DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0966) // 0966;DEVANAGARI DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x09E6) // 09E6;BENGALI DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0A66) // 0A66;GURMUKHI DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0AE6) // 0AE6;GUJARATI DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0B66) // 0B66;ORIYA DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0C66) // 0C66;TELUGU DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0CE6) // 0CE6;KANNADA DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0D66) // 0D66;MALAYALAM DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0E50) // 0E50;THAI DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0ED0) // 0ED0;LAO DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x0F20) // 0F20;TIBETAN DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x1040) // 1040;MYANMAR DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x17E0) // 17E0;KHMER DIGIT ZERO
|
||
|
DIGIT_RANGE_TEST(0x1810) // 1810;MONGOLIAN DIGIT ZERO
|
||
|
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
#undef DIGIT_RANGE_TEST
|
||
|
|
||
|
if (ch < 0xFF10 + 10)
|
||
|
{
|
||
|
return ch - 0xFF10;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
__int64 __cdecl _wtoi64(const WCHAR *nptr)
|
||
|
{
|
||
|
int c; /* current char */
|
||
|
__int64 total; /* current total */
|
||
|
int sign; /* if '-', then negative, otherwise positive */
|
||
|
|
||
|
if (!nptr)
|
||
|
return 0i64;
|
||
|
|
||
|
c = (int)(WCHAR)*nptr++;
|
||
|
|
||
|
total = 0;
|
||
|
|
||
|
while ((c = _wchartodigit((WCHAR)c)) != -1)
|
||
|
{
|
||
|
total = 10 * total + c; /* accumulate digit */
|
||
|
c = (WCHAR)*nptr++; /* get next char */
|
||
|
}
|
||
|
|
||
|
return total;
|
||
|
}
|
||
|
#endif //!DBG
|
||
|
|
||
|
|
||
|
//
|
||
|
// The purpose of this function is two-fold:
|
||
|
//
|
||
|
// 1. It is passed the decimal value of the NetMeeting process handle on the cmdline
|
||
|
// so that it can wait for NetMeeting to terminate and make sure that the mnmdd
|
||
|
// driver is disabled.
|
||
|
//
|
||
|
// 2. We also add ourselves to the runonce in case the machine is rebooted or bugchecks
|
||
|
// while NetMeeting is running so we can disable the driver at next logon.
|
||
|
//
|
||
|
STDAPI_(void) CleanupNetMeetingDispDriverW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR pswszCmdLine, int nCmdShow)
|
||
|
{
|
||
|
HANDLE hNetMeetingProcess = NULL;
|
||
|
HANDLE hEvent;
|
||
|
BOOL bAddedToRunOnce = FALSE;
|
||
|
BOOL bNetMeetingStillRunning = FALSE;
|
||
|
|
||
|
// the handle of the process to watch is passed to us on the cmdline as a decimal string value
|
||
|
if (pswszCmdLine && *pswszCmdLine)
|
||
|
{
|
||
|
hNetMeetingProcess = (HANDLE)_wtoi64(pswszCmdLine);
|
||
|
}
|
||
|
|
||
|
if (hNetMeetingProcess)
|
||
|
{
|
||
|
// add ourselves to the runonce in case the machine bugchecks or is rebooted, we can still disable the
|
||
|
// mnmdd driver at next logon
|
||
|
bAddedToRunOnce = SetCleanupInRunone(TRUE);
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
MSG msg;
|
||
|
DWORD dwReturn = MsgWaitForMultipleObjects(1, &hNetMeetingProcess, FALSE, INFINITE, QS_ALLINPUT);
|
||
|
|
||
|
if (dwReturn != (WAIT_OBJECT_0 + 1))
|
||
|
{
|
||
|
// something other than a message (either our event is signaled or MsgWait failed)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||
|
{
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If hNetMeetingProcess is NULL, this means we are running as part of RunOnce due to a reboot
|
||
|
// or a bugcheck. We have to signal the "msgina: ShellReadyEvent" early so that the desktop switch
|
||
|
// will take place or else our call to ChangeDisplaySettingsEx will fail becuase the input desktop
|
||
|
// will still be winlogon's secure desktop
|
||
|
hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("msgina: ShellReadyEvent"));
|
||
|
if (hEvent)
|
||
|
{
|
||
|
SetEvent(hEvent);
|
||
|
CloseHandle(hEvent);
|
||
|
}
|
||
|
|
||
|
// we also wait 10 seconds just to be sure that the desktop switch has taken place
|
||
|
Sleep(10 * 1000);
|
||
|
}
|
||
|
|
||
|
// Only disable the driver if conf.exe/mnmsrvc.exe is not running. We need this extra check since the service (mnmsrvc.exe)
|
||
|
// can stop and start but conf.exe might still be running, and we don't want to disable the driver while the app
|
||
|
// is still using it. Conversely, we dont want to detach the driver if mnmsrvc.exe is still running.
|
||
|
hEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("CONF:Init"));
|
||
|
if (hEvent)
|
||
|
{
|
||
|
bNetMeetingStillRunning = TRUE;
|
||
|
CloseHandle(hEvent);
|
||
|
}
|
||
|
|
||
|
if (FindWindow(TEXT("MnmSrvcHiddenWindow"), NULL))
|
||
|
{
|
||
|
bNetMeetingStillRunning = TRUE;
|
||
|
}
|
||
|
|
||
|
if (bNetMeetingStillRunning)
|
||
|
{
|
||
|
// make sure we are in the runonce
|
||
|
bAddedToRunOnce = SetCleanupInRunone(TRUE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// this will detach the mnmdd driver from the hw, allowing DX games to run
|
||
|
if (DisableNetMeetingDriver() && bAddedToRunOnce)
|
||
|
{
|
||
|
// remove ourselves from the runonce
|
||
|
SetCleanupInRunone(FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|