/*++ Copyright (C) Microsoft Corporation Module Name: globals.cpp Abstract: This module implements global functions needed for the program. It also contain global variables/classes. Author: William Hsieh (williamh) created Revision History: --*/ #include "devmgr.h" #include #define NO_SHELL_TREE_TYPE #include // // global classes and variables // // this, of course, our dll's instance handle. HINSTANCE g_hInstance = NULL; // // A CMachineList is created for each instance of DLL. It is shared // by all the CComponentData the instance might create. The class CMachine // contains all the information about all the classes and devices on the // machine. Each CComponent should register itself to CMachine. This way, // A CComponent will get notification whenever there are changes in // the CMachine(Refresh, Property changes on a device, for example). // We do not rely on MMC's view notification(UpdatAllView) because // it only reaches all the CComponents created by a CComponenetData. // CMachineList g_MachineList; CMemoryException g_MemoryException(TRUE); String g_strStartupMachineName; String g_strStartupDeviceId; String g_strStartupCommand; String g_strDevMgr; BOOL g_IsAdmin = FALSE; CPrintDialog g_PrintDlg; // // UUID consts // const CLSID CLSID_DEVMGR = {0x74246BFC,0x4C96,0x11D0,{0xAB,0xEF,0x00,0x20,0xAF,0x6B,0x0B,0x7A}}; const CLSID CLSID_DEVMGR_EXTENSION = {0x90087284,0xd6d6,0x11d0,{0x83,0x53,0x00,0xa0,0xc9,0x06,0x40,0xbf}}; const CLSID CLSID_SYSTOOLS = {0x476e6448,0xaaff,0x11d0,{0xb9,0x44,0x00,0xc0,0x4f,0xd8,0xd5,0xb0}}; const CLSID CLSID_DEVMGR_ABOUT = {0x94abaf2a,0x892a,0x11d1,{0xbb,0xc4,0x00,0xa0,0xc9,0x06,0x40,0xbf}}; const TCHAR* const CLSID_STRING_DEVMGR = TEXT("{74246bfc-4c96-11d0-abef-0020af6b0b7a}"); const TCHAR* const CLSID_STRING_DEVMGR_EXTENSION = TEXT("{90087284-d6d6-11d0-8353-00a0c90640bf}"); const TCHAR* const CLSID_STRING_SYSTOOLS = TEXT("{476e6448-aaff-11d0-b944-00c04fd8d5b0}"); const TCHAR* const CLSID_STRING_DEVMGR_ABOUT = TEXT("{94abaf2a-892a-11d1-bbc4-00a0c90640bf}"); // // ProgID // const TCHAR* const PROGID_DEVMGR = TEXT("DevMgrSnapin.DevMgrSnapin.1"); const TCHAR* const PROGID_DEVMGREXT = TEXT("DevMgrExtension.DevMgrExtension.1"); const TCHAR* const PROGID_DEVMGR_ABOUT = TEXT("DevMgrAbout.DevMgrAbout.1"); // // Node types const // const NODEINFO NodeInfo[TOTAL_COOKIE_TYPES] = { { COOKIE_TYPE_SCOPEITEM_DEVMGR, IDS_NAME_DEVMGR, IDS_DISPLAYNAME_SCOPE_DEVMGR, {0xc41dfb2a,0x4d5b,0x11d0,{0xab,0xef,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{c41dfb2a-4d5b-11d0-abef-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_RESOURCE_IRQ, IDS_NAME_IRQ, 0, {0x494535fe,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{494535fe-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_RESOURCE_DMA, IDS_NAME_DMA, 0, {0x49f0df4e,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{49f0df4e-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_RESOURCE_IO, IDS_NAME_IO, 0, {0xa2958d7a,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{a2958d7a-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_RESOURCE_MEMORY, IDS_NAME_MEMORY, 0, {0xa2958d7b,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{a2958d7b-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_COMPUTER, IDS_NAME_COMPUTER, 0, {0xa2958d7c,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{a2958d7c-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_DEVICE, IDS_NAME_DEVICE, 0, {0xa2958d7d,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{a2958d7d-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_CLASS, IDS_NAME_CLASS, 0, {0xe677e204,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{e677e204-5aa2-11d0-abf0-0020af6b0b7a}") }, { COOKIE_TYPE_RESULTITEM_RESTYPE, IDS_NAME_RESOURCES, 0, {0xa2958d7e,0x5aa2,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}, TEXT("{a2958d7e-5aa2-11d0-abf0-0020af6b0b7a}") } }; const IID IID_IDMTVOCX = \ {0x142525f2,0x59d8,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}; const IID IID_ISnapinCallback = \ {0x8e0ba98a,0xd161,0x11d0,{0x83,0x53,0x00,0xa0,0xc9,0x06,0x40,0xbf}}; // // cliboard format strings // const TCHAR* const MMC_SNAPIN_MACHINE_NAME = TEXT("MMC_SNAPIN_MACHINE_NAME"); const TCHAR* const SNAPIN_INTERNAL = TEXT("SNAPIN_INTERNAL"); const TCHAR* const DEVMGR_SNAPIN_CLASS_GUID = TEXT("DEVMGR_SNAPIN_CLASS_GUID"); const TCHAR* const DEVMGR_SNAPIN_DEVICE_ID = TEXT("DEVMGR_SNAPIN_DEVICE_ID"); const TCHAR* const DEVMGR_COMMAND_PROPERTY = TEXT("Property"); const TCHAR* const REG_PATH_DEVICE_MANAGER = TEXT("SOFTWARE\\Microsoft\\DeviceManager"); const TCHAR* const REG_STR_BUS_TYPES = TEXT("BusTypes"); const TCHAR* const REG_STR_TROUBLESHOOTERS = TEXT("TroubleShooters"); const TCHAR* const DEVMGR_HELP_FILE_NAME = TEXT("devmgr.hlp"); const TCHAR* const DEVMGR_HTML_HELP_FILE_NAME = TEXT("\\help\\devmgr.chm"); // lookup table to translate problem number to its text resource id. const PROBLEMINFO g_ProblemInfo[] = { {IDS_PROB_NOPROBLEM, 0}, // NO PROBLEM {IDS_PROB_NOT_CONFIGURED, PIF_CODE_EMBEDDED}, // CM_PROB_NOT_CONFIGURED {IDS_PROB_DEVLOADERFAILED, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_FAILED {IDS_PROB_OUT_OF_MEMORY, PIF_CODE_EMBEDDED}, // CM_PROB_OUT_OF_MEMORY {IDS_PROB_WRONG_TYPE, PIF_CODE_EMBEDDED}, // CM_PROB_ENTRY_IS_WRONG_TYPE {IDS_PROB_LACKEDARBITRATOR, PIF_CODE_EMBEDDED}, // CM_PROB_LACKED_ARBITRATOR {IDS_PROB_BOOT_CONFIG_CONFLICT, PIF_CODE_EMBEDDED}, // CM_PROB_BOOT_CONFIG_CONFLICT {IDS_PROB_FAILED_FILTER, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_FILTER {IDS_PROB_DEVLOADER_NOT_FOUND, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_NOT_FOUND {IDS_PROB_INVALID_DATA, PIF_CODE_EMBEDDED}, // CM_PROB_INVALID_DATA {IDS_PROB_FAILED_START, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_START {IDS_PROB_LIAR, PIF_CODE_EMBEDDED}, // CM_PROB_LIAR {IDS_PROB_NORMAL_CONFLICT, PIF_CODE_EMBEDDED}, // CM_PROB_NORMAL_CONFLICT {IDS_PROB_NOT_VERIFIED, PIF_CODE_EMBEDDED}, // CM_PROB_NOT_VERIFIED {IDS_PROB_NEEDRESTART, PIF_CODE_EMBEDDED}, // CM_PROB_NEED_RESTART {IDS_PROB_REENUMERATION, PIF_CODE_EMBEDDED}, // CM_PROB_REENUMERATION {IDS_PROB_PARTIALCONFIG, PIF_CODE_EMBEDDED}, // CM_PROB_PARTIAL_LOG_CONF {IDS_PROB_UNKNOWN_RESOURCE, PIF_CODE_EMBEDDED}, // CM_PROB_UNKNOWN_RESOURCE {IDS_PROB_REINSTALL, PIF_CODE_EMBEDDED}, // CM_PROB_REINSTALL {IDS_PROB_REGISTRY, PIF_CODE_EMBEDDED}, // CM_PROB_REGISTRY {IDS_PROB_SYSTEMFAILURE, PIF_CODE_EMBEDDED}, // CM_PROB_VXDLDR {IDS_PROB_WILL_BE_REMOVED, PIF_CODE_EMBEDDED}, // CM_PROB_WILL_BE_REMOVED {IDS_PROB_DISABLED, PIF_CODE_EMBEDDED}, // CM_PROB_DISABLED {IDS_PROB_SYSTEMFAILURE, PIF_CODE_EMBEDDED}, // CM_PROB_DEVLOADER_NOT_READY {IDS_DEVICE_NOT_THERE, PIF_CODE_EMBEDDED}, // CM_PROB_DEVICE_NOT_THERE {IDS_PROB_MOVED, PIF_CODE_EMBEDDED}, // CM_PROB_MOVED {IDS_PROB_TOO_EARLY, PIF_CODE_EMBEDDED}, // CM_PROB_TOO_EARLY {IDS_PROB_NO_VALID_LOG_CONF, PIF_CODE_EMBEDDED}, // CM_PROB_NO_VALID_LOG_CONF {IDS_PROB_FAILEDINSTALL, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_INSTALL {IDS_PROB_HARDWAREDISABLED, PIF_CODE_EMBEDDED}, // CM_PROB_HARDWARE_DISABLED {IDS_PROB_CANT_SHARE_IRQ, PIF_CODE_EMBEDDED}, // CM_PROB_CANT_SHARE_IRQ {IDS_PROB_FAILED_ADD, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_ADD {IDS_PROB_DISABLED_SERVICE, PIF_CODE_EMBEDDED}, // CM_PROB_DISABLED_SERVICE {IDS_PROB_TRANSLATION_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_TRANSLATION_FAILED {IDS_PROB_NO_SOFTCONFIG, PIF_CODE_EMBEDDED}, // CM_PROB_NO_SOFTCONFIG {IDS_PROB_BIOS_TABLE, PIF_CODE_EMBEDDED}, // CM_PROB_BIOS_TABLE {IDS_PROB_IRQ_TRANSLATION_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_IRQ_TRANSLATION_FAILED {IDS_PROB_FAILED_DRIVER_ENTRY, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_DRIVER_ENTRY {IDS_PROB_DRIVER_FAILED_PRIOR_UNLOAD, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD {IDS_PROB_DRIVER_FAILED_LOAD, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_FAILED_LOAD {IDS_PROB_DRIVER_SERVICE_KEY_INVALID, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_SERVICE_KEY_INVALID {IDS_PROB_LEGACY_SERVICE_NO_DEVICES, PIF_CODE_EMBEDDED}, // CM_PROB_LEGACY_SERVICE_NO_DEVICES {IDS_PROB_DUPLICATE_DEVICE, PIF_CODE_EMBEDDED}, // CM_PROB_DUPLICATE_DEVICE {IDS_PROB_FAILED_POST_START, PIF_CODE_EMBEDDED}, // CM_PROB_FAILED_POST_START {IDS_PROB_HALTED, PIF_CODE_EMBEDDED}, // CM_PROB_HALTED {IDS_PROB_PHANTOM, PIF_CODE_EMBEDDED}, // CM_PROB_PHANTOM {IDS_PROB_SYSTEM_SHUTDOWN, PIF_CODE_EMBEDDED}, // CM_PROB_SYSTEM_SHUTDOWN {IDS_PROB_HELD_FOR_EJECT, PIF_CODE_EMBEDDED}, // CM_PROB_HELD_FOR_EJECT {IDS_PROB_DRIVER_BLOCKED, PIF_CODE_EMBEDDED}, // CM_PROB_DRIVER_BLOCKED {IDS_PROB_REGISTRY_TOO_LARGE, PIF_CODE_EMBEDDED}, // CM_PROB_REGISTRY_TOO_LARGE {IDS_PROB_SETPROPERTIES_FAILED, PIF_CODE_EMBEDDED}, // CM_PROB_SETPROPERTIES_FAILED {IDS_PROB_UNKNOWN_WITHCODE, PIF_CODE_EMBEDDED} // UNKNOWN PROBLEM }; // // Global functions // #if DBG // // Debugging aids // void Trace( LPCTSTR format, ... ) { // according to wsprintf specification, the max buffer size is // 1024 TCHAR Buffer[1024]; va_list arglist; va_start(arglist, format); StringCchVPrintf(Buffer, ARRAYLEN(Buffer), format, arglist); va_end(arglist); OutputDebugString(TEXT("DEVMGR: ")); OutputDebugString(Buffer); OutputDebugString(TEXT("\r\n")); } #endif inline BOOL IsBlankChar(TCHAR ch) { return (_T(' ') == ch || _T('\t') == ch); } inline LPTSTR SkipBlankChars( LPTSTR psz ) { while (IsBlankChar(*psz)) psz++; return psz; } // // This function converts a given string to GUID // INPUT: // GuidString -- the null terminated guid string // LPGUID -- buffer to receive the GUID // OUTPUT: // TRUE if the conversion succeeded. // FALSE if failed. // inline BOOL GuidFromString( LPCTSTR GuidString, LPGUID pGuid ) { return ERROR_SUCCESS == pSetupGuidFromString(GuidString, pGuid); } // This function converts the given GUID to a string // INPUT: // pGuid -- the guid // Buffer -- the buffer to receive the string // BufferLen -- the buffer size in char unit // OUTPUT: // TRUE if the conversion succeeded. // FLASE if failed. // // inline BOOL GuidToString( LPGUID pGuid, LPTSTR Buffer, DWORD BufferLen ) { return ERROR_SUCCESS == pSetupStringFromGuid(pGuid, Buffer, BufferLen); } // // This function allocates an OLE string from OLE task memory pool // It does necessary char set conversion before copying the string. // // INPUT: LPCTSTR str -- the initial string // OUTPUT: LPOLESTR -- the result OLE string. NULL if the function fails. // LPOLESTR AllocOleTaskString( LPCTSTR str ) { if (!str) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } size_t Len = lstrlen(str); // allocate the null terminate char also. LPOLESTR olestr = (LPOLESTR)::CoTaskMemAlloc((Len + 1) * sizeof(TCHAR)); if (olestr) { StringCchCopy((LPTSTR)olestr, Len + 1, str); return olestr; } return NULL; } inline void FreeOleTaskString( LPOLESTR olestr ) { if (olestr) { ::CoTaskMemFree(olestr); } } // // This function addes the given menu item to snapin // INPUT: // iNameStringId -- menu item text resource id // iStatusBarStringId -- status bar text resource id. // iCommandId -- command id to be assigned to the menu item // InsertionPointId -- Insertion point id // Flags -- flags // SpecialFlags -- special flags // OUTPUT: // HRESULT // HRESULT AddMenuItem( LPCONTEXTMENUCALLBACK pCallback, int iNameStringId, int iStatusBarStringId, long lCommandId, long InsertionPointId, long Flags, long SpecialFlags ) { ASSERT(pCallback); CONTEXTMENUITEM tCMI; memset(&tCMI, 0, sizeof(tCMI)); tCMI.lCommandID = lCommandId; tCMI.lInsertionPointID = InsertionPointId; tCMI.fFlags = Flags; tCMI.fSpecialFlags = SpecialFlags; TCHAR Name[MAX_PATH]; TCHAR Status[MAX_PATH]; if (::LoadString(g_hInstance, iNameStringId, Name, ARRAYLEN(Name)) != 0) { tCMI.strName = Name; } if (iStatusBarStringId && (::LoadString(g_hInstance, iStatusBarStringId, Status, ARRAYLEN(Status)) != 0)) { tCMI.strStatusBarText = Status; } return pCallback->AddItem(&tCMI); } // // This function verifies the given machine name can be accessed remotely. // INPUT: // MachineName -- the machine name. The machine name must be // led by "\\\\". // OUTPUT: // BOOL TRUE for success and FALSE for failure. Check GetLastError() for failure // reason. // BOOL VerifyMachineName( LPCTSTR MachineName ) { CONFIGRET cr = CR_SUCCESS; HMACHINE hMachine = NULL; HKEY hRemoteKey = NULL; HKEY hClass = NULL; String m_strMachineFullName; if (MachineName && (_T('\0') != MachineName[0])) { if (_T('\\') == MachineName[0] && _T('\\') == MachineName[1]) { m_strMachineFullName = MachineName; } else { m_strMachineFullName = TEXT("\\\\"); m_strMachineFullName+=MachineName; } // // make sure we can connect the machine using cfgmgr32. // cr = CM_Connect_Machine((LPTSTR)m_strMachineFullName, &hMachine); // // We could not connect to the machine using cfgmgr32 // if (CR_SUCCESS != cr) { goto clean0; } // // make sure we can connect to the registry of the remote machine // if (RegConnectRegistry((LPTSTR)m_strMachineFullName, HKEY_LOCAL_MACHINE, &hRemoteKey) != ERROR_SUCCESS) { cr = CR_REGISTRY_ERROR; goto clean0; } cr = CM_Open_Class_Key_Ex(NULL, NULL, KEY_READ, RegDisposition_OpenExisting, &hClass, CM_OPEN_CLASS_KEY_INSTALLER, hMachine ); } clean0: if (hMachine) { CM_Disconnect_Machine(hMachine); } if (hRemoteKey) { RegCloseKey(hRemoteKey); } if (hClass) { RegCloseKey(hClass); } // // We will basically set two different error codes for this API, since we need // to present this information to the user. // 1) ERROR_MACHINE_UNABAILABLE // 2) ERROR_ACCESS_DENIED. // if (CR_SUCCESS == cr) { SetLastError(NO_ERROR); } else if (CR_MACHINE_UNAVAILABLE == cr) { SetLastError(ERROR_MACHINE_UNAVAILABLE); } else { SetLastError(ERROR_ACCESS_DENIED); } return (CR_SUCCESS == cr); } // This function loads the string designated by the given // string id(resource id) from the module's resource to the provided // buffer. It returns the required buffer size(in chars) to hold the string, // not including the terminated NULL chars. Last error will be set // appropaitely. // // input: int StringId -- the resource id of the string to be loaded. // LPTSTR Buffer -- provided buffer to receive the string // UINT BufferSize -- the size of Buffer in chars // output: // UINT the required buffer size to receive the string // if it returns 0, GetLastError() returns the error code. // UINT LoadResourceString( int StringId, LPTSTR Buffer, UINT BufferSize ) { // do some trivial tests. if (BufferSize && !Buffer) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } // if caller provides buffer, try to load the string with the given buffer // and length. UINT FinalLen; if (Buffer) { FinalLen = ::LoadString(g_hInstance, StringId, Buffer, BufferSize); if (BufferSize > FinalLen) { return FinalLen; } } // Either the caller does not provide the buffer or the given buffer // is too small. Try to figure out the requried size. // // first use a stack-based buffer to get the string. If the buffer // is big enough, we are happy. TCHAR Temp[256]; UINT ArrayLen = ARRAYLEN(Temp); FinalLen = ::LoadString(g_hInstance, StringId, Temp, ArrayLen); DWORD LastError = ERROR_SUCCESS; if (ArrayLen <= FinalLen) { // the stack-based buffer is too small, use heap-based buffer. // we have not got all the chars. we increase the buffer size of 256 // chars each time it fails. The initial size is 512(256+256) // the max size is 32K ArrayLen = 256; TCHAR* HeapBuffer; FinalLen = 0; while (ArrayLen < 0x8000) { ArrayLen += 256; HeapBuffer = new TCHAR[ArrayLen]; if (HeapBuffer) { FinalLen = ::LoadString(g_hInstance, StringId, HeapBuffer, ArrayLen); delete [] HeapBuffer; if (FinalLen < ArrayLen) break; } else { LastError = ERROR_NOT_ENOUGH_MEMORY; break; } } if (ERROR_SUCCESS != LastError) { SetLastError(LastError); FinalLen = 0; } } return FinalLen; } // This function get the problem text designated by the given problem number // for the given devnode on the given machine. // // // input: // ULONG ProblemNumber -- the problem number // LPTSTR Buffer -- provided buffer to receive the string // UINT BufferSize -- the size of Buffer in chars // output: // UINT the required buffer size to receive the string // if it returns 0, GetLastError() returns the error code. // UINT GetDeviceProblemText( ULONG ProblemNumber, LPTSTR Buffer, UINT BufferSize ) { // // first does a trivial test // if (!Buffer && BufferSize) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } String strMainText; UINT RequiredSize = 0; PROBLEMINFO pi; // // Get the PROBLEMINFO for the problem number // pi = g_ProblemInfo[min(ProblemNumber, DEVMGR_NUM_CM_PROB-1)]; try { String strProblemDesc; strProblemDesc.LoadString(g_hInstance, pi.StringId); if (pi.Flags & PIF_CODE_EMBEDDED) { String strFormat; strFormat.LoadString(g_hInstance, IDS_PROB_CODE); String strCodeText; strCodeText.Format((LPTSTR)strFormat, ProblemNumber); strMainText.Format((LPTSTR)strProblemDesc, (LPTSTR)strCodeText); } else { strMainText = strProblemDesc; } RequiredSize = strMainText.GetLength() + 1; // // copy the main text // if (RequiredSize && (BufferSize > RequiredSize)) { StringCchCopy(Buffer, BufferSize, (LPTSTR)strMainText); } } catch (CMemoryException* e) { e->Delete(); SetLastError(ERROR_NOT_ENOUGH_MEMORY); RequiredSize = 0; } return RequiredSize; } // // This function creates and shows a message box // INPUT: // hwndParent -- the window handle servers as the parent window to the // message box // MsgId -- string id for message box body. The string can be // a format string. // CaptionId -- string id for caption. if 0, default is device manager // Type -- the standard message box flags(MB_xxxx) // ... -- parameters to MsgId string if it contains any // format chars. //OUTPUT: // return value from MessageBox(IDOK, IDYES...) int MsgBoxParam( HWND hwndParent, int MsgId, int CaptionId, DWORD Type, ... ) { TCHAR szMsg[MAX_PATH * 4], szCaption[MAX_PATH];; LPCTSTR pCaption; va_list parg; int Result; // if no MsgId is given, it is for no memory error; if (MsgId) { va_start(parg, Type); // load the msg string to szCaption(temp). The text may contain // format information if (!::LoadString(g_hInstance, MsgId, szCaption, ARRAYLEN(szCaption))) { goto NoMemory; } //finish up format string StringCchVPrintf(szMsg, ARRAYLEN(szMsg), szCaption, parg); // if caption id is given, load it. if (CaptionId) { if (!::LoadString(g_hInstance, CaptionId, szCaption, ARRAYLEN(szCaption))) { goto NoMemory; } pCaption = szCaption; } else { pCaption = g_strDevMgr; } if ((Result = MessageBox(hwndParent, szMsg, pCaption, Type)) == 0) { goto NoMemory; } return Result; } NoMemory: g_MemoryException.ReportError(hwndParent); return 0; } // This functin prompts for restart. // INPUT: // hwndParent -- the window handle to be used as the parent window // to the restart dialog // RestartFlags -- flags(RESTART/REBOOT/POWERRECYCLE) // ResId -- designated string resource id. If 0, default will // be used. // OUTPUT: // ID returned from the MessageBox. IDYES if the user said Yes to the restart // dialog and IDNO if they said NO. INT PromptForRestart( HWND hwndParent, DWORD RestartFlags, int ResId ) { INT id = 0; if (RestartFlags & (DI_NEEDRESTART | DI_NEEDREBOOT)) { DWORD ExitWinCode = 0; try { String str; if (RestartFlags & DI_NEEDRESTART) { if (!ResId) { ResId = IDS_DEVCHANGE_RESTART; } str.LoadString(g_hInstance, ResId); ExitWinCode = EWX_REBOOT; } else { if (!ResId && RestartFlags & DI_NEEDPOWERCYCLE) { String str2; str.LoadString(g_hInstance, IDS_POWERCYC1); str2.LoadString(g_hInstance, IDS_POWERCYC2); str += str2; ExitWinCode = EWX_SHUTDOWN; } else { if (!ResId) { ResId = IDS_DEVCHANGE_RESTART; } str.LoadString(g_hInstance, ResId); ExitWinCode = EWX_REBOOT; } } if (ExitWinCode != 0) { id = RestartDialogEx(hwndParent, str, ExitWinCode, REASON_PLANNED_FLAG | REASON_HWINSTALL ); } } catch(CMemoryException* e) { e->Delete(); MsgBoxParam(hwndParent, 0, 0, 0); } } return id; } BOOL LoadEnumPropPage32( LPCTSTR RegString, HMODULE* pDll, FARPROC* pProcAddress ) { // verify parameters if (!RegString || _T('\0') == RegString[0] || !pDll || !pProcAddress) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // make a copy of the string because we have to party on it ULONG Len = lstrlen(RegString) + 1; TCHAR* psz = new TCHAR[Len]; if (!psz) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } StringCchCopy(psz, Len, RegString); LPTSTR DllName = NULL; LPTSTR DllNameEnd = NULL; LPTSTR FunctionName = NULL; LPTSTR FunctionNameEnd = NULL; LPTSTR p; p = psz; SetLastError(ERROR_SUCCESS); // the format of the string is "dllname, dllentryname" p = SkipBlankChars(p); if (_T('\0') != *p) { // looking for dllname which could be enclosed // inside double quote chars. // NOTE: not double quote chars inside double quoted string is allowed. if (_T('\"') == *p) { DllName = ++p; while (_T('\"') != *p && _T('\0') != *p) p++; DllNameEnd = p; if (_T('\"') == *p) p++; } else { DllName = p; while (!IsBlankChar(*p) && _T(',') != *p) p++; DllNameEnd = p; } // looking for ',' p = SkipBlankChars(p); if (_T('\0') != *p && _T(',') == *p) { p = SkipBlankChars(p + 1); if (_T('\0') != *p) { FunctionName = p++; while (!IsBlankChar(*p) && _T('\0') != *p) p++; FunctionNameEnd = p; } } } if (DllName && FunctionName) { if (DllNameEnd) { *DllNameEnd = _T('\0'); } if (FunctionNameEnd) { *FunctionNameEnd = _T('\0'); } *pDll = LoadLibrary(DllName); if (*pDll) { // convert Wide char to ANSI which is GetProcAddress expected. // We do not append a 'A" or a "W' here. CHAR FuncNameA[256]; WideCharToMultiByte(CP_ACP, 0, FunctionName, (int)wcslen(FunctionName) + 1, FuncNameA, sizeof(FuncNameA), NULL, NULL); *pProcAddress = GetProcAddress(*pDll, FuncNameA); } } delete [] psz; if (!*pProcAddress && *pDll) FreeLibrary(*pDll); return (*pDll && *pProcAddress); } BOOL AddPropPageCallback( HPROPSHEETPAGE hPage, LPARAM lParam ) { CPropSheetData* ppsData = (CPropSheetData*)lParam; ASSERT(ppsData); return ppsData->InsertPage(hPage); } BOOL AddToolTips( HWND hDlg, UINT id, LPCTSTR pszText, HWND *phwnd ) { if (*phwnd == NULL) { *phwnd = CreateWindow(TOOLTIPS_CLASS, TEXT(""), WS_POPUP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hDlg, NULL, g_hInstance, NULL); if (*phwnd) { TOOLINFO ti; ti.cbSize = sizeof(ti); ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; ti.hwnd = hDlg; ti.uId = (UINT_PTR)GetDlgItem(hDlg, id); ti.lpszText = (LPTSTR)pszText; // const -> non const ti.hinst = g_hInstance; SendMessage(*phwnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti); } } return (*phwnd) ? TRUE : FALSE; } void Int64ToStr(LONGLONG n, LPTSTR lpBuffer) { TCHAR szTemp[40]; LONGLONG iChr = 0; do { szTemp[iChr++] = TEXT('0') + (TCHAR)(n % 10); n = n / 10; } while (n != 0); do { iChr--; *lpBuffer++ = szTemp[iChr]; } while (iChr != 0); *lpBuffer++ = '\0'; } // // Obtain NLS info about how numbers should be grouped. // // The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT // have different ways of specifying number grouping. // // LOCALE NUMBERFMT Sample Country // // 3;0 3 1,234,567 United States // 3;2;0 32 12,34,567 India // 3 30 1234,567 ?? // // Not my idea. That's the way it works. // UINT GetNLSGrouping(void) { TCHAR szGrouping[32]; // If no locale info, then assume Western style thousands if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYLEN(szGrouping))) return 3; UINT grouping = 0; LPTSTR psz = szGrouping; for (;;) { if (*psz == '0') break; // zero - stop else if ((UINT)(*psz - '0') < 10) // digit - accumulate it grouping = grouping * 10 + (UINT)(*psz - '0'); else if (*psz) // punctuation - ignore it { } else // end of string, no "0" found { grouping = grouping * 10; // put zero on end (see examples) break; // and finished } psz++; } return grouping; } STDAPI_(LPTSTR) AddCommas64( LONGLONG n, LPTSTR pszResult, UINT cchResult ) { TCHAR szTemp[MAX_COMMA_NUMBER_SIZE]; TCHAR szSep[5]; NUMBERFMT nfmt; nfmt.NumDigits=0; nfmt.LeadingZero=0; nfmt.Grouping = GetNLSGrouping(); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYLEN(szSep)); nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep; nfmt.NegativeOrder= 0; Int64ToStr(n, szTemp); if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, cchResult) == 0) StringCchCopy(pszResult, cchResult, szTemp); return pszResult; } LPTSTR FormatString( LPCTSTR format, ... ) { LPTSTR str = NULL; va_list arglist; va_start(arglist, format); if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, format, 0, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPTSTR)&str, 0, &arglist ) == 0) { str = NULL; } va_end(arglist); return str; } STDAPI_(CONFIGRET) GetLocationInformation( DEVNODE dn, LPTSTR Location, ULONG LocationLen, HMACHINE hMachine ) /*++ Slot x (LocationInformation) Slot x LocationInformation on parent bus --*/ { CONFIGRET LastCR; DEVNODE dnParent; ULONG ulSize; DWORD UINumber; TCHAR Buffer[MAX_PATH]; TCHAR UINumberDescFormat[MAX_PATH]; TCHAR Format[MAX_PATH]; Buffer[0] = TEXT('\0'); // // We will first get any LocationInformation for the device. This will either // be in the LocationInformationOverride value in the devices driver (software) key // or if that is not present we will look for the LocationInformation value in // the devices device (hardware) key. // HKEY hKey; DWORD Type = REG_SZ; ulSize = sizeof(Buffer); if (CR_SUCCESS == CM_Open_DevNode_Key_Ex(dn, KEY_READ, 0, RegDisposition_OpenExisting, &hKey, CM_REGISTRY_SOFTWARE, hMachine )) { if (RegQueryValueEx(hKey, REGSTR_VAL_LOCATION_INFORMATION_OVERRIDE, NULL, &Type, (const PBYTE)Buffer, &ulSize) != ERROR_SUCCESS) { Buffer[0] = TEXT('\0'); } RegCloseKey(hKey); } // // If the buffer is empty then we didn't get the LocationInformationOverride // value in the device's software key. So, we will see if their is a // LocationInformation value in the device's hardware key. // if (Buffer[0] == TEXT('\0')) { ulSize = sizeof(Buffer); if (CM_Get_DevNode_Registry_Property_Ex(dn, CM_DRP_LOCATION_INFORMATION, NULL, Buffer, &ulSize, 0, hMachine) != CR_SUCCESS) { Buffer[0] = TEXT('\0'); } } // // UINumber has precedence over all other location information so check if this // device has a UINumber. // ulSize = sizeof(UINumber); if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dn, CM_DRP_UI_NUMBER, NULL, &UINumber, &ulSize, 0, hMachine )) == CR_SUCCESS) && (ulSize == sizeof(ULONG))) { UINumberDescFormat[0] = TEXT('\0'); ulSize = sizeof(UINumberDescFormat); // // Get the UINumber description format string from the device's parent, // if there is one, otherwise default to 'Location %1' if ((CM_Get_Parent_Ex(&dnParent, dn, 0, hMachine) == CR_SUCCESS) && (CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_UI_NUMBER_DESC_FORMAT, NULL, UINumberDescFormat, &ulSize, 0, hMachine) == CR_SUCCESS) && *UINumberDescFormat) { } else { ::LoadString(g_hInstance, IDS_UI_NUMBER_DESC_FORMAT, UINumberDescFormat, ARRAYLEN(UINumberDescFormat)); } LPTSTR UINumberBuffer = NULL; // // Fill in the UINumber string // UINumberBuffer = FormatString(UINumberDescFormat, UINumber); if (UINumberBuffer) { StringCchCopy((LPTSTR)Location, LocationLen, UINumberBuffer); LocalFree(UINumberBuffer); } else { Location[0] = TEXT('\0'); } // // If we also have LocationInformation then tack that on the end of the string // as well. // if (*Buffer) { StringCchCat((LPTSTR)Location, LocationLen, TEXT(" (")); StringCchCat((LPTSTR)Location, LocationLen, Buffer); StringCchCat((LPTSTR)Location, LocationLen, TEXT(")")); } } // // We don't have a UINumber but we do have LocationInformation // else if (*Buffer && (::LoadString(g_hInstance, IDS_LOCATION, Format, sizeof(Format)/sizeof(TCHAR)) != 0)) { StringCchPrintf((LPTSTR)Location, LocationLen, Format, Buffer); } // // We don't have a UINumber or LocationInformation so we need to get a description // of the parent of this device. // else { if ((LastCR = CM_Get_Parent_Ex(&dnParent, dn, 0, hMachine)) == CR_SUCCESS) { // // Try the registry for FRIENDLYNAME // Buffer[0] = TEXT('\0'); ulSize = sizeof(Buffer); if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_FRIENDLYNAME, NULL, Buffer, &ulSize, 0, hMachine )) != CR_SUCCESS) || !*Buffer) { // // Try the registry for DEVICEDESC // ulSize = sizeof(Buffer); if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_DEVICEDESC, NULL, Buffer, &ulSize, 0, hMachine )) != CR_SUCCESS) || !*Buffer) { ulSize = sizeof(Buffer); if (((LastCR = CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_CLASS, NULL, Buffer, &ulSize, 0, hMachine )) != CR_SUCCESS) || !*Buffer) { // // no parent, or parent name. // Buffer[0] = TEXT('\0'); } } } } if (*Buffer && (::LoadString(g_hInstance, IDS_LOCATION_NOUINUMBER, Format, sizeof(Format)/sizeof(TCHAR)) != 0)) { // // We have a description of the parent // StringCchPrintf((LPTSTR)Location, LocationLen, Format, Buffer); } else { // // We don't have any information so we will just say Unknown // ::LoadString(g_hInstance, IDS_UNKNOWN, Location, LocationLen); } } // // Make sure the Location string is NULL terminated. // Location[LocationLen - 1] = TEXT('\0'); return CR_SUCCESS; }