//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: dvdprop.c // //-------------------------------------------------------------------------- #include "propp.h" #include "ntddcdvd.h" #include "dvdprop.h" #include "volprop.h" #include "resource.h" // // Instantiate the device interface GUIDs defined in ntddstor.h in this module // #include #include // // Help ID mapping for context sensitive help // const DWORD DvdHelpIDs[]= { IDC_CURRENT_REGION, IDH_DEVMGR_DVD_CURRENT, //Current region box IDC_NEW_REGION, IDH_DEVMGR_DVD_NEW, //New region box IDC_DVD_COUNTRY_LIST, IDH_DEVMGR_DVD_LIST, //List box IDC_DVD_HELP, IDH_DEVMGR_DVD_NOHELP, IDC_CHANGE_TEXT, IDH_DEVMGR_DVD_NOHELP, 0, 0 }; ULONG RegionIndexTable[MAX_REGIONS] = { DVD_REGION1_00, DVD_REGION2_00, DVD_REGION3_00, DVD_REGION4_00, DVD_REGION5_00, DVD_REGION6_00 }; ULONG RegionSizeTable[MAX_REGIONS] = { DVD_MAX_REGION1, DVD_MAX_REGION2, DVD_MAX_REGION3, DVD_MAX_REGION4, DVD_MAX_REGION5, DVD_MAX_REGION6 }; BOOL IsUserAdmin( VOID ); PPAGE_INFO DvdCreatePageInfo(IN HDEVINFO deviceInfoSet, IN PSP_DEVINFO_DATA deviceInfoData) { PPAGE_INFO tmp = NULL; if (!(tmp = LocalAlloc(LPTR, sizeof(PAGE_INFO)))) { return NULL; } memset (tmp, 0, sizeof(PAGE_INFO)); tmp->deviceInfoSet = deviceInfoSet; tmp->deviceInfoData = deviceInfoData; return tmp; } void DvdDestroyPageInfo(PPAGE_INFO * ppPageInfo) { PPAGE_INFO ppi = *ppPageInfo; LocalFree(ppi); *ppPageInfo = NULL; } HPROPSHEETPAGE DvdCreatePropertyPage(PROPSHEETPAGE * ppsp, PPAGE_INFO ppi) { // // Add the Port Settings property page // ppsp->dwSize = sizeof(PROPSHEETPAGE); ppsp->dwFlags = PSP_USECALLBACK; // | PSP_HASHELP; ppsp->hInstance = ModuleInstance; ppsp->pszTemplate = MAKEINTRESOURCE(ID_DVD_PROPPAGE); // // following points to the dlg window proc // ppsp->pfnDlgProc = DvdDlgProc; ppsp->lParam = (LPARAM) ppi; // // Following points to the control callback of the dlg window proc. // The callback gets called before creation/after destruction of the page // ppsp->pfnCallback = DvdDlgCallback; // // Allocate the actual page // return CreatePropertySheetPage(ppsp); } BOOL APIENTRY DvdPropPageProvider(LPVOID pinfo, LPFNADDPROPSHEETPAGE pfnAdd, LPARAM lParam) { PSP_PROPSHEETPAGE_REQUEST ppr; PROPSHEETPAGE psp; HPROPSHEETPAGE hpsp; PPAGE_INFO ppi = NULL; ppr = (PSP_PROPSHEETPAGE_REQUEST) pinfo; if (ppr->PageRequested == SPPSR_ENUM_ADV_DEVICE_PROPERTIES) { ppi = DvdCreatePageInfo(ppr->DeviceInfoSet, ppr->DeviceInfoData); if (!ppi) { return FALSE; } if (!GetCurrentRpcData(ppi, &ppi->regionData)) { // // not a DVD-ROM with RPC2 support // DvdDestroyPageInfo(&ppi); return FALSE; } memset(&psp, 0, sizeof(PROPSHEETPAGE)); hpsp = DvdCreatePropertyPage(&psp, ppi); if (!hpsp) { return FALSE; } if (!pfnAdd(hpsp, lParam)) { DestroyPropertySheetPage(hpsp); return FALSE; } } return TRUE; } UINT CALLBACK DvdDlgCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp) { PPAGE_INFO ppi; switch (uMsg) { case PSPCB_CREATE: return TRUE; // return TRUE to continue with creation of page case PSPCB_RELEASE: ppi = (PPAGE_INFO) ppsp->lParam; DvdDestroyPageInfo(&ppi); return 0; // return value ignored default: break; } return TRUE; } void DvdInitializeControls(PPAGE_INFO ppi, HWND hDlg) { DWORD dwError; DWORD dwType; DWORD dwSize; BOOL disableControls = FALSE; HWND hwnd; ULONG i; ULONG j; TCHAR buffer[1000]; hwnd = GetDlgItem(hDlg, IDC_DVD_COUNTRY_LIST); for (i=0; inewRegion == 0) || (ppi->currentRegion == ppi->newRegion)) { // // nothing to do // return TRUE; } // // confirm with the user // okToProceed = FALSE; if (ppi->regionData.ResetCount == 1) { if (LoadString(ModuleInstance, DVD_SET_RPC_CONFIRM_LAST_CHANCE, buffer, 500) && LoadString(ModuleInstance, DVD_SET_RPC_CONFIRM_TITLE, titleBuffer, 500)) { if (MessageBox(hDlg, buffer, titleBuffer, MB_ICONEXCLAMATION | MB_OKCANCEL | MB_DEFBUTTON2 | MB_APPLMODAL) == IDOK) { okToProceed = TRUE; } } } else { if (LoadString(ModuleInstance, DVD_SET_RPC_CONFIRM, bufferFormat, 500) && LoadString(ModuleInstance, DVD_SET_RPC_CONFIRM_TITLE, titleBuffer, 500)) { wsprintf (buffer, bufferFormat, ppi->newRegion); if (MessageBox(hDlg, buffer, titleBuffer, MB_ICONEXCLAMATION | MB_OKCANCEL | MB_DEFBUTTON2 | MB_APPLMODAL) == IDOK) { okToProceed = TRUE; } } } if (okToProceed == FALSE) { return FALSE; } // // make sure the drive has a dvd with the same region // call GetCurrentRpcData // writeHandle = UtilpGetDeviceHandle(ppi->deviceInfoSet, ppi->deviceInfoData, (LPGUID)&CdRomClassGuid, GENERIC_READ | GENERIC_WRITE); if (writeHandle != INVALID_HANDLE_VALUE) { copyProtectKey = (PDVD_COPY_PROTECT_KEY) keyBuffer; memset(copyProtectKey, 0, DVD_SET_RPC_KEY_LENGTH); copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH; copyProtectKey->KeyType = DvdSetRpcKey; rpcKey = (PDVD_SET_RPC_KEY) copyProtectKey->KeyData; rpcKey->PreferredDriveRegionCode = ~(1 << (ppi->newRegion - 1)); status = DeviceIoControl(writeHandle, IOCTL_DVD_SEND_KEY2, copyProtectKey, DVD_SET_RPC_KEY_LENGTH, NULL, 0, &returned, FALSE); CloseHandle (writeHandle); } else { status = 0; } if (!status) { if (LoadString(ModuleInstance, DVD_SET_RPC_ERROR, bufferFormat, 500) && LoadString(ModuleInstance, DVD_SET_RPC_ERROR_TITLE, titleBuffer, 500)) { wsprintf (buffer, bufferFormat, ppi->newRegion); MessageBox(hDlg, buffer, titleBuffer, MB_ICONERROR | MB_OK); } return FALSE; } else { GetCurrentRpcData(ppi, &ppi->regionData); return TRUE; } } INT_PTR APIENTRY DvdDlgProc(IN HWND hDlg, IN UINT uMessage, IN WPARAM wParam, IN LPARAM lParam) { PPAGE_INFO ppi; ppi = (PPAGE_INFO) GetWindowLongPtr(hDlg, DWLP_USER); switch (uMessage) { case WM_INITDIALOG: // // on WM_INITDIALOG call, lParam points to the property // sheet page. // // The lParam field in the property sheet page struct is set by the // caller. When I created the property sheet, I passed in a pointer // to a struct containing information about the device. Save this in // the user window long so I can access it on later messages. // ppi = (PPAGE_INFO) ((LPPROPSHEETPAGE)lParam)->lParam; SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) ppi); // // Initialize dlg controls // DvdInitializeControls(ppi, hDlg); // // Didn't set the focus to a particular control. If we wanted to, // then return FALSE // return TRUE; case WM_COMMAND: switch (HIWORD(wParam)) { case CBN_SELCHANGE: PropSheet_Changed(GetParent(hDlg), hDlg); DvdUpdateNewRegionBox (ppi, hDlg); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR) PSNRET_NOERROR); return TRUE; default: break; } switch(LOWORD(wParam)) { default: break; } break; case WM_CONTEXTMENU: return DvdContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_HELP: DvdHelp(hDlg, (LPHELPINFO) lParam); break; case WM_NOTIFY: switch (((NMHDR *)lParam)->code) { // case PSN_KILLACTIVE: // if (ppi->changesFailed) { // // SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); // } else { // // SetWindowLong(hDlg, DWL_MSGRESULT, FALSE); // } // return TRUE; // // Sent when the user clicks on Apply OR OK !! // case PSN_APPLY: // // Do what ever action is necessary // if (DvdApplyChanges(ppi, hDlg)) { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR); ppi->changesFailed = FALSE; DvdUpdateCurrentSettings(ppi, hDlg); } else { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); ppi->changesFailed = TRUE; } return TRUE; default: break; } break; } SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR); return FALSE; } void DvdUpdateNewRegionBox (PPAGE_INFO ppi, HWND hDlg) { HWND hwnd; ULONG selectionIndex; WCHAR buffer[100]; ULONG currentRegion; BOOL status; // // new region code // hwnd = GetDlgItem(hDlg, IDC_DVD_COUNTRY_LIST); // // NOTE: SendMessage() will return 64-bit result in Sundown // selectionIndex = (ULONG) SendMessage(hwnd, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); if (selectionIndex != LB_ERR) { if (LB_ERR != SendMessage(hwnd, LB_GETTEXT, selectionIndex, (LPARAM) buffer)) { ppi->newRegion = DvdCountryToRegion (buffer); if (ppi->newRegion != -1) { if (LoadString(ModuleInstance, DVD_REGION1_NAME + ppi->newRegion - 1, buffer, 100)) { hwnd = GetDlgItem(hDlg, IDC_NEW_REGION); SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) buffer); } } } } } ULONG DvdCountryToRegion (LPCTSTR Country) { ULONG i; ULONG j; WCHAR buffer[100]; for (i=0; ideviceInfoSet, ppi->deviceInfoData, (LPGUID)&CdRomClassGuid, GENERIC_READ); if (deviceHandle == INVALID_HANDLE_VALUE) { return FALSE; } copyProtectKey = LocalAlloc(LPTR, DVD_RPC_KEY_LENGTH); if (copyProtectKey == NULL) { CloseHandle(deviceHandle); return FALSE; } copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; copyProtectKey->KeyType = DvdGetRpcKey; returned = 0; status = DeviceIoControl(deviceHandle, IOCTL_DVD_READ_KEY, copyProtectKey, DVD_RPC_KEY_LENGTH, copyProtectKey, DVD_RPC_KEY_LENGTH, &returned, FALSE); // // this will default to not showing the property page // rpcScheme = 0; if (status && (returned == DVD_RPC_KEY_LENGTH)) { rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; rpcScheme = rpcKey->RpcScheme; regionMask = ~rpcKey->RegionMask; userResetsAvailable = rpcKey->UserResetsAvailable; DebugPrint((1, "Got %x user resets available\n", userResetsAvailable)); } else { DebugPrint((1, "Maybe not a DVD drive?\n", status, returned)); } LocalFree(copyProtectKey); if (rpcScheme == 0) { // // all region drive. no need to show the property sheet // DebugPrint((1, "All Region DVD-ROM Drive -- no property sheet\n")); CloseHandle(deviceHandle); return FALSE; } // // get region status // memset(regionData, 0, sizeof(DVD_REGION)); status = DeviceIoControl(deviceHandle, IOCTL_DVD_GET_REGION, NULL, 0, regionData, sizeof(DVD_REGION), &returned, FALSE); if (!(status && (returned == sizeof(DVD_REGION)))) { // // no media in the drive // DebugPrint((1, "No media in drive? making up info\n")); regionData->CopySystem = 1; regionData->RegionData = 0xff; regionData->SystemRegion = regionMask; regionData->ResetCount = userResetsAvailable; } CloseHandle(deviceHandle); return TRUE; } ULONG DvdRegionMaskToRegionNumber ( UCHAR PlayMask ) { ULONG i; ULONG mask; if (!PlayMask) { return 0; } for (i=0, mask=1; i<8; i++, mask <<= 1) { if (PlayMask & mask) { return i + 1; } } return 0; } void DvdUpdateCurrentSettings (PPAGE_INFO ppi, HWND hDlg) { HWND hwnd; ULONG selectionIndex; WCHAR buffer[100]; WCHAR formatBuffer[100]; BOOL status; ppi->currentRegion = DvdRegionMaskToRegionNumber ( ppi->regionData.SystemRegion ); // // current region // if (ppi->currentRegion) { status = LoadString(ModuleInstance, DVD_REGION1_NAME + ppi->currentRegion - 1, buffer, 100); } else { status = LoadString(ModuleInstance, DVD_NOREGION_NAME, buffer, 100); } if (status) { hwnd = GetDlgItem(hDlg, IDC_CURRENT_REGION); SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) buffer); } // // region change limit // if (LoadString(ModuleInstance, DVD_CHANGE_TEXT, formatBuffer, 100)) { wsprintf (buffer, formatBuffer, (ULONG) ppi->regionData.ResetCount); hwnd = GetDlgItem(hDlg, IDC_CHANGE_TEXT); SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) buffer); } if (ppi->regionData.ResetCount == 0) { EnableWindow(GetDlgItem(hDlg, IDC_DVD_COUNTRY_LIST), FALSE); EnableWindow(GetDlgItem(hDlg, IDC_NEW_REGION), FALSE); } return; } // // SystemLocale2DvdRegion expects // this list to be sorted by LCID numbers // // Note: Due to PoliCheck, comments with // approximated region/country names // had to be removed. // LCID_2_DVD_TABLE Lcid2DvdTable[] = { {0x0401, 2}, {0x0402, 2}, {0x0403, 2}, {0x0404, 3}, {0x0405, 2}, {0x0406, 2}, {0x0407, 2}, {0x0408, 2}, {0x0409, 1}, {0x040a, 2}, {0x040b, 2}, {0x040c, 2}, {0x040d, 2}, {0x040e, 2}, {0x040f, 2}, {0x0410, 2}, {0x0411, 2}, {0x0412, 3}, {0x0413, 2}, {0x0414, 2}, {0x0415, 2}, {0x0416, 4}, {0x0418, 2}, {0x0419, 5}, {0x041a, 2}, {0x041b, 2}, {0x041c, 2}, {0x041d, 2}, {0x041e, 3}, {0x041f, 2}, {0x0420, 5}, {0x0421, 3}, {0x0422, 5}, {0x0423, 5}, {0x0424, 2}, {0x0425, 5}, {0x0426, 5}, {0x0427, 5}, {0x0429, 2}, {0x042a, 3}, {0x042b, 5}, {0x042c, 5}, {0x042d, 2}, {0x042f, 2}, {0x0436, 2}, {0x0437, 5}, {0x0438, 2}, {0x0439, 5}, {0x043e, 3}, {0x043f, 5}, {0x0441, 5}, {0x0443, 5}, {0x0444, 5}, {0x0445, 5}, {0x0446, 5}, {0x0447, 5}, {0x0448, 5}, {0x0449, 5}, {0x044a, 5}, {0x044b, 5}, {0x044c, 5}, {0x044d, 5}, {0x044e, 5}, {0x044f, 5}, {0x0457, 5}, {0x0801, 2}, {0x0804, 6}, {0x0807, 2}, {0x0809, 2}, {0x080a, 4}, {0x080c, 2}, {0x0810, 2}, {0x0813, 2}, {0x0814, 2}, {0x0816, 2}, {0x081a, 2}, {0x081d, 2}, {0x0827, 5}, {0x082c, 5}, {0x083e, 3}, {0x0843, 5}, {0x0861, 5}, {0x0c01, 2}, {0x0c04, 3}, {0x0c07, 2}, {0x0c09, 4}, {0x0c0a, 2}, {0x0c0c, 1}, {0x0c1a, 2}, {0x1001, 5}, {0x1004, 3}, {0x1007, 2}, {0x1009, 1}, {0x100a, 4}, {0x100c, 2}, {0x1401, 5}, {0x1404, 3}, {0x1407, 2}, {0x1409, 4}, {0x140a, 4}, {0x140c, 2}, {0x1801, 5}, {0x1809, 2}, {0x180a, 4}, {0x180c, 2}, {0x1c01, 5}, {0x1c09, 2}, {0x1c0a, 4}, {0x2001, 2}, {0x2009, 4}, {0x200a, 4}, {0x2401, 2}, {0x2409, 4}, {0x240a, 4}, {0x2801, 2}, {0x2809, 4}, {0x280a, 4}, {0x2c01, 2}, {0x2c09, 4}, {0x2c0a, 4}, {0x3001, 2}, {0x3009, 5}, {0x300a, 4}, {0x3401, 2}, {0x3409, 3}, {0x340a, 4}, {0x3801, 2}, {0x380a, 4}, {0x3c01, 2}, {0x3c0a, 4}, {0x4001, 2}, {0x400a, 4}, {0x440a, 4}, {0x480a, 4}, {0x4c0a, 4}, {0x500a, 1} }; #define NUM_LCID_ENTRIES (sizeof(Lcid2DvdTable)/sizeof(LCID_2_DVD_TABLE)) DWORD SystemLocale2DvdRegion ( LCID Lcid ) { #define MID_INDEX(x,y) (((y-x)/2) + x) DWORD i; DWORD j; DWORD k; i=0; j=NUM_LCID_ENTRIES; while (1) { k = MID_INDEX(i,j); if (Lcid2DvdTable[k].Lcid != Lcid) { if (i == j) { // // not in the table, // return a default region of ZERO!!! // return 0; } if (Lcid2DvdTable[k].Lcid < Lcid) { i = k; } else { // Lcid2DvdTable[k].Lcid > Lcid j = k; } } else { return Lcid2DvdTable[k].DvdRegion; } } } DWORD DvdClassInstaller( IN DI_FUNCTION InstallFunction, IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) /*++ Routine Description: This routine acts as the class installer for hard disk controllers (IDE controllers/channels). It provides special handling for the following DeviceInstaller function codes: DIF_INSTALLDEVICE - get the system locale and write it to the driver key Arguments: InstallFunction - Specifies the device installer function code indicating the action being performed. DeviceInfoSet - Supplies a handle to the device information set being acted upon by this install action. DeviceInfoData - Optionally, supplies the address of a device information element being acted upon by this install action. Return Value: If this function successfully completed the requested action, the return value is NO_ERROR. If the default behavior is to be performed for the requested action, the return value is ERROR_DI_DO_DEFAULT. If an error occurred while attempting to perform the requested action, a Win32 error code is returned. --*/ { switch(InstallFunction) { case DIF_ADDPROPERTYPAGE_ADVANCED: case DIF_ADDREMOTEPROPERTYPAGE_ADVANCED: { // // get the current class install parameters for the device // SP_ADDPROPERTYPAGE_DATA AddPropertyPageData; // // DeviceInfoSet is NULL if setup is requesting property pages for // the device setup class. We don't want to do anything in this // case. // if (DeviceInfoData==NULL) return ERROR_DI_DO_DEFAULT; ZeroMemory(&AddPropertyPageData, sizeof(SP_ADDPROPERTYPAGE_DATA)); AddPropertyPageData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); if (SetupDiGetClassInstallParams(DeviceInfoSet, DeviceInfoData, (PSP_CLASSINSTALL_HEADER)&AddPropertyPageData, sizeof(SP_ADDPROPERTYPAGE_DATA), NULL )) { PVOLUME_PAGE_DATA pVolumePageData; HPROPSHEETPAGE pageHandle; PROPSHEETPAGE page; // // Ensure that the maximum number of dynamic pages for the // device has not yet been met // if (AddPropertyPageData.NumDynamicPages >= MAX_INSTALLWIZARD_DYNAPAGES) return NO_ERROR; if (IsUserAdmin()) pVolumePageData = HeapAlloc(GetProcessHeap(), 0, sizeof(VOLUME_PAGE_DATA)); else pVolumePageData = NULL; if (pVolumePageData) { HMODULE LdmModule; SP_DEVINFO_LIST_DETAIL_DATA DeviceInfoSetDetailData; // // Save DeviceInfoSet and DeviceInfoData // pVolumePageData->DeviceInfoSet = DeviceInfoSet; pVolumePageData->DeviceInfoData = DeviceInfoData; // // Create volume property sheet page // memset(&page, 0, sizeof(PROPSHEETPAGE)); page.dwSize = sizeof(PROPSHEETPAGE); page.dwFlags = PSP_USECALLBACK; page.hInstance = ModuleInstance; page.pszTemplate = MAKEINTRESOURCE(ID_VOLUME_PROPPAGE); page.pfnDlgProc = VolumeDialogProc; page.pfnCallback = VolumeDialogCallback; page.lParam = (LPARAM) pVolumePageData; pageHandle = CreatePropertySheetPage(&page); if(!pageHandle) { HeapFree(GetProcessHeap(), 0, pVolumePageData); return NO_ERROR; } // // Add the new page to the list of dynamic property // sheets // AddPropertyPageData.DynamicPages[ AddPropertyPageData.NumDynamicPages++]=pageHandle; // // Check if the property page is pulled up from disk manager. // pVolumePageData->bInvokedByDiskmgr = FALSE; LdmModule = GetModuleHandle(TEXT("dmdskmgr")); if ( LdmModule ) { IS_REQUEST_PENDING pfnIsRequestPending; pfnIsRequestPending = (IS_REQUEST_PENDING) GetProcAddress(LdmModule, "IsRequestPending"); if (pfnIsRequestPending) { if ((*pfnIsRequestPending)()) pVolumePageData->bInvokedByDiskmgr = TRUE; } } SetupDiSetClassInstallParams(DeviceInfoSet,DeviceInfoData, (PSP_CLASSINSTALL_HEADER)&AddPropertyPageData, sizeof(SP_ADDPROPERTYPAGE_DATA)); } } return NO_ERROR; } case DIF_INSTALLDEVICE : { HKEY hKey; DWORD dvdRegion; hKey = SetupDiOpenDevRegKey (DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ | KEY_WRITE); if (hKey != INVALID_HANDLE_VALUE) { dvdRegion = SystemLocale2DvdRegion (GetSystemDefaultLCID()); RegSetValueEx (hKey, TEXT("DefaultDvdRegion"), 0, REG_DWORD, (PUCHAR) &dvdRegion, sizeof(dvdRegion) ); RegCloseKey (hKey); } } // // fall through // default : // // Just do the default action. // return ERROR_DI_DO_DEFAULT; } } BOOL DvdContextMenu( HWND HwndControl, WORD Xpos, WORD Ypos ) { WinHelp(HwndControl, _T("devmgr.hlp"), HELP_CONTEXTMENU, (ULONG_PTR) DvdHelpIDs); return FALSE; } void DvdHelp( HWND ParentHwnd, LPHELPINFO HelpInfo ) { if (HelpInfo->iContextType == HELPINFO_WINDOW) { WinHelp((HWND) HelpInfo->hItemHandle, _T("devmgr.hlp"), HELP_WM_HELP, (ULONG_PTR) DvdHelpIDs); } }