/******************************************************************************** ** Copyright (c) 2000 Microsoft Corporation. All Rights Reserved. ********************************************************************************/ #include #include #include #include #include #include "resource.h" #include "prvprop.h" // // Global Variables. // HINSTANCE ghInstance = NULL; // module handle. const TCHAR AppletName[] = TEXT("AC97 control panel"); #if (DBG) ///////////////////////////////////////////////////////////////////////////////// // dbgError ///////////////////////////////////////////////////////////////////////////////// // This function prints an error message. // It prints first the string passed and then the error that it gets with // GetLastError as a string. // // Arguments: // szMsg - message to print. // // Return Value: // None. // void dbgError (LPCTSTR szMsg) { LPTSTR errorMessage; DWORD count; // Get the error message from the system. count = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError (), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorMessage, 0, NULL); // Print the msg + error + \n\r. if (count) { OutputDebugString (szMsg); OutputDebugString (errorMessage); OutputDebugString (TEXT("\n\r")); // This is for those without a debugger. // MessageBox (NULL, szErrorMessage, szMsg, MB_OK | MB_ICONSTOP); LocalFree (errorMessage); } else { OutputDebugString (AppletName); OutputDebugString (TEXT(": Low memory condition. Cannot ") TEXT("print error message.\n\r")); } } #else #define dbgError(a) 0; #endif ///////////////////////////////////////////////////////////////////////////////// // DllMain ///////////////////////////////////////////////////////////////////////////////// // Main enty point of the DLL. // Save the instance handle; it is needed for property sheet creation. // // Arguments: // hModule - instance data, is equal to module handle // ul_reason_for_call - the reason for the call // lpReserved - some additional parameter. // // Return Value: // BOOL: FALSE if DLL should fail, TRUE on success // BOOL APIENTRY DllMain (HANDLE hModule, ULONG ul_reason_for_call, LPVOID lpReserved) { ghInstance = (HINSTANCE) hModule; return TRUE; } ///////////////////////////////////////////////////////////////////////////////// // FindAC97Device ///////////////////////////////////////////////////////////////////////////////// // This function stores the device info and device data of the "ac97smpl" driver. // It first creates a list of all devices that have a topology filter exposed // and then searches through the list to find the device with the service // "ac97smpl". The information is stored in pspRequest. // // For simplicity we search for the service name "ac97smpl". // Alternately, one could get more information about the device or driver, // then decide if it is suitable (regardless of its name). // // Arguments: // pspRequest pointer to Property sheet page request structure. // DeviceInfoData pointer to Device info data structure. // // Return Value: // TRUE on success, otherwise FALSE. // Note: on success that we have the device list still open - we have to destroy // the list later. The handle is stored at pspRequest->DeviceInfoSet. // BOOL FindAC97Device (PSP_PROPSHEETPAGE_REQUEST pspRequest, PSP_DEVINFO_DATA DeviceInfoData) { TCHAR szServiceName[128]; // // Prepare the pspRequest structure... // pspRequest->cbSize = sizeof (SP_PROPSHEETPAGE_REQUEST); pspRequest->DeviceInfoData = DeviceInfoData; pspRequest->PageRequested = SPPSR_ENUM_ADV_DEVICE_PROPERTIES; // ...and the DeviceInfoData structure. DeviceInfoData->cbSize = sizeof (SP_DEVINFO_DATA); // Create a list of devices with Topology interface. pspRequest->DeviceInfoSet = SetupDiGetClassDevs (&KSCATEGORY_TOPOLOGY, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // None found? if (pspRequest->DeviceInfoSet == INVALID_HANDLE_VALUE) { dbgError (TEXT("Test: SetupDiGetClassDevs: ")); return FALSE; } // // Go through the list of all devices found. // int nIndex = 0; while (SetupDiEnumDeviceInfo (pspRequest->DeviceInfoSet, nIndex, DeviceInfoData)) { // // Get the service name for that device. // if (!SetupDiGetDeviceRegistryProperty (pspRequest->DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, NULL, (PBYTE)szServiceName, sizeof (szServiceName), NULL)) { dbgError (TEXT("Test: SetupDiGetDeviceRegistryProperty: ")); SetupDiDestroyDeviceInfoList (pspRequest->DeviceInfoSet); return FALSE; } // // We only care about service "ac97smpl" // DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); if (CompareString (lcid, NORM_IGNORECASE, szServiceName, -1, TEXT("AC97SMPL"), -1) == CSTR_EQUAL) { // // We found it! The information is already stored, just return. // Note that we have the device list still open - we have to destroy // the list later. // return TRUE; } // Take the next in the list. nIndex++; } // // We did not find the service. // SetupDiDestroyDeviceInfoList (pspRequest->DeviceInfoSet); return FALSE; } ///////////////////////////////////////////////////////////////////////////////// // AddPropSheet ///////////////////////////////////////////////////////////////////////////////// // This function is a callback that is passed to the "AC97PropPageProvider" // function of "ac97prop.dll". It is used to add the property page sheet // to the property dialog. // What we do here is store the property sheet handle and return. // // Arguments: // hPSP property sheet handle // lParam parameter that we passed to "AC97PropPageProvider". // // Return Value: // TRUE on success, otherwise FALSE. // BOOL APIENTRY AddPropSheet (HPROPSHEETPAGE hPSP, LPARAM lParam) { HPROPSHEETPAGE **pphPropSheet = (HPROPSHEETPAGE **)lParam; HPROPSHEETPAGE *phPropSheetPage; // Check the parameters if (!pphPropSheet) return FALSE; // Allocate the space for the handle. phPropSheetPage = (HPROPSHEETPAGE *)LocalAlloc (LPTR, sizeof(HPROPSHEETPAGE)); if (!phPropSheetPage) return FALSE; *phPropSheetPage = hPSP; *pphPropSheet = phPropSheetPage; return TRUE; } ///////////////////////////////////////////////////////////////////////////////// // GetDLLInfo ///////////////////////////////////////////////////////////////////////////////// // This function loads the library and gets the entry point address. // With the information returned we can create the property dialog. // // For simplicity reasons we load the DLL "ac97prop" and call the function // "AC97PropPageProvider". A more flexible course of action is to get this // information from the registry. To do this, we read the "EnumPropPages32" // entry in the drivers section to obtain a registry path (read the "drivers" // entry in the device section to get the registry path of the drivers section) // -- this path leads to a key and entry which specifies the DLL and entry point. // // Arguments: // *phDLL pointer to module handle of loaded DLL. // *pAC97PropPageProvider pointer to "AC97PropPageProvider" in loaded DLL // // Return Value: // TRUE on success, otherwise FALSE. // Note: on success the library is still loaded. // BOOL GetDLLInfo (HMODULE *phDLL, LPFNADDPROPSHEETPAGES *pAC97PropPageProvider) { // Load the library. *phDLL = LoadLibrary (TEXT("ac97prop.dll")); if (!*phDLL) { dbgError (TEXT("DisplayPropertySheet: LoadLibrary")); return FALSE; } // Get the address of the function. *pAC97PropPageProvider = (LPFNADDPROPSHEETPAGES)GetProcAddress (*phDLL, "AC97PropPageProvider"); if (!*pAC97PropPageProvider) { dbgError (TEXT("DisplayPropertySheet: GetProcAddress")); FreeLibrary (*phDLL); return FALSE; } return TRUE; } ///////////////////////////////////////////////////////////////////////////////// // DisplayPropertySheet ///////////////////////////////////////////////////////////////////////////////// // This function displays the property dialog. // It is called by CPlApplet when the ac97cpl icon gets a double click. // // Arguments: // hWnd parent window handle // // Return Value: // None. // void DisplayPropertySheet (HWND hWnd) { SP_PROPSHEETPAGE_REQUEST pspRequest; // structure passed to ac97prop SP_DEVINFO_DATA DeviceInfoData; // pspRequest points to it. HMODULE hDLL; // Module handle of library LPFNADDPROPSHEETPAGES AC97PropPageProvider; // function to be called. PROPSHEETHEADER psh; // You could move this to CPL_INIT, then it would be called // before the control panel window appears. // In case of an failure the icon would not be displayed. In our sample // however, the icon will be displayed and when the user clicks on it he // gets the error message. if (!FindAC97Device(&pspRequest, &DeviceInfoData)) { MessageBox (hWnd, TEXT("You must install the ac97 sample driver."), AppletName, MB_ICONSTOP | MB_OK); return; } // Load the library and get the function pointer. if (!GetDLLInfo (&hDLL, &AC97PropPageProvider)) { MessageBox (hWnd, TEXT("The ac97 property page DLL could not load."), AppletName, MB_ICONSTOP | MB_OK); SetupDiDestroyDeviceInfoList (pspRequest.DeviceInfoSet); return; } // // Prepare the header for the property sheet. // psh.nStartPage = 0; psh.dwSize = sizeof(psh); psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW; psh.hwndParent = hWnd; psh.hInstance = ghInstance; psh.pszIcon = NULL; psh.pszCaption = MAKEINTRESOURCE(IDS_AC97CPL); psh.nPages = 1; // Call the function to request the property sheet page. if (!(*AC97PropPageProvider) ((LPVOID)&pspRequest, AddPropSheet, (LPARAM)&psh.phpage)) { MessageBox (hWnd, TEXT("\"AC97PropPageProvider\" had a failure."), AppletName, MB_ICONSTOP | MB_OK); FreeLibrary (hDLL); SetupDiDestroyDeviceInfoList (pspRequest.DeviceInfoSet); return; } // Create the dialog. The function returns when the dialog is closed. if (PropertySheet (&psh) < 0) { // // Dialog closed abnormally. This might be a system failure. // MessageBox (hWnd, TEXT("Please reinstall the ac97 sample driver."), AppletName, MB_ICONSTOP | MB_OK); } // Clean up. FreeLibrary (hDLL); LocalFree (psh.phpage); SetupDiDestroyDeviceInfoList (pspRequest.DeviceInfoSet); } ///////////////////////////////////////////////////////////////////////////////// // CPlApplet ///////////////////////////////////////////////////////////////////////////////// // This function is called by the control panel manager. It is very similar to // a Windows message handler (search for CplApplet in MSDN for description). // // Arguments: // HWND hWnd Parent window handle // UINT uMsg The message // LPARAM lParam1 depends on message // LPARAM lParam2 depends on message // // Return Value: // LONG (depends on message; in general 0 means failure). // LONG APIENTRY CPlApplet (HWND hWnd, UINT uMsg, LPARAM lParam1, LPARAM lParam2) { switch (uMsg) { // Initialize. You can allocate memory here. case CPL_INIT: return TRUE; // How many applets are in this DLL? case CPL_GETCOUNT: return 1; // Requesting information about the dialog box. // lParam1 is the dialog box number (we have only one) and // lParam2 is the pointer to a CPLINFO structure. // There is no return value. case CPL_INQUIRE: { UINT uAppNum = (UINT)lParam1; LPCPLINFO pCplInfo = (LPCPLINFO)lParam2; if (!pCplInfo) return TRUE; // unsuccessful if (uAppNum == 0) // first Applet? { pCplInfo->idIcon = IDI_AC97CPL; pCplInfo->idName = IDS_AC97CPL; pCplInfo->idInfo = IDS_AC97CPLINFO; } break; } // This is basically the same as CPL_INQUIRE, but passes a pointer // to a different structure. // This function is called before CPL_INQUIRE and if we return zero // here, then CPL_INQUIRE is called. case CPL_NEWINQUIRE: break; // One of these messages are sent when we should display the dialog box. // There is no return value. // lParam1 is the dialog box number (we have only one) case CPL_DBLCLK: case CPL_STARTWPARMS: { UINT uAppNum = (UINT)lParam1; if (uAppNum == 0) // first Applet? DisplayPropertySheet (hWnd); break; } // We get unloaded in a second. // There is no return value. case CPL_EXIT: break; default: // Who knows? break; } return 0; }