/**Module*Header**\ * Module Name: PROFMAN.C * Module Descripton: Profile management functions. * Warnings: * Issues: * Public Routines: * Created: 5 Nov 1996 * Author: Srinivasan Chandrasekar [srinivac] * Copyright (c) 1996, 1997 Microsoft Corporation */ #include "mscms.h" #include "objbase.h" #include "initguid.h" #include "devguid.h" #include "sti.h" #define TAG_DEVICESETTINGS 'devs' #define TAG_MS01 'MS01' #define TAG_MS02 'MS02' #define TAG_MS03 'MS03' #define ID_MSFT_REVERSED 'tfsm' #define ID_MEDIATYPE_REVERSED 'aidm' #define ID_DITHER_REVERSED 'ntfh' #define ID_RESLN_REVERSED 'nlsr' #define DEVICE_PROFILE_DATA 1 #define DEVICE_PROFILE_ENUMMODE 2 // Local types typedef enum { NOMATCH = 0, MATCH = 1, EXACT_MATCH = 2, } MATCHTYPE; typedef struct tagREGDATA { DWORD dwRefCount; DWORD dwManuID; DWORD dwModelID; } REGDATA, * PREGDATA; typedef struct tagSCANNERDATA { PWSTR pDeviceName; HINSTANCE hModule; PSTI pSti; } SCANNERDATA, * PSCANNERDATA; typedef BOOL(WINAPI* PFNOPENDEVICE)(PTSTR, LPHANDLE, PTSTR); typedef BOOL(WINAPI* PFNCLOSEDEVICE)(HANDLE); typedef DWORD(WINAPI* PFNGETDEVICEDATA)(HANDLE, PTSTR, PTSTR, PDWORD, PBYTE, DWORD, PDWORD); typedef DWORD(WINAPI* PFNSETDEVICEDATA)(HANDLE, PTSTR, PTSTR, DWORD, PBYTE, DWORD); typedef HRESULT(__stdcall* PFNSTICREATEINSTANCE)(HINSTANCE, DWORD, PSTI*, LPDWORD); // Local functions BOOL InternalGetColorDirectory(LPCTSTR, PTSTR, DWORD*); BOOL InternalInstallColorProfile(LPCTSTR, LPCTSTR); BOOL InternalUninstallColorProfile(LPCTSTR, LPCTSTR, BOOL); BOOL InternalAssociateColorProfileWithDevice(LPCTSTR, LPCTSTR, LPCTSTR); BOOL InternalDisassociateColorProfileFromDevice(LPCTSTR, LPCTSTR, LPCTSTR); BOOL InternalEnumColorProfiles(LPCTSTR, PENUMTYPE, PBYTE, PDWORD, PDWORD); BOOL InternalSetSCSProfile(LPCTSTR, DWORD, LPCTSTR); BOOL InternalGetSCSProfile(LPCTSTR, DWORD, PTSTR, PDWORD); VOID ConvertDwordToString(DWORD, PTSTR); PTSTR ConvertClassIdToClassString(DWORD); BOOL GetProfileClassString(LPCTSTR, PTSTR, PPROFILEHEADER); BOOL GetDeviceData(LPCTSTR, DWORD, DWORD, PVOID*, PDWORD, BOOL); BOOL SetDeviceData(LPCTSTR, DWORD, DWORD, PVOID, DWORD); BOOL IGetDeviceData(LPCTSTR, DWORD, DWORD, PVOID*, PDWORD, BOOL); BOOL ISetDeviceData(LPCTSTR, DWORD, DWORD, PVOID, DWORD); BOOL IsStringInMultiSz(PTSTR, PTSTR); DWORD RemoveStringFromMultiSz(PTSTR, PTSTR, DWORD); VOID InsertInBuffer(PBYTE, PBYTE, PTSTR); MATCHTYPE DoesProfileMatchEnumRecord(PTSTR, PENUMTYPE); MATCHTYPE CheckResMedHftnMatch(HPROFILE, PENUMTYPE); BOOL DwordMatches(PSETTINGS, DWORD); BOOL QwordMatches(PSETTINGS, PDWORD); BOOL WINAPI OpenPrtr(PTSTR, LPHANDLE, PTSTR); BOOL WINAPI ClosePrtr(HANDLE); DWORD WINAPI GetPrtrData(HANDLE, PTSTR, PTSTR, PDWORD, PBYTE, DWORD, PDWORD); DWORD WINAPI SetPrtrData(HANDLE, PTSTR, PTSTR, DWORD, PBYTE, DWORD); BOOL WINAPI OpenMonitor(PTSTR, LPHANDLE, PTSTR); BOOL WINAPI CloseMonitor(HANDLE); DWORD WINAPI GetMonitorData(HANDLE, PTSTR, PTSTR, PDWORD, PBYTE, DWORD, PDWORD); DWORD WINAPI SetMonitorData(HANDLE, PTSTR, PTSTR, DWORD, PBYTE, DWORD); BOOL WINAPI OpenScanner(PTSTR, LPHANDLE, PTSTR); BOOL WINAPI CloseScanner(HANDLE); DWORD WINAPI GetScannerData(HANDLE, PTSTR, PTSTR, PDWORD, PBYTE, DWORD, PDWORD); DWORD WINAPI SetScannerData(HANDLE, PTSTR, PTSTR, DWORD, PBYTE, DWORD); #ifdef _WIN95_ BOOL LoadSetupAPIDll(VOID); #else VOID ChangeICMSetting(LPCTSTR, LPCTSTR, DWORD); #endif // _WIN95_ // SetupAPI function pointers typedef WINSETUPAPI HKEY (WINAPI* FP_SetupDiOpenDevRegKey)( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Scope, IN DWORD HwProfile, IN DWORD KeyType, IN REGSAM samDesired ); typedef WINSETUPAPI BOOL (WINAPI* FP_SetupDiDestroyDeviceInfoList)( IN HDEVINFO DeviceInfoSet ); typedef WINSETUPAPI BOOL (WINAPI* FP_SetupDiEnumDeviceInfo)( IN HDEVINFO DeviceInfoSet, IN DWORD MemberIndex, OUT PSP_DEVINFO_DATA DeviceInfoData ); #if !defined(_WIN95_) typedef WINSETUPAPI BOOL (WINAPI* FP_SetupDiGetDeviceInstanceId)( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PWSTR DeviceInstanceId, IN DWORD DeviceInstanceIdSize, OUT PDWORD RequiredSize OPTIONAL ); typedef WINSETUPAPI HDEVINFO (WINAPI* FP_SetupDiGetClassDevs)( IN LPGUID ClassGuid, OPTIONAL IN PCWSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ); #else typedef WINSETUPAPI BOOL (WINAPI* FP_SetupDiGetDeviceInstanceId)( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PSTR DeviceInstanceId, IN DWORD DeviceInstanceIdSize, OUT PDWORD RequiredSize OPTIONAL ); typedef WINSETUPAPI HDEVINFO (WINAPI* FP_SetupDiGetClassDevs)( IN LPGUID ClassGuid, OPTIONAL IN PCSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ); #endif HMODULE ghModSetupAPIDll = NULL; FP_SetupDiOpenDevRegKey fpSetupDiOpenDevRegKey = NULL; FP_SetupDiDestroyDeviceInfoList fpSetupDiDestroyDeviceInfoList = NULL; FP_SetupDiEnumDeviceInfo fpSetupDiEnumDeviceInfo = NULL; FP_SetupDiGetDeviceInstanceId fpSetupDiGetDeviceInstanceId = NULL; FP_SetupDiGetClassDevs fpSetupDiGetClassDevs = NULL; // Predefined profiles in order - INF file has 1-based index into this list TCHAR* gszDispProfiles[] = { __TEXT("mnB22G15.icm"), // 1 __TEXT("mnB22G18.icm"), // 2 __TEXT("mnB22G21.icm"), // 3 __TEXT("mnEBUG15.icm"), // 4 __TEXT("mnEBUG18.icm"), // 5 __TEXT("mnEBUG21.icm"), // 6 __TEXT("mnP22G15.icm"), // 7 __TEXT("mnP22G18.icm"), // 8 __TEXT("mnP22G21.icm"), // 9 __TEXT("Diamond Compatible 9300K G2.2.icm"), // 10 __TEXT("Hitachi Compatible 9300K G2.2.icm"), // 11 __TEXT("NEC Compatible 9300K G2.2.icm"), // 12 __TEXT("Trinitron Compatible 9300K G2.2.icm"), // 13 }; TCHAR* gpszClasses[] = { // different profile classes __TEXT("mntr"), // 0 __TEXT("prtr"), // 1 __TEXT("scnr"), // 2 __TEXT("link"), // 3 __TEXT("abst"), // 4 __TEXT("spac"), // 5 __TEXT("nmcl") // 6 }; #define INDEX_CLASS_MONITOR 0 #define INDEX_CLASS_PRINTER 1 #define INDEX_CLASS_SCANNER 2 #define INDEX_CLASS_LINK 3 #define INDEX_CLASS_ABSTRACT 4 #define INDEX_CLASS_COLORSPACE 5 #define INDEX_CLASS_NAMED 6 /* * GetColorDirectory * Function: * These are the ANSI & Unicode wrappers for InternalGetColorDirectory. * Please see InternalGetColorDirectory for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the path * to the color directory is requested * pBuffer - pointer to buffer to receive pathname * pdwSize - pointer to size of buffer. On return it has size of * buffer needed if failure, and used on success * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI GetColorDirectoryA( PCSTR pMachineName, PSTR pBuffer, PDWORD pdwSize ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwBuffer = NULL; // Unicode color directory path DWORD dwSize; // size of Unicode buffer DWORD dwErr = 0; // error code BOOL rc = TRUE; // return code TRACEAPI((__TEXT("GetColorDirectoryA\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize))) { WARNING((__TEXT("Invalid parameter to GetColorDirectory\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; dwSize = *pdwSize * sizeof(WCHAR); // Create a buffer to get Unicode directory from system if (pBuffer && dwSize) { pwBuffer = (PWSTR)MemAlloc(dwSize); if (!pwBuffer) { WARNING((__TEXT("Error allocating memory for Unicode string\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndGetColorDirectoryA; } } rc = rc && InternalGetColorDirectory(pwszMachineName, pwBuffer, &dwSize); *pdwSize = dwSize / sizeof(WCHAR); // Convert Unicode path to Ansi if (pwBuffer) { rc = rc && ConvertToAnsi(pwBuffer, &pBuffer, FALSE); } EndGetColorDirectoryA: if (pwszMachineName) { MemFree(pwszMachineName); } if (pwBuffer) { MemFree(pwBuffer); } return rc; } BOOL WINAPI GetColorDirectoryW( PCWSTR pMachineName, PWSTR pBuffer, PDWORD pdwSize ) { TRACEAPI((__TEXT("GetColorDirectoryW\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalGetColorDirectory(pMachineName, pBuffer, pdwSize); } #else // Windows 95 versions BOOL WINAPI GetColorDirectoryA( PCSTR pMachineName, PSTR pBuffer, PDWORD pdwSize ) { TRACEAPI((__TEXT("GetColorDirectoryA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalGetColorDirectory(pMachineName, pBuffer, pdwSize); } BOOL WINAPI GetColorDirectoryW( PCWSTR pMachineName, PWSTR pBuffer, PDWORD pdwSize ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszBuffer = NULL; // Ansi color directory path DWORD dwSize; // size of Ansi buffer BOOL rc = TRUE; // return code TRACEAPI((__TEXT("GetColorDirectoryW\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize))) { WARNING((__TEXT("Invalid parameter to GetColorDirectory\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Create a buffer to get Ansi directory from system dwSize = *pdwSize / sizeof(WCHAR); if (pBuffer && dwSize) { pszBuffer = (PSTR)MemAlloc(dwSize); if (!pszBuffer) { WARNING((__TEXT("Error allocating memory for Ansi string\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndGetColorDirectoryW; } } rc = rc && InternalGetColorDirectory(pszMachineName, pszBuffer, &dwSize); *pdwSize = dwSize * sizeof(WCHAR); // Convert Ansi path to Unicode if (pszBuffer) { rc = rc && ConvertToUnicode(pszBuffer, &pBuffer, FALSE); } EndGetColorDirectoryW: if (pszMachineName) { MemFree(pszMachineName); } if (pszBuffer) { MemFree(pszBuffer); } return rc; } #endif // ! UNICODE /* * InstallColorProfile * Function: * These are the ANSI & Unicode wrappers for InternalInstallColorProfile. * Please see InternalInstallColorProfile for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the profile * should be installed. NULL implies local * pProfileName - pointer to filename of profile to install * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI InstallColorProfileA( PCSTR pMachineName, PCSTR pProfileName ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszProfileName = NULL; // Unicode profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("InstallColorProfileA\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to InstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // Convert profile name to Unicode rc = rc && ConvertToUnicode(pProfileName, &pwszProfileName, TRUE); // Call the internal Unicode function rc = rc && InternalInstallColorProfile(pwszMachineName, pwszProfileName); // Free memory before leaving if (pwszProfileName) { MemFree(pwszProfileName); } if (pwszMachineName) { MemFree(pwszMachineName); } return rc; } BOOL WINAPI InstallColorProfileW( PCWSTR pMachineName, PCWSTR pProfileName ) { TRACEAPI((__TEXT("InstallColorProfileW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalInstallColorProfile(pMachineName, pProfileName); } #else // Windows 95 versions BOOL WINAPI InstallColorProfileA( PCSTR pMachineName, PCSTR pProfileName ) { TRACEAPI((__TEXT("InstallColorProfileA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalInstallColorProfile(pMachineName, pProfileName); } BOOL WINAPI InstallColorProfileW( PCWSTR pMachineName, PCWSTR pProfileName ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszProfileName = NULL; // Ansi profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("InstallColorProfileW\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to InstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Convert profile name to Ansi rc = rc && ConvertToAnsi(pProfileName, &pszProfileName, TRUE); // Call the internal Ansi function rc = rc && InternalInstallColorProfile(pszMachineName, pszProfileName); // Free memory before leaving if (pszProfileName) { MemFree(pszProfileName); } if (pszMachineName) { MemFree(pszMachineName); } return rc; } #endif // ! UNICODE /* * UninstallColorProfile * Function: * These are the ANSI & Unicode wrappers for InternalUninstallColorProfile. * Please see InternalUninstallColorProfile for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the profile * should be uninstalled. NULL implies local * pProfileName - pointer to filename of profile to uninstall * bDelete - TRUE if profile should be deleted in disk * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI UninstallColorProfileA( PCSTR pMachineName, PCSTR pProfileName, BOOL bDelete ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszProfileName = NULL; // Unicode profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("UninstallColorProfileA\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to UninstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // Convert profile name to Unicode rc = rc && ConvertToUnicode(pProfileName, &pwszProfileName, TRUE); // Call the internal Unicode function rc = rc && InternalUninstallColorProfile(pwszMachineName, pwszProfileName, bDelete); // Free memory before leaving if (pwszProfileName) { MemFree(pwszProfileName); } if (pwszMachineName) { MemFree(pwszMachineName); } return rc; } BOOL WINAPI UninstallColorProfileW( PCWSTR pMachineName, PCWSTR pProfileName, BOOL bDelete ) { TRACEAPI((__TEXT("UninstallColorProfileW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalUninstallColorProfile(pMachineName, pProfileName, bDelete); } #else // Windows 95 versions BOOL WINAPI UninstallColorProfileA( PCSTR pMachineName, PCSTR pProfileName, BOOL bDelete ) { TRACEAPI((__TEXT("UninstallColorProfileA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalUninstallColorProfile(pMachineName, pProfileName, bDelete); } BOOL WINAPI UninstallColorProfileW( PCWSTR pMachineName, PCWSTR pProfileName, BOOL bDelete ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszProfileName = NULL; // Ansi profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("UninstallColorProfileW\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to UninstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Convert profile name to Ansi rc = rc && ConvertToAnsi(pProfileName, &pszProfileName, TRUE); // Call the internal Ansi function rc = rc && InternalUninstallColorProfile(pszMachineName, pszProfileName, bDelete); // Free memory before leaving if (pszProfileName) { MemFree(pszProfileName); } if (pszMachineName) { MemFree(pszMachineName); } return rc; } #endif // ! UNICODE /* * AssociateColorProfileWithDevice * Function: * These are the ANSI & Unicode wrappers for * InternalAssociateColorProfileWithDevice. Please see * InternalAssociateColorProfileWithDevice for more details * on this function. * Arguments: * pMachineName - name identifying machine. NULL implies local * pProfileName - pointer to profile to associate * pDeviceName - pointer to device name * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI AssociateColorProfileWithDeviceA( PCSTR pMachineName, PCSTR pProfileName, PCSTR pDeviceName ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszProfileName = NULL; // Unicode profile name PWSTR pwszDeviceName = NULL; // Unicode device name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("AssociateColorProfileWithDeviceA\n"))); // Validate parameters before we touch them if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to AssociateColorProfileWithDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // Convert profile name to Unicode rc = rc && ConvertToUnicode(pProfileName, &pwszProfileName, TRUE); // Convert device name to Unicode rc = rc && ConvertToUnicode(pDeviceName, &pwszDeviceName, TRUE); // Call the internal Unicode function rc = rc && InternalAssociateColorProfileWithDevice(pwszMachineName, pwszProfileName, pwszDeviceName); // Free memory before leaving if (pwszProfileName) { MemFree(pwszProfileName); } if (pwszMachineName) { MemFree(pwszMachineName); } if (pwszDeviceName) { MemFree(pwszDeviceName); } return rc; } BOOL WINAPI AssociateColorProfileWithDeviceW( PCWSTR pMachineName, PCWSTR pProfileName, PCWSTR pDeviceName ) { TRACEAPI((__TEXT("AssociateColorProfileWithDeviceW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalAssociateColorProfileWithDevice(pMachineName, pProfileName, pDeviceName); } #else // Windows 95 versions BOOL WINAPI AssociateColorProfileWithDeviceA( PCSTR pMachineName, PCSTR pProfileName, PCSTR pDeviceName ) { TRACEAPI((__TEXT("AssociateColorProfileWithDeviceA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalAssociateColorProfileWithDevice(pMachineName, pProfileName, pDeviceName); } BOOL WINAPI AssociateColorProfileWithDeviceW( PCWSTR pMachineName, PCWSTR pProfileName, PCWSTR pDeviceName ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszProfileName = NULL; // Ansi profile name PSTR pszDeviceName = NULL; // Ansi device name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("AssociateColorProfileWithDeviceW\n"))); // Validate parameters before we touch them if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to AssociateColorProfileWithDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Convert profile name to Ansi rc = rc && ConvertToAnsi(pProfileName, &pszProfileName, TRUE); // Convert device name to Ansi rc = rc && ConvertToAnsi(pDeviceName, &pszDeviceName, TRUE); // Call the internal Ansi function rc = rc && InternalAssociateColorProfileWithDevice(pszMachineName, pszProfileName, pszDeviceName); // Free memory before leaving if (pszProfileName) { MemFree(pszProfileName); } if (pszMachineName) { MemFree(pszMachineName); } if (pszDeviceName) { MemFree(pszDeviceName); } return rc; } #endif // ! UNICODE /* * DisassociateColorProfileFromDevice * Function: * These are the ANSI & Unicode wrappers for * InternalDisassociateColorProfileFromDevice. Please see * InternalDisassociateColorProfileFromDevice for more details * on this function. * Arguments: * pMachineName - name identifying machine. NULL implies local * pProfileName - pointer to profile to disassiciate * pDeviceName - pointer to device name * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI DisassociateColorProfileFromDeviceA( PCSTR pMachineName, PCSTR pProfileName, PCSTR pDeviceName ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszProfileName = NULL; // Unicode profile name PWSTR pwszDeviceName = NULL; // Unicode device name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("DisassociateColorProfileWithDeviceA\n"))); // Validate parameters before we touch them if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to DisassociateColorProfileFromDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // Convert profile name to Unicode rc = rc && ConvertToUnicode(pProfileName, &pwszProfileName, TRUE); // Convert device name to Unicode rc = rc && ConvertToUnicode(pDeviceName, &pwszDeviceName, TRUE); // Call the internal Unicode function rc = rc && InternalDisassociateColorProfileFromDevice(pwszMachineName, pwszProfileName, pwszDeviceName); // Free memory before leaving if (pwszProfileName) { MemFree(pwszProfileName); } if (pwszMachineName) { MemFree(pwszMachineName); } if (pwszDeviceName) { MemFree(pwszDeviceName); } return rc; } BOOL WINAPI DisassociateColorProfileFromDeviceW( PCWSTR pMachineName, PCWSTR pProfileName, PCWSTR pDeviceName ) { TRACEAPI((__TEXT("DisassociateColorProfileWithDeviceW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalDisassociateColorProfileFromDevice(pMachineName, pProfileName, pDeviceName); } #else // Windows 95 versions BOOL WINAPI DisassociateColorProfileFromDeviceA( PCSTR pMachineName, PCSTR pProfileName, PCSTR pDeviceName ) { TRACEAPI((__TEXT("DisassociateColorProfileWithDeviceA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalDisassociateColorProfileFromDevice(pMachineName, pProfileName, pDeviceName); } BOOL WINAPI DisassociateColorProfileFromDeviceW( PCWSTR pMachineName, PCWSTR pProfileName, PCWSTR pDeviceName ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszProfileName = NULL; // Ansi profile name PSTR pszDeviceName = NULL; // Ansi device name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("DisassociateColorProfileWithDeviceW\n"))); // Validate parameters before we touch them if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to AssociateColorProfileWithDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Convert profile name to Ansi rc = rc && ConvertToAnsi(pProfileName, &pszProfileName, TRUE); // Convert device name to Ansi rc = rc && ConvertToAnsi(pDeviceName, &pszDeviceName, TRUE); // Call the internal Ansi function rc = rc && InternalDisassociateColorProfileFromDevice(pszMachineName, pszProfileName, pszDeviceName); // Free memory before leaving if (pszProfileName) { MemFree(pszProfileName); } if (pszMachineName) { MemFree(pszMachineName); } if (pszDeviceName) { MemFree(pszDeviceName); } return rc; } #endif // ! UNICODE /* * EnumColorProfiles * Function: * These are the ANSI & Unicode wrappers for InternalEnumColorProfile. * Please see InternalEnumColorProfile for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the enumeration * needs to be done * pEnumRecord - pointer to enumeration criteria * pBuffer - pointer to buffer to receive result, can be NULL * pdwSize - pointer to buffer size. On return it is actual number * of bytes copied/needed. * pnProfiles - pointer to DWORD. On return, it is number of profiles * copied to pBuffer. * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI EnumColorProfilesA( PCSTR pMachineName, PENUMTYPEA pEnumRecord, PBYTE pBuffer, PDWORD pdwSize, PDWORD pnProfiles ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszDeviceName = NULL; // Unicode device name PSTR pAnsiDeviceName = NULL; // incoming Ansi device name PWSTR pwBuffer = NULL; // buffer to receive data PWSTR pwTempBuffer = NULL; // temporary pointer to buffer DWORD dwSize; // size of buffer DWORD dwSizeOfStruct; // size of ENUMTYPE structure DWORD dwVersion; // ENUMTYPE structure version BOOL rc = TRUE; // return code TRACEAPI((__TEXT("EnumColorProfilesA\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize)) || !pEnumRecord || IsBadReadPtr(pEnumRecord, sizeof(DWORD) * 3)) // probe until ENUMTYPE.dwFields { ParameterError_EnumColorProfilesA: WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Check structure size based on its version. dwSizeOfStruct = pEnumRecord->dwSize; dwVersion = pEnumRecord->dwVersion; if (dwVersion >= ENUM_TYPE_VERSION) { if (dwSizeOfStruct < sizeof(ENUMTYPE)) goto ParameterError_EnumColorProfilesA; } else if (dwVersion == 0x0200) { if (dwSizeOfStruct < sizeof(ENUMTYPE) - sizeof(DWORD)) goto ParameterError_EnumColorProfilesA; // Version 2 should not have ET_DEVICECLASS bit if (pEnumRecord->dwFields & ET_DEVICECLASS) goto ParameterError_EnumColorProfilesA; WARNING((__TEXT("Old version ENUMTYPE to EnumColorProfiles\n"))); } else { goto ParameterError_EnumColorProfilesA; } if (IsBadReadPtr(pEnumRecord, dwSizeOfStruct)) { goto ParameterError_EnumColorProfilesA; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // If device name is specified, convert it to Unicode if (pEnumRecord->dwFields & ET_DEVICENAME) { if (!pEnumRecord->pDeviceName) { WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); rc = FALSE; goto EndEnumColorProfilesA; } // Convert device name to Unicode pAnsiDeviceName = (PSTR)pEnumRecord->pDeviceName; rc = rc && ConvertToUnicode(pAnsiDeviceName, &pwszDeviceName, TRUE); pEnumRecord->pDeviceName = (PSTR)pwszDeviceName; } dwSize = *pdwSize * sizeof(WCHAR); // Allocate buffer of suitable size if (pBuffer && dwSize) { pwBuffer = MemAlloc(dwSize); if (!pwBuffer) { WARNING((__TEXT("Error allocating memory for Unicode buffer\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndEnumColorProfilesA; } } // Call the internal Unicode function rc = rc && InternalEnumColorProfiles(pwszMachineName, (PENUMTYPEW)pEnumRecord, (PBYTE)pwBuffer, &dwSize, pnProfiles); if (pwBuffer && rc) { pwTempBuffer = pwBuffer; while (*pwTempBuffer) { rc = rc && ConvertToAnsi(pwTempBuffer, (PSTR*)&pBuffer, FALSE); pwTempBuffer += lstrlenW(pwTempBuffer) + 1; pBuffer += (lstrlenA((PSTR)pBuffer) + 1) * sizeof(char); } *((PSTR)pBuffer) = '\0'; } *pdwSize = dwSize / sizeof(WCHAR); EndEnumColorProfilesA: if (pwszMachineName) { MemFree(pwszMachineName); } if (pAnsiDeviceName) { ASSERT(pEnumRecord->pDeviceName != NULL); MemFree((PBYTE)pEnumRecord->pDeviceName); pEnumRecord->pDeviceName = (PCSTR)pAnsiDeviceName; } if (pwBuffer) { MemFree(pwBuffer); } return rc; } BOOL WINAPI EnumColorProfilesW( PCWSTR pMachineName, PENUMTYPEW pEnumRecord, PBYTE pBuffer, PDWORD pdwSize, PDWORD pnProfiles ) { TRACEAPI((__TEXT("EnumColorProfilesW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalEnumColorProfiles(pMachineName, pEnumRecord, pBuffer, pdwSize, pnProfiles); } #else // Windows 95 versions BOOL WINAPI EnumColorProfilesA( PCSTR pMachineName, PENUMTYPEA pEnumRecord, PBYTE pBuffer, PDWORD pdwSize, PDWORD pnProfiles ) { TRACEAPI((__TEXT("EnumColorProfilesA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalEnumColorProfiles(pMachineName, pEnumRecord, pBuffer, pdwSize, pnProfiles); } BOOL WINAPI EnumColorProfilesW( PCWSTR pMachineName, PENUMTYPEW pEnumRecord, PBYTE pBuffer, PDWORD pdwSize, PDWORD pnProfiles ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszDeviceName = NULL; // Ansi device name PWSTR pUnicodeDeviceName = NULL;// incoming Unicode device name PSTR pszBuffer = NULL; // buffer to receive data PSTR pszTempBuffer = NULL; // temporary pointer to buffer DWORD dwSize; // size of buffer DWORD dwSizeOfStruct; // size of ENUMTYPE structure DWORD dwVersion; // ENUMTYPE structure version BOOL rc = TRUE; // return code TRACEAPI((__TEXT("EnumColorProfilesW\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize)) || !pEnumRecord || IsBadReadPtr(pEnumRecord, sizeof(DWORD) * 3)) // probe until ENUMTYPE.dwFields { ParameterError_EnumColorProfilesW: WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Check structure size based on its version. dwSizeOfStruct = pEnumRecord->dwSize; dwVersion = pEnumRecord->dwVersion; if (dwVersion >= ENUM_TYPE_VERSION) { if (dwSizeOfStruct < sizeof(ENUMTYPE)) goto ParameterError_EnumColorProfilesW; } else if (dwVersion == 0x0200) { if (dwSizeOfStruct < sizeof(ENUMTYPE) - sizeof(DWORD)) goto ParameterError_EnumColorProfilesW; // Version 2 should not have ET_DEVICECLASS bit if (pEnumRecord->dwFields & ET_DEVICECLASS) goto ParameterError_EnumColorProfilesW; WARNING((__TEXT("Old version ENUMTYPE to EnumColorProfiles\n"))); } else { goto ParameterError_EnumColorProfilesW; } if (IsBadReadPtr(pEnumRecord, dwSizeOfStruct)) { goto ParameterError_EnumColorProfilesW; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } // If device name is specified, convert it to Unicode if (pEnumRecord->dwFields & ET_DEVICENAME) { if (!pEnumRecord->pDeviceName) { WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); goto EndEnumColorProfilesW; } // Convert device name to Ansi pUnicodeDeviceName = (PWSTR)pEnumRecord->pDeviceName; rc = rc && ConvertToAnsi(pUnicodeDeviceName, &pszDeviceName, TRUE); pEnumRecord->pDeviceName = (PCWSTR)pszDeviceName; } dwSize = *pdwSize / sizeof(WCHAR); // Allocate buffer of suitable size if (pBuffer && dwSize) { pszBuffer = MemAlloc(dwSize); if (!pszBuffer) { WARNING((__TEXT("Error allocating memory for Ansi buffer\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndEnumColorProfilesW; } } // Call the internal Ansi function rc = rc && InternalEnumColorProfiles(pszMachineName, (PENUMTYPEA)pEnumRecord, (PBYTE)pszBuffer, &dwSize, pnProfiles); if (pszBuffer && rc) { pszTempBuffer = pszBuffer; while (*pszTempBuffer) { rc = rc && ConvertToUnicode(pszTempBuffer, (PWSTR*)&pBuffer, FALSE); pszTempBuffer += lstrlenA(pszTempBuffer) + 1; pBuffer += (lstrlenW((PWSTR)pBuffer) + 1) * sizeof(WCHAR); } *((PWSTR)pBuffer) = '\0'; } *pdwSize = dwSize * sizeof(WCHAR); EndEnumColorProfilesW: if (pszMachineName) { MemFree(pszMachineName); } if (pUnicodeDeviceName) { ASSERT(pEnumRecord->pDeviceName != NULL); MemFree((PSTR)pEnumRecord->pDeviceName); pEnumRecord->pDeviceName = (PCWSTR)pUnicodeDeviceName; } if (pszBuffer) { MemFree(pszBuffer); } return rc; } #endif // ! UNICODE /* * SetStandardColorSpaceProfile * Function: * These are the ANSI & Unicode wrappers for InternalSetSCSProfile. * Please see InternalSetSCSProfile for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the standard color * space profile should be registered * dwSCS - ID for the standard color space * pProfileName - pointer to profile filename * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI SetStandardColorSpaceProfileA( PCSTR pMachineName, DWORD dwSCS, PCSTR pProfileName ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwszProfileName = NULL; // Unicode profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("SetStandardColorSpaceProfileA\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to SetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; // Convert profile name to Unicode rc = rc && ConvertToUnicode(pProfileName, &pwszProfileName, TRUE); // Call the internal Unicode function rc = rc && InternalSetSCSProfile(pwszMachineName, dwSCS, pwszProfileName); // Free memory before leaving if (pwszProfileName) { MemFree(pwszProfileName); } if (pwszMachineName) { MemFree(pwszMachineName); } return rc; } BOOL WINAPI SetStandardColorSpaceProfileW( PCWSTR pMachineName, DWORD dwSCS, PCWSTR pProfileName ) { TRACEAPI((__TEXT("SetStandardColorSpaceProfileW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalSetSCSProfile(pMachineName, dwSCS, pProfileName); } #else // Windows 95 versions BOOL WINAPI SetStandardColorSpaceProfileA( PCSTR pMachineName, DWORD dwSCS, PCSTR pProfileName ) { TRACEAPI((__TEXT("SetStandardColorSpaceProfileA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalSetSCSProfile(pMachineName, dwSCS, pProfileName); } BOOL WINAPI SetStandardColorSpaceProfileW( PCWSTR pMachineName, DWORD dwSCS, PCWSTR pProfileName ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszProfileName = NULL; // Ansi profile name BOOL rc = TRUE; // return code TRACEAPI((__TEXT("SetStandardColorSpaceProfileW\n"))); // Validate parameters before we touch them if (!pProfileName) { WARNING((__TEXT("Invalid parameter to SetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; // Convert profile name to Ansi rc = rc && ConvertToAnsi(pProfileName, &pszProfileName, TRUE); // Call the internal Ansi function rc = rc && InternalSetSCSProfile(pszMachineName, dwSCS, pszProfileName); // Free memory before leaving if (pszProfileName) { MemFree(pszProfileName); } if (pszMachineName) { MemFree(pszMachineName); } return rc; } #endif // ! UNICODE /* * GetStandardColorSpaceProfile * Function: * These are the ANSI & Unicode wrappers for InternalGetSCSProfile. * Please see InternalGetSCSProfile for more details on this * function. * Arguments: * pMachineName - name identifying machine on which the standard color * space profile should be queried * dwSCS - ID for the standard color space * pBuffer - pointer to buffer to receive profile filename * pdwSize - pointer to DWORD specifying size of buffer. On return * it has size needed/used * Returns: * TRUE if successful, NULL otherwise */ #ifdef UNICODE // Windows NT versions BOOL WINAPI GetStandardColorSpaceProfileA( PCSTR pMachineName, DWORD dwSCS, PSTR pBuffer, PDWORD pdwSize ) { PWSTR pwszMachineName = NULL; // Unicode machine name PWSTR pwBuffer = NULL; // Unicode color directory path DWORD dwSize; // size of Unicode buffer BOOL rc = TRUE; // return code TRACEAPI((__TEXT("GetStandardColorSpaceProfileA\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize))) { WARNING((__TEXT("Invalid parameter to GetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Unicode if (pMachineName) { rc = ConvertToUnicode(pMachineName, &pwszMachineName, TRUE); } else pwszMachineName = NULL; dwSize = *pdwSize * sizeof(WCHAR); // Create a buffer to get Unicode filename from system if (pBuffer && dwSize) { pwBuffer = (PWSTR)MemAlloc(dwSize); if (!pwBuffer) { WARNING((__TEXT("Error allocating memory for Unicode string\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndGetSCSProfileA; } } rc = rc && InternalGetSCSProfile(pwszMachineName, dwSCS, pwBuffer, &dwSize); *pdwSize = dwSize / sizeof(WCHAR); // Convert Unicode path to Ansi if (pwBuffer) { rc = rc && ConvertToAnsi(pwBuffer, &pBuffer, FALSE); } EndGetSCSProfileA: if (pwszMachineName) { MemFree(pwszMachineName); } if (pwBuffer) { MemFree(pwBuffer); } return rc; } BOOL WINAPI GetStandardColorSpaceProfileW( PCWSTR pMachineName, DWORD dwSCS, PWSTR pBuffer, PDWORD pdwSize ) { TRACEAPI((__TEXT("GetStandardColorSpaceProfileW\n"))); // Internal function is Unicode in Windows NT, call it directly. return InternalGetSCSProfile(pMachineName, dwSCS, pBuffer, pdwSize); } #else // Windows 95 versions BOOL WINAPI GetStandardColorSpaceProfileA( PCSTR pMachineName, DWORD dwSCS, PSTR pBuffer, PDWORD pdwSize ) { TRACEAPI((__TEXT("GetStandardColorSpaceProfileA\n"))); // Internal function is ANSI in Windows 95, call it directly. return InternalGetSCSProfile(pMachineName, dwSCS, pBuffer, pdwSize); } BOOL WINAPI GetStandardColorSpaceProfileW( PCWSTR pMachineName, DWORD dwSCS, PWSTR pBuffer, PDWORD pdwSize ) { PSTR pszMachineName = NULL; // Ansi machine name PSTR pszBuffer = NULL; // Ansi color directory path DWORD dwSize; // size of Ansi buffer BOOL rc = TRUE; // return code TRACEAPI((__TEXT("GetStandardColorSpaceProfileW\n"))); // Validate parameters before we touch them if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize))) { WARNING((__TEXT("Invalid parameter to GetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Convert machine name to Ansi if (pMachineName) { rc = ConvertToAnsi(pMachineName, &pszMachineName, TRUE); } else pszMachineName = NULL; dwSize = *pdwSize / sizeof(WCHAR); // Create a buffer to get Ansi profilename from system if (pBuffer && dwSize) { pszBuffer = (PSTR)MemAlloc(dwSize); if (!pBuffer) { WARNING((__TEXT("Error allocating memory for Ansi string\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FALSE; goto EndGetSCSProfileW; } } rc = rc && InternalGetSCSProfile(pszMachineName, dwSCS, pszBuffer, &dwSize); *pdwSize = dwSize * sizeof(WCHAR); // Convert Ansi path to Unicode if (pszBuffer) { rc = rc && ConvertToUnicode(pszBuffer, &pBuffer, FALSE); } EndGetSCSProfileW: if (pszMachineName) { MemFree(pszMachineName); } if (pszBuffer) { MemFree(pszBuffer); } return rc; } #endif // ! UNICODE /* * GenerateCopyFilePaths * Function: * This function is called by the Windows NT spooler to find the * directories from which color profiles should be picked up and copied * to. This is useful if the locations are version or processor * architecture dependent. As color profiles depend on neither, we don't * have to do anything, but have to export this function. * Arguments: * don't care * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI GenerateCopyFilePaths( LPCWSTR pszPrinterName, LPCWSTR pszDirectory, LPBYTE pSplClientInfo, DWORD dwLevel, LPWSTR pszSourceDir, LPDWORD pcchSourceDirSize, LPWSTR pszTargetDir, LPDWORD pcchTargetDirSize, DWORD dwFlags ) { TRACEAPI((__TEXT("GenerateCopyFilePaths\n"))); return ERROR_SUCCESS; } /* * SpoolerCopyFileEvent * Function: * This function is called by the Windows NT spooler when one of the * following events happens: * 1. When someone does a SetPrinterDataEx of the CopyFiles section * 2. When a printer connection is made * 3. When files for a printer connection get updated * 4. When a printer is deleted * Arguments: * pszPrinterName - friendly name of printer * pszKey - "CopyFiles\ICM" for us * dwCopyFileEvent - reason for calling * Returns: * TRUE if successful, NULL otherwise */ BOOL WINAPI SpoolerCopyFileEvent( LPWSTR pszPrinterName, LPWSTR pszKey, DWORD dwCopyFileEvent ) { PTSTR pProfileList, pTemp, pBuffer; DWORD dwSize; BOOL bRc = FALSE; TCHAR szPath[MAX_PATH]; TRACEAPI((__TEXT("SpoolerCopyFileEvent\n"))); switch (dwCopyFileEvent) { case COPYFILE_EVENT_SET_PRINTER_DATAEX: // When associating profiles with printer connections, we copy // the files to the remote machine, and then do a SePrinterDataEx. // This causes this event to be generated on the remote machine. We // use this to install the profile. This is not needed after we make // our APIs remotable // Fall thru' TERSE((__TEXT("SetPrinterDataEx event\n"))); case COPYFILE_EVENT_ADD_PRINTER_CONNECTION: case COPYFILE_EVENT_FILES_CHANGED: // This event is generated when a printer connection is added or // associated profiles have changed. Install all the profiles in // the client machine now. #ifdef DBG if (dwCopyFileEvent == COPYFILE_EVENT_ADD_PRINTER_CONNECTION) { WARNING((__TEXT("AddPrinterConnection Event\n"))); } else if (dwCopyFileEvent == COPYFILE_EVENT_FILES_CHANGED) { WARNING((__TEXT("FilesChanged Event\n"))); } #endif dwSize = 0; if (GetDeviceData((PTSTR)pszPrinterName, CLASS_PRINTER, DEVICE_PROFILE_DATA, (PVOID*)&pProfileList, &dwSize, TRUE)) { dwSize = sizeof(szPath); if (InternalGetColorDirectory(NULL, szPath, &dwSize)) { lstrcat(szPath, gszBackslash); pBuffer = szPath + lstrlen(szPath); pTemp = pProfileList; while (*pTemp) { lstrcpy(pBuffer, pTemp); InstallColorProfile(NULL, szPath); pTemp += lstrlen(pTemp) + 1; } } MemFree(pProfileList); } break; case COPYFILE_EVENT_DELETE_PRINTER: // This event is generated when a printer is about to be deleted. // Get all profiles associated with the printer and disassociate // them now. TERSE((__TEXT("DeletePrinterDataEx Event\n"))); dwSize = 0; if (GetDeviceData((PTSTR)pszPrinterName, CLASS_PRINTER, DEVICE_PROFILE_DATA, (PVOID*)&pProfileList, &dwSize, TRUE)) { pTemp = pProfileList; while (*pTemp) { DisassociateColorProfileFromDevice(NULL, pTemp, (PTSTR)pszPrinterName); pTemp += lstrlen(pTemp) + 1; } MemFree(pProfileList); } break; } bRc = TRUE; return bRc; } /** Internal Functions **/ /* * InternalGetColorDirectory * Function: * This function returns the path to the color directory on the machine * specified. * associations should be removed before calling this function. It also * optionally deletes the file if the profile was successfully * uninstalled. * Arguments: * pMachineName - name identifying machine on which the path * to the color directory is requested * pBuffer - pointer to buffer to receive pathname * pdwSize - pointer to size of buffer. On return it has size of * buffer needed if failure, and used on success * Returns: * TRUE if successful, FALSE otherwise */ BOOL InternalGetColorDirectory( LPCTSTR pMachineName, PTSTR pBuffer, DWORD* pdwSize ) { DWORD dwBufLen = *pdwSize; // size supplied BOOL rc = FALSE; // return value // Get the printer driver directory #if !defined(_WIN95_) DWORD dwNeeded; // size needed if (!pBuffer && pdwSize && !IsBadWritePtr(pdwSize, sizeof(DWORD))) { *pdwSize = 0; } if (GetPrinterDriverDirectory((PTSTR)pMachineName, NULL, 1, (PBYTE)pBuffer, *pdwSize, pdwSize)) { // This API returns the print$ path appended with the environment // directory. e.g. c:\winnt\system32\spool\drivers\w32x86. So we need // to go back one step and then append the color directory. PWSTR pDriverDir; pDriverDir = GetFilenameFromPath(pBuffer); ASSERT(pDriverDir != NULL); *pdwSize -= lstrlen(pDriverDir) * sizeof(WCHAR); *pDriverDir = '\0'; // Calculate size of buffer needed to append color directory dwNeeded = *pdwSize + lstrlen(gszColorDir) * sizeof(WCHAR); if (pBuffer[lstrlen(pBuffer) - 1] != '\\') { dwNeeded += sizeof(WCHAR); } // Update size needed *pdwSize = dwNeeded; // If supplied buffer is big enough, append our stuff if (dwNeeded <= dwBufLen) { if (pBuffer[lstrlen(pBuffer) - 1] != '\\') { lstrcat(pBuffer, gszBackslash); } lstrcat(pBuffer, gszColorDir); rc = TRUE; } else { WARNING((__TEXT("Input buffer to GetColorDirectory not big enough\n"))); SetLastError(ERROR_INSUFFICIENT_BUFFER); } } else if (GetLastError() == ERROR_INVALID_USER_BUFFER) { // Spooler sets this error if buffer is NULL. Map it to our error SetLastError(ERROR_INSUFFICIENT_BUFFER); } else if (GetLastError() == RPC_S_SERVER_UNAVAILABLE) { TCHAR achTempPath[MAX_PATH * 2]; // Make sure enough path space. // Spooler service is not running. Use hardcoded path if (GetSystemDirectory(achTempPath, MAX_PATH) != 0) { _tcscat(achTempPath, TEXT("\\spool\\drivers\\color")); *pdwSize = wcslen(achTempPath) + 1; if (pBuffer && (*pdwSize <= dwBufLen)) { _tcscpy(pBuffer, achTempPath); rc = TRUE; } else { WARNING((__TEXT("Input buffer to GetColorDirectory not big enough\n"))); SetLastError(ERROR_INSUFFICIENT_BUFFER); } } } #else HKEY hkSetup; // registry key DWORD dwErr; // error code // Only local color directory query is allowed in Memphis if (pMachineName) { WARNING((__TEXT("Remote color directory query, failing...\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // On Memphis, get this information from the setup section in the registry. // The reason we don't call GetPrinterDriverDirectory is that when we call // this function from GDI 16, it tries to go back into 16-bit mode and // deadlocks on the Win16 lock. if ((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszSetupPath, &hkSetup)) == ERROR_SUCCESS) { if ((dwErr = RegQueryValueEx(hkSetup, gszICMDir, 0, NULL, (PBYTE)pBuffer, pdwSize)) == ERROR_SUCCESS) { rc = TRUE; } RegCloseKey(hkSetup); } if (!rc) { // Make error codes consistent if (dwErr == ERROR_MORE_DATA) { dwErr = ERROR_INSUFFICIENT_BUFFER; } WARNING((__TEXT("Error getting color directory: %d\n"), dwErr)); SetLastError(dwErr); } // RegQueryValueEx returns TRUE even if the calling buffer is NULL. Our API // is supposed to return FALSE. Check for this case. if (pBuffer == NULL && rc) { SetLastError(ERROR_INSUFFICIENT_BUFFER); rc = FALSE; } #endif // !defined(_WIN95_) return rc; } /* * InternalInstallColorProfile * Function: * This function installs a given color profile on a a given machine. * Arguments: * pMachineName - name identifying machine on which the profile * should be uninstalled. NULL implies local * pProfileName - Fully qualified pathname of profile to uninstall * bDelete - TRUE if profile should be deleted in disk * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local install is supported, so pMachineName should * be NULL. */ BOOL InternalInstallColorProfile( LPCTSTR pMachineName, LPCTSTR pProfileName ) { PROFILEHEADER header; // profile header REGDATA regData; // for storing registry data about profile HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkDevice = NULL; // key to ICM device branch in registry DWORD dwSize; // size of registry data for profile DWORD dwErr; // error code BOOL rc = FALSE; // return code PTSTR pFilename; // profile name without path TCHAR szDest[MAX_PATH]; // destination path for profile TCHAR szClass[5]; // profile class BOOL FileExist; // profile already in directory? BOOL RegExist; // profile in registry? // Validate parameters if (!pProfileName) { WARNING((__TEXT("Invalid parameter to InstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local installs are allowed now if (pMachineName) { WARNING((__TEXT("Remote install attempted, failing...\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Get rid of the directory path and get a pointer to the filename pFilename = GetFilenameFromPath((PTSTR)pProfileName); if (!pFilename) { WARNING((__TEXT("Could not parse file name from profile path %s\n"), pProfileName)); SetLastError(ERROR_INVALID_PARAMETER); goto EndInstallColorProfile; } // Get the profile class in the form of a string if (!GetProfileClassString(pProfileName, szClass, &header)) { WARNING((__TEXT("Installing invalid profile %s\n"), pProfileName)); SetLastError(ERROR_INVALID_PROFILE); goto EndInstallColorProfile; } // Open the registry path where profiles are kept if (((dwErr = RegCreateKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) != ERROR_SUCCESS) || ((dwErr = RegCreateKey(hkICM, szClass, &hkDevice)) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICM\\device branch of registry: %d\n"), dwErr)); SetLastError(dwErr); goto EndInstallColorProfile; } // If registry data exists && the profile is in the directory, then the profile is already installed, // in which case, return success. Otherwise, copy profile to color // directory (if it's not already there) and add it to the registry (if it's not already there). dwSize = sizeof(szDest); // Copy the file to the color directory if (!InternalGetColorDirectory(NULL, szDest, &dwSize)) { WARNING((__TEXT("Could not get color directory\n"))); goto EndInstallColorProfile; } // This creates the directory if it doesn't exist, doesn't do anything // if it already exists CreateDirectory(szDest, NULL); if (szDest[lstrlen(szDest) - 1] != '\\') { lstrcat(szDest, gszBackslash); } lstrcat(szDest, pFilename); // If the profile is already in the color directory, do not attempt // to copy it again; it will fail. dwSize = sizeof(REGDATA); FileExist = GetFileAttributes(szDest) != (DWORD)-1; RegExist = RegQueryValueEx(hkDevice, pFilename, 0, NULL, (PBYTE)®Data, &dwSize) == ERROR_SUCCESS; // If the file does exist, short circuit the CopyFile // and go on to add it into the registry. if (!FileExist && !CopyFile(pProfileName, szDest, FALSE)) { WARNING((__TEXT("Could not copy profile %s to color directory\n"), pProfileName)); goto EndInstallColorProfile; } // Add profile to the registry if (!RegExist) { regData.dwRefCount = 0; regData.dwManuID = FIX_ENDIAN(header.phManufacturer); regData.dwModelID = FIX_ENDIAN(header.phModel); if ((dwErr = RegSetValueEx(hkDevice, pFilename, 0, REG_BINARY, (PBYTE)®Data, sizeof(REGDATA))) != ERROR_SUCCESS) { WARNING((__TEXT("Could not set registry data to install profile %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndInstallColorProfile; } } rc = TRUE; // Everything went well! EndInstallColorProfile: if (hkICM) { RegCloseKey(hkICM); } if (hkDevice) { RegCloseKey(hkDevice); } return rc; } /* * InternalUninstallColorProfile * Function: * This function uninstalls a given color profile on a a given machine. * It fails if the color profile is associated with any device, so all * associations should be removed before calling this function. It also * optionally deletes the file if the profile was successfully * uninstalled. * Arguments: * pMachineName - name identifying machine on which the profile * should be uninstalled. NULL implies local * pProfileName - pointer to profile to uninstall * bDelete - TRUE if profile should be deleted in disk * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local uninstall is supported, so pMachineName should * be NULL. */ BOOL InternalUninstallColorProfile( LPCTSTR pMachineName, LPCTSTR pProfileName, BOOL bDelete ) { REGDATA regData; // for storing registry data about profile HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkDevice = NULL; // key to ICM device branch in registry DWORD dwSize; // size of registry data for profile DWORD dwErr; // error code BOOL rc = FALSE; // return code PTSTR pFilename; // profile name without path TCHAR szColorPath[MAX_PATH]; // full path name of profile TCHAR szClass[5]; // profile class // Validate parameters if (!pProfileName) { WARNING((__TEXT("Invalid parameter to UninstallColorProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local installs are allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote uninstall attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // Get rid of the directory path and get a pointer to the filename pFilename = GetFilenameFromPath((PTSTR)pProfileName); if (!pFilename) { WARNING((__TEXT("Could not parse file name from profile path\n"), pProfileName)); SetLastError(ERROR_INVALID_PARAMETER); goto EndUninstallColorProfile; } // Create a fully qualified path name dwSize = sizeof(szColorPath); if (!InternalGetColorDirectory(NULL, szColorPath, &dwSize)) { WARNING((__TEXT("Could not get color directory\n"))); goto EndUninstallColorProfile; } if (szColorPath[lstrlen(szColorPath) - 1] != '\\') { lstrcat(szColorPath, gszBackslash); } lstrcat(szColorPath, pFilename); // Get the profile class in the form of a string if (!GetProfileClassString(szColorPath, szClass, NULL)) { WARNING((__TEXT("Installing invalid profile\n"))); SetLastError(ERROR_INVALID_PROFILE); goto EndUninstallColorProfile; } // Open the registry path where profiles are kept if (((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) != ERROR_SUCCESS) || ((dwErr = RegOpenKey(hkICM, szClass, &hkDevice)) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICM\\device branch of registry: %d\n"), dwErr)); SetLastError(dwErr); goto EndUninstallColorProfile; } // Check if reference count is zero and remove value from registry dwSize = sizeof(REGDATA); if ((dwErr = RegQueryValueEx(hkDevice, pFilename, 0, NULL, (PBYTE)®Data, &dwSize)) != ERROR_SUCCESS) { WARNING((__TEXT("Trying to uninstall a profile that is not installed %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndUninstallColorProfile; } if (regData.dwRefCount != 0) { WARNING((__TEXT("Trying to uninstall profile %s whose refcount is %d\n"), pFilename, regData.dwRefCount)); goto EndUninstallColorProfile; } if ((dwErr = RegDeleteValue(hkDevice, pFilename)) != ERROR_SUCCESS) { WARNING((__TEXT("Error deleting profile %s from registry: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndUninstallColorProfile; } // Remove profile from the registry if (bDelete) { // Delete profile from the color directory if (!DeleteFile(szColorPath)) { WARNING((__TEXT("Error deleting profile %s: %d\n"), szColorPath, GetLastError())); goto EndUninstallColorProfile; } } rc = TRUE; // Everything went well! EndUninstallColorProfile: if (hkICM) { RegCloseKey(hkICM); } if (hkDevice) { RegCloseKey(hkDevice); } return rc; } /* * InternalAssociateColorProfileWithDevice * Function: * This function associates a color profile on a a given machine with a * particular device. It fails if the color profile is not installed on * the machine. It increases the usage reference count of the profile. * Arguments: * pMachineName - name identifying machine. NULL implies local * pProfileName - pointer to profile to associate * pDeviceName - pointer to device name * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local association is supported, so pMachineName should * be NULL. */ BOOL InternalAssociateColorProfileWithDevice( LPCTSTR pMachineName, LPCTSTR pProfileName, LPCTSTR pDeviceName ) { PROFILEHEADER header; // profile header REGDATA regData; // for storing registry data about profile HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkDevice = NULL; // key to ICM device branch in registry DWORD dwSize; // size of registry data DWORD dwNewSize; // new size of device registry data DWORD dwErr; // error code BOOL rc = FALSE; // return code PTSTR pFilename; // profile name without path PTSTR pProfileList = NULL; // list of associated profiles TCHAR szColorPath[MAX_PATH]; // full path name of profile TCHAR szClass[5]; // profile class BOOL bFirstProfile = FALSE; // First profile to be associated for device DWORD dwDeviceClass; // device class // Validate parameters if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to AssociateColorProfileWithDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local associations are allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote profile association attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // Get rid of the directory path and get a pointer to the filename pFilename = GetFilenameFromPath((PTSTR)pProfileName); if (!pFilename) { WARNING((__TEXT("Could not parse file name from profile path %s\n"), pProfileName)); SetLastError(ERROR_INVALID_PARAMETER); goto EndAssociateProfileWithDevice; } // Create a fully qualified path name dwSize = sizeof(szColorPath); if (!InternalGetColorDirectory(NULL, szColorPath, &dwSize)) { WARNING((__TEXT("Could not get color directory\n"))); goto EndAssociateProfileWithDevice; } if (szColorPath[lstrlen(szColorPath) - 1] != '\\') { lstrcat(szColorPath, gszBackslash); } lstrcat(szColorPath, pFilename); // Get the profile class in the form of a string if (!GetProfileClassString(szColorPath, szClass, &header)) { WARNING((__TEXT("Installing invalid profile %s\n"), szColorPath)); SetLastError(ERROR_INVALID_PROFILE); goto EndAssociateProfileWithDevice; } // Open the registry path where profiles are kept if (((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) != ERROR_SUCCESS) || ((dwErr = RegOpenKey(hkICM, szClass, &hkDevice)) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICM\\device branch of registry: %d\n"), dwErr)); SetLastError(dwErr); goto EndAssociateProfileWithDevice; } // Check if profile is installed dwSize = sizeof(REGDATA); if ((dwErr = RegQueryValueEx(hkDevice, pFilename, 0, NULL, (PBYTE)®Data, &dwSize)) != ERROR_SUCCESS) { WARNING((__TEXT("Trying to associate a profile that is not installed %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndAssociateProfileWithDevice; } // Treat CLASS_MONITOR as CLASS_COLORSPACE. if ((dwDeviceClass = header.phClass) == CLASS_MONITOR) { // since CLASS_MONTOR profile can be associated any device class. dwDeviceClass = CLASS_COLORSPACE; } // Read in the list of profiles associated with the device dwSize = 0; if (!GetDeviceData(pDeviceName, dwDeviceClass, DEVICE_PROFILE_DATA, (PVOID*)&pProfileList, &dwSize, TRUE)) { pProfileList = NULL; // no data found } // If the profile is already associated with the device, return success. // Do not check if we didn't get a profile list if (pProfileList && IsStringInMultiSz(pProfileList, pFilename) == TRUE) { rc = TRUE; goto EndAssociateProfileWithDevice; } if (dwSize <= sizeof(TCHAR)) { bFirstProfile = TRUE; } // Add new profile to the list of profiles, and double NULL // terminate the MULTI_SZ string. if (dwSize > 0) { // Use a temporary pointer, so that if MemReAlloc fails, we do // not have a memory leak - the original pointer needs to be freed PTSTR pTemp; dwNewSize = dwSize + (lstrlen(pFilename) + 1) * sizeof(TCHAR); pTemp = MemReAlloc(pProfileList, dwNewSize); if (!pTemp) { WARNING((__TEXT("Error reallocating pProfileList\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto EndAssociateProfileWithDevice; } else pProfileList = pTemp; } else { // Allocate extra character for double NULL termination. Setting // dwSize to 1 accomplishes this and lets the lstrcpy below // to work correctly. dwSize = sizeof(TCHAR); // extra char for double NULL termination dwNewSize = dwSize + (lstrlen(pFilename) + 1) * sizeof(TCHAR); pProfileList = MemAlloc(dwNewSize); if (!pProfileList) { WARNING((__TEXT("Error allocating pProfileList\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto EndAssociateProfileWithDevice; } } { PTSTR pPtr; // temporary pointer pPtr = (PTSTR)((PBYTE)pProfileList + dwSize - sizeof(TCHAR)); lstrcpy(pPtr, pFilename); pPtr = (PTSTR)((PBYTE)pProfileList + dwNewSize - sizeof(TCHAR)); *pPtr = '\0'; // double NULL terminate } // Set the device data if (!SetDeviceData(pDeviceName, dwDeviceClass, DEVICE_PROFILE_DATA, pProfileList, dwNewSize)) { WARNING((__TEXT("Error setting device profile data for %s\n"), pDeviceName)); goto EndAssociateProfileWithDevice; } // Increment usage count and set it regData.dwRefCount++; if ((dwErr = RegSetValueEx(hkDevice, pFilename, 0, REG_BINARY, (PBYTE)®Data, sizeof(REGDATA))) != ERROR_SUCCESS) { ERR((__TEXT("Could not set registry data for profile %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndAssociateProfileWithDevice; } #if !defined(_WIN95_) if (bFirstProfile) { ChangeICMSetting(pMachineName, pDeviceName, ICM_ON); } #endif rc = TRUE; // Everything went well! EndAssociateProfileWithDevice: if (hkICM) { RegCloseKey(hkICM); } if (hkDevice) { RegCloseKey(hkDevice); } if (pProfileList) { MemFree(pProfileList); } return rc; } /* * InternalDisassociateColorProfileFromDevice * Function: * This function disassociates a color profile on a a given machine from * a particular device. It fails if the color profile is not installed * on the machine and associated with the device. It decreases the usage * reference count of the profile. * Arguments: * pMachineName - name identifying machine. NULL implies local * pProfileName - pointer to profile to disassociate * pDeviceName - pointer to device name * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local disassociation is supported, so pMachineName * should be NULL. */ BOOL InternalDisassociateColorProfileFromDevice( LPCTSTR pMachineName, LPCTSTR pProfileName, LPCTSTR pDeviceName ) { PROFILEHEADER header; // profile header REGDATA regData; // for storing registry data about profile HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkDevice = NULL; // key to ICM device branch in registry DWORD dwSize; // size of registry data DWORD dwNewSize; // new size of device registry data DWORD dwErr; // error code BOOL rc = FALSE; // return code PTSTR pFilename; // profile name without path PTSTR pProfileList = NULL; // list of associated profiles TCHAR szColorPath[MAX_PATH]; // full path name of profile TCHAR szClass[5]; // profile class BOOL bLastProfile = FALSE; // whether last profile is being removed DWORD dwDeviceClass; // device class // Validate parameters if (!pProfileName || !pDeviceName) { WARNING((__TEXT("Invalid parameter to DisassociateColorProfileFromDevice\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local associations are allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote profile disassociation attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // Get rid of the directory path and get a pointer to the filename pFilename = GetFilenameFromPath((PTSTR)pProfileName); if (!pFilename) { WARNING((__TEXT("Could not parse file name from profile path %s\n"), pProfileName)); SetLastError(ERROR_INVALID_PARAMETER); goto EndDisassociateProfileWithDevice; } // Create a fully qualified path name dwSize = sizeof(szColorPath); if (!InternalGetColorDirectory(NULL, szColorPath, &dwSize)) { WARNING((__TEXT("Could not get color directory\n"))); goto EndDisassociateProfileWithDevice; } if (szColorPath[lstrlen(szColorPath) - 1] != '\\') { lstrcat(szColorPath, gszBackslash); } lstrcat(szColorPath, pFilename); // Get the profile class in the form of a string if (!GetProfileClassString(szColorPath, szClass, &header)) { WARNING((__TEXT("Installing invalid profile %s\n"), szColorPath)); SetLastError(ERROR_INVALID_PROFILE); goto EndDisassociateProfileWithDevice; } // Open the registry path where profiles are kept if (((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) != ERROR_SUCCESS) || ((dwErr = RegOpenKey(hkICM, szClass, &hkDevice)) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICM\\device branch of registry: %d\n"), dwErr)); SetLastError(dwErr); goto EndDisassociateProfileWithDevice; } // Check if profile is installed dwSize = sizeof(REGDATA); if ((dwErr = RegQueryValueEx(hkDevice, pFilename, 0, NULL, (PBYTE)®Data, &dwSize)) != ERROR_SUCCESS) { WARNING((__TEXT("Trying to disassociate a profile that is not installed %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndDisassociateProfileWithDevice; } // Treat CLASS_MONITOR as CLASS_COLORSPACE. if ((dwDeviceClass = header.phClass) == CLASS_MONITOR) { // since CLASS_MONTOR profile can be associated any device class. dwDeviceClass = CLASS_COLORSPACE; } // Read in the list of profiles associated with the device dwSize = 0; if (!GetDeviceData(pDeviceName, dwDeviceClass, DEVICE_PROFILE_DATA, (PVOID*)&pProfileList, &dwSize, TRUE)) { pProfileList = NULL; // no data found } // If the profile is not associated with the device, return failure if (!pProfileList || !IsStringInMultiSz(pProfileList, pFilename)) { WARNING((__TEXT("Trying to disassociate a profile that is not associated %s\n"), pFilename)); SetLastError(ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE); goto EndDisassociateProfileWithDevice; } // Remove profile from the list of profiles, and double NULL // terminate the MULTI_SZ string. dwNewSize = RemoveStringFromMultiSz(pProfileList, pFilename, dwSize); // Set the device data if (!SetDeviceData(pDeviceName, dwDeviceClass, DEVICE_PROFILE_DATA, pProfileList, dwNewSize)) { WARNING((__TEXT("Error setting device profile data for %s\n"), pDeviceName)); goto EndDisassociateProfileWithDevice; } if (dwNewSize <= sizeof(TCHAR)) { bLastProfile = TRUE; } // Decrement usage count and set it regData.dwRefCount--; if ((dwErr = RegSetValueEx(hkDevice, pFilename, 0, REG_BINARY, (PBYTE)®Data, sizeof(REGDATA))) != ERROR_SUCCESS) { ERR((__TEXT("Could not set registry data for profile %s: %d\n"), pFilename, dwErr)); SetLastError(dwErr); goto EndDisassociateProfileWithDevice; } #if !defined(_WIN95_) if (bLastProfile) { ChangeICMSetting(pMachineName, pDeviceName, ICM_OFF); } #endif rc = TRUE; // Everything went well! EndDisassociateProfileWithDevice: if (hkICM) { RegCloseKey(hkICM); } if (hkDevice) { RegCloseKey(hkDevice); } if (pProfileList) { MemFree(pProfileList); } return rc; } /* * InternalEnumColorProfiles * Function: * These functions enumerates color profiles satisfying the given * enumeration criteria. It searches among all installed profiles, and * on return fills out a buffer with a series of NULL terminated profile * filenames double NULL terminated at the end. * Arguments: * pMachineName - name identifying machine on which the enumeration * needs to be done * pEnumRecord - pointer to enumeration criteria * pBuffer - pointer to buffer to receive result, can be NULL * pdwSize - pointer to buffer size. On return it is actual number * of bytes copied/needed. * pnProfiles - pointer to DWORD. On return, it is number of profiles * copied to pBuffer. * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local enumeration is supported, so pMachineName should * be NULL. */ BOOL InternalEnumColorProfiles( LPCTSTR pMachineName, PENUMTYPE pEnumRecord, PBYTE pBuffer, PDWORD pdwSize, PDWORD pnProfiles ) { REGDATA regData; // for storing registry data about profile HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkDevice = NULL; // key to ICM device branch in registry PTSTR pProfileList = NULL; // list of associated profiles PTSTR pTempProfileList; // temporary copy of profile list DWORD dwSize; // size of profile value DWORD dwDataSize; // size of profile data DWORD dwInputSize; // incoming size of buffer DWORD i, j; // counter variables DWORD dwErr; // error code BOOL rc = FALSE; // return code TCHAR szFullPath[MAX_PATH]; // full path of profile to open PTSTR pProfile; // pointer to profile name in full path DWORD dwLen; // length of color directory string DWORD bSkipMatch; // true for skipping exact profile matching MATCHTYPE match; // Type of profile match PBYTE pBufferStart; // Start of user given buffer DWORD dwSizeOfStruct; // size of ENUMTYPE structure DWORD dwVersion; // ENUMTYPE structure version // Validate parameters if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize)) || (pnProfiles && IsBadWritePtr(pnProfiles, sizeof(DWORD))) || !pEnumRecord || IsBadReadPtr(pEnumRecord, sizeof(DWORD) * 3)) // probe until ENUMTYPE.dwFields { ParameterError_InternalEnumColorProfiles: WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Check structure size based on its version. dwSizeOfStruct = pEnumRecord->dwSize; dwVersion = pEnumRecord->dwVersion; if (dwVersion >= ENUM_TYPE_VERSION) { if (dwSizeOfStruct < sizeof(ENUMTYPE)) goto ParameterError_InternalEnumColorProfiles; } else if (dwVersion == 0x0200) { if (dwSizeOfStruct < sizeof(ENUMTYPE) - sizeof(DWORD)) goto ParameterError_InternalEnumColorProfiles; // Version 2 should not have ET_DEVICECLASS bit if (pEnumRecord->dwFields & ET_DEVICECLASS) goto ParameterError_InternalEnumColorProfiles; WARNING((__TEXT("Old version ENUMTYPE to InternalEnumColorProfiles\n"))); } else { goto ParameterError_InternalEnumColorProfiles; } if (IsBadReadPtr(pEnumRecord, dwSizeOfStruct)) { goto ParameterError_InternalEnumColorProfiles; } // Only local enumerations are allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote profile enumeration attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } dwInputSize = *pdwSize; // Get color directory dwLen = sizeof(szFullPath); if (!InternalGetColorDirectory(NULL, szFullPath, &dwLen)) { WARNING((__TEXT("Error getting color directory\n"))); return FALSE; } if (szFullPath[lstrlen(szFullPath) - 1] != '\\') { lstrcat(szFullPath, gszBackslash); } pProfile = &szFullPath[lstrlen(szFullPath)]; dwLen = lstrlen(szFullPath) * sizeof(TCHAR); // Initialize return parameters *pdwSize = 0; if (pnProfiles) *pnProfiles = 0; if (pBuffer && dwInputSize >= sizeof(TCHAR)) { *((PTSTR)pBuffer) = '\0'; } // Check if we are looking for the profiles of a particular device or // not because we enumerate them from different places. if (pEnumRecord->dwFields & ET_DEVICENAME) { DWORD* pbSkipMatch = &bSkipMatch; DWORD dwDeviceClass; if (!pEnumRecord->pDeviceName) { WARNING((__TEXT("Invalid parameter to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); goto EndEnumerateColorProfiles; } // Get list of profiles associated with the device. If we don't // know what device it is, specify ColorSpace, which tries all three if (pEnumRecord->dwFields & ET_DEVICECLASS) { dwDeviceClass = pEnumRecord->dwDeviceClass; } else { dwDeviceClass = CLASS_COLORSPACE; } // Get the device configuration whether we do exact matching or not. dwSize = sizeof(DWORD); if (!GetDeviceData(pEnumRecord->pDeviceName, dwDeviceClass, DEVICE_PROFILE_ENUMMODE, (PVOID*)&pbSkipMatch, &dwSize, FALSE)) { bSkipMatch = FALSE; } // Get the profile data. dwSize = 0; if (!GetDeviceData(pEnumRecord->pDeviceName, dwDeviceClass, DEVICE_PROFILE_DATA, (PVOID*)&pProfileList, &dwSize, TRUE)) { pProfileList = NULL; // no data found } if (!pProfileList) { // No profiles associated with this device rc = TRUE; if (pBuffer && dwInputSize >= sizeof(TCHAR) * 2) { *((PTSTR)pBuffer + 1) = '\0'; } goto EndEnumerateColorProfiles; } // Run through the list of profiles and check each one to see if it // matches the enumeration criteria. If it does, and the buffer is // large enough, copy it to the buffer, and increment the byte count // and number of profiles enumerated. pBufferStart = pBuffer; pTempProfileList = pProfileList; while (*pTempProfileList) { lstrcpy(pProfile, pTempProfileList); if (bSkipMatch) { match = EXACT_MATCH; } else { match = DoesProfileMatchEnumRecord(szFullPath, pEnumRecord); } if (match != NOMATCH) { *pdwSize += (lstrlen(pTempProfileList) + 1) * sizeof(TCHAR); // Check strictly less than because you need one more for // the final NULL termination if (pBuffer && (*pdwSize < dwInputSize)) { if (match == MATCH) { lstrcpy((PTSTR)pBuffer, pTempProfileList); } else { // Exact match, add to beginning of buffer InsertInBuffer(pBufferStart, pBuffer, pTempProfileList); } pBuffer += (lstrlen(pTempProfileList) + 1) * sizeof(TCHAR); } if (pnProfiles) (*pnProfiles)++; } pTempProfileList += lstrlen(pTempProfileList) + 1; } } else { DWORD dwNumClasses; PTSTR* ppszEnumClasses; PTSTR pszEnumClassArray[2]; // We are not looking at a particular device, so enumerate // profiles from the registry if (pEnumRecord->dwFields & ET_DEVICECLASS) { // If device class is specified, enumrate the specified device class and color // space class which can be associated to any device. pszEnumClassArray[0] = ConvertClassIdToClassString(pEnumRecord->dwDeviceClass); pszEnumClassArray[1] = ConvertClassIdToClassString(CLASS_COLORSPACE); if (!pszEnumClassArray[0] || !pszEnumClassArray[1]) { WARNING((__TEXT("Invalid DeviceClass to EnumColorProfiles\n"))); SetLastError(ERROR_INVALID_PARAMETER); goto EndEnumerateColorProfiles; } ppszEnumClasses = pszEnumClassArray; dwNumClasses = 2; } else { ppszEnumClasses = gpszClasses; dwNumClasses = sizeof(gpszClasses) / sizeof(PTSTR); } // Open the registry path where profiles are kept (and create it if not exist) if ((dwErr = RegCreateKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) != ERROR_SUCCESS) { WARNING((__TEXT("Cannot open ICM branch of registry: %d\n"), dwErr)); SetLastError(dwErr); goto EndEnumerateColorProfiles; } pBufferStart = pBuffer; for (i = 0; i < dwNumClasses; i++, ppszEnumClasses++) { DWORD nValues; // number of name-values in key if (RegOpenKey(hkICM, *ppszEnumClasses, &hkDevice) != ERROR_SUCCESS) { continue; // go to next key } if ((dwErr = RegQueryInfoKey(hkDevice, NULL, NULL, 0, NULL, NULL, NULL, &nValues, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS) { WARNING((__TEXT("Cannot count values in device branch of registry: %d\n"), dwErr)); RegCloseKey(hkDevice); SetLastError(dwErr); goto EndEnumerateColorProfiles; } // Go through the list of profiles and return everything that // satisfies the enumeration criteria for (j = 0; j < nValues; j++) { dwSize = sizeof(szFullPath) - dwLen; dwDataSize = sizeof(REGDATA); if (RegEnumValue(hkDevice, j, pProfile, &dwSize, 0, NULL, (PBYTE)®Data, &dwDataSize) == ERROR_SUCCESS) { match = DoesProfileMatchEnumRecord(szFullPath, pEnumRecord); if (match != NOMATCH) { *pdwSize += (lstrlen(pProfile) + 1) * sizeof(TCHAR); if (pBuffer && (*pdwSize < dwInputSize)) { if (match == MATCH) { lstrcpy((PTSTR)pBuffer, pProfile); } else { // Exact match, add to beginning of buffer InsertInBuffer(pBufferStart, pBuffer, pProfile); } pBuffer += (lstrlen(pProfile) + 1) * sizeof(TCHAR); } if (pnProfiles) (*pnProfiles)++; } } } RegCloseKey(hkDevice); } } *pdwSize += sizeof(TCHAR); // extra NULL termination if (pBuffer && *pdwSize <= dwInputSize) { *((PTSTR)pBuffer) = '\0'; rc = TRUE; } else { SetLastError(ERROR_INSUFFICIENT_BUFFER); } EndEnumerateColorProfiles: if (hkICM) { RegCloseKey(hkICM); } if (pProfileList) { MemFree(pProfileList); } return rc; } VOID InsertInBuffer( PBYTE pStart, PBYTE pEnd, PTSTR pString ) { DWORD cnt = (lstrlen(pString) + 1) * sizeof(TCHAR); MyCopyMemory(pStart + cnt, pStart, (DWORD)(pEnd - pStart)); lstrcpy((PTSTR)pStart, pString); return; } /* * InternalSetSCSProfile * Function: * These functions regsiters the given profile for the standard color * space specified. This will register it in the OS and can be queried * using GetStandardColorSpaceProfile. * Arguments: * pMachineName - name identifying machine on which the standard color * space profile should be registered * dwSCS - ID for the standard color space * pProfileName - pointer to profile filename * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local registration is supported, so pMachineName should * be NULL. */ BOOL InternalSetSCSProfile( LPCTSTR pMachineName, DWORD dwSCS, LPCTSTR pProfileName ) { HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkRegProf = NULL; // key to registered color spaces branch DWORD dwSize; // size of registry data DWORD dwErr; // error code BOOL rc = FALSE; // return code TCHAR szProfileID[5]; // profile class // Validate parameters if (!pProfileName) { WARNING((__TEXT("Invalid parameter to SetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local registration is allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote SCS profile registration attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } dwSize = (lstrlen(pProfileName) + 1) * sizeof(TCHAR); // Open the registry location where this is kept if (((dwErr = RegCreateKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) == ERROR_SUCCESS) && ((dwErr = RegCreateKey(hkICM, gszRegisteredProfiles, &hkRegProf)) == ERROR_SUCCESS)) { ConvertDwordToString(dwSCS, szProfileID); if ((dwErr = RegSetValueEx(hkRegProf, szProfileID, 0, REG_SZ, (PBYTE)pProfileName, dwSize)) == ERROR_SUCCESS) { rc = TRUE; } } if (hkICM) { RegCloseKey(hkICM); } if (hkRegProf) { RegCloseKey(hkRegProf); } if (!rc) { WARNING((__TEXT("InternalSetSCSProfile failed: %d\n"), dwErr)); SetLastError(dwErr); } return rc; } /* * InternalGetSCSProfile * Function: * These functions retrieves the profile regsitered for the standard color * space specified. * Arguments: * pMachineName - name identifying machine on which the standard color * space profile should be queried * dwSCS - ID for the standard color space * pBuffer - pointer to buffer to receive profile filename * pdwSize - pointer to DWORD specifying size of buffer. On return * it has size needed/used * Returns: * TRUE if successful, NULL otherwise * Warning: * Currently only local query is supported, so pMachineName should * be NULL. */ BOOL InternalGetSCSProfile( LPCTSTR pMachineName, DWORD dwSCS, PTSTR pBuffer, PDWORD pdwSize ) { HKEY hkICM = NULL; // key to ICM branch in registry HKEY hkRegProf = NULL; // key to registered color spaces branch DWORD dwErr; // error code DWORD dwSize; BOOL rc = FALSE; // return code TCHAR szProfileID[5]; // profile class // Validate parameters if (!pdwSize || IsBadWritePtr(pdwSize, sizeof(DWORD)) || (pBuffer && IsBadWritePtr(pBuffer, *pdwSize))) { WARNING((__TEXT("Invalid parameter to GetStandardColorSpaceProfile\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Only local query is allowed now if (pMachineName != NULL) { WARNING((__TEXT("Remote SCS profile query attempted, failing...\n"))); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } dwSize = *pdwSize; // Look in the registry for a profile registered for this color space ID if (((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM)) == ERROR_SUCCESS) && ((dwErr = RegOpenKey(hkICM, gszRegisteredProfiles, &hkRegProf)) == ERROR_SUCCESS)) { ConvertDwordToString(dwSCS, szProfileID); if ((dwErr = RegQueryValueEx(hkRegProf, szProfileID, NULL, NULL, (PBYTE)pBuffer, pdwSize)) == ERROR_SUCCESS) { rc = TRUE; } } if (hkICM) { RegCloseKey(hkICM); } if (hkRegProf) { RegCloseKey(hkRegProf); } if (!rc && (dwSCS == LCS_sRGB || dwSCS == LCS_WINDOWS_COLOR_SPACE)) { *pdwSize = dwSize; rc = GetColorDirectory(NULL, pBuffer, pdwSize); if (!rc && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return FALSE; } *pdwSize += (lstrlen(gszBackslash) + lstrlen(gszsRGBProfile)) * sizeof(TCHAR); if (*pdwSize <= dwSize && pBuffer) { lstrcat(pBuffer, gszBackslash); lstrcat(pBuffer, gszsRGBProfile); rc = TRUE; } else { dwErr = ERROR_INSUFFICIENT_BUFFER; } } if (!rc) { WARNING((__TEXT("InternalGetSCSProfile failed: %d\n"), dwErr)); SetLastError(dwErr); } // If pBuffer is NULL, RegQueryValueEx return TRUE. Our API should return FALSE // in this case. Handle this. if (pBuffer == NULL && rc) { SetLastError(ERROR_INSUFFICIENT_BUFFER); rc = FALSE; } return rc; } /* * ConvertDwordToString * Function: * This function converts a DWORD into a string. The string passed in * is large enough. It converts to Unicode or Ansi depending on how * this is compiled. * Arguments: * dword - DWORD to convert * pString - pointer to buffer to hold the result * Returns: * Nothing */ VOID ConvertDwordToString( DWORD dword, PTSTR pString ) { int i; // counter for (i = 0; i < 4; i++) { pString[i] = (TCHAR)(((char*)&dword)[3 - i]); } pString[4] = '\0'; return; } /* * ConvertClassIdToClassString * Function: * This function converts a DWORD Device Class Id into a its device string * Arguments: * dwClassId - Device class id. * Returns: * pointer to a string */ PTSTR ConvertClassIdToClassString( DWORD dwClassId ) { switch (dwClassId) { case CLASS_MONITOR: return (gpszClasses[INDEX_CLASS_MONITOR]); case CLASS_PRINTER: return (gpszClasses[INDEX_CLASS_PRINTER]); case CLASS_SCANNER: return (gpszClasses[INDEX_CLASS_SCANNER]); case CLASS_COLORSPACE: return (gpszClasses[INDEX_CLASS_COLORSPACE]); default: return NULL; } } /* * GetProfileClassString * Function: * This function returns the profile class from the header as a string. * It also validates the profile. * Arguments: * pProfileName - name of profile * pClass - pointer to buffer to hold the profile class string * pHeader - if this is non NULL, it returns the header here * Returns: * TRUE on success, FALSE otherwise */ BOOL GetProfileClassString( LPCTSTR pProfileName, PTSTR pClass, PPROFILEHEADER pHeader ) { PROFILEHEADER header; // color profile header PROFILE prof; // profile object for opening profile HPROFILE hProfile = NULL; // handle to opened profile BOOL bValidProfile = FALSE; // validation of the profile BOOL rc = FALSE; // return code // Open a handle to the profile prof.dwType = PROFILE_FILENAME; prof.pProfileData = (PVOID)pProfileName; prof.cbDataSize = (lstrlen(pProfileName) + 1) * sizeof(TCHAR); hProfile = OpenColorProfile(&prof, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); if (!hProfile) { WARNING((__TEXT("Error opening profile %s\n"), pProfileName)); goto EndGetProfileClassString; } // Check the validation of the profile. if (!IsColorProfileValid(hProfile, &bValidProfile) || !bValidProfile) { WARNING((__TEXT("Error invalid profile %s\n"), pProfileName)); goto EndGetProfileClassString; } // Get the profile class if (!pHeader) { pHeader = &header; } if (!GetColorProfileHeader(hProfile, pHeader)) { ERR((__TEXT("Error getting color profile header for %s\n"), pProfileName)); goto EndGetProfileClassString; } ConvertDwordToString(pHeader->phClass, pClass); rc = TRUE; EndGetProfileClassString: if (hProfile) { CloseColorProfile(hProfile); } return rc; } /* * GetFilenameFromPath * Function: * This function takes a fully qualified pathname and returns a pointer * to the filename part alone * Arguments: * pPathName - pointer to pathname * Returns: * Pointer to filename on success, NULL otherwise */ PTSTR GetFilenameFromPath( PTSTR pPathName ) { DWORD dwLen; // length of pathname PTSTR pPathNameStart = pPathName; dwLen = lstrlen(pPathName); if (dwLen == 0) { return NULL; } // Go to the end of the pathname, and start going backwards till // you reach the beginning or a backslash pPathName += dwLen; // Currently 'pPathName' points null-terminate character, so move // the pointer to last character. do { pPathName = CharPrev(pPathNameStart, pPathName); if (*pPathName == TEXT('\\')) { pPathName = CharNext(pPathName); break; } // Loop until fist } while (pPathNameStart < pPathName); // if *pPathName is zero, then we had a string that ends in a backslash return *pPathName ? pPathName : NULL; } /* * GetDeviceData * Function: * This function is a wrapper for IGetDeviceData. For devices like monitor, * printer & scanner it calls the internal function. If we are asked * to get the device data for a "colorspace device", we try monitor, printer * and scanner till one succeeds or they all fail. This is done so that we * we can associate sRGB like profiles with any device. * Arguments: * pDeviceName - pointer to name of the device * dwClass - device type like monitor, printer etc. * ppDeviceData - pointer to pointer to buffer to receive data * pdwSize - pointer to size of buffer. On return it is size of * data returned/size needed. * bAllocate - If TRUE, allocate memory for data * Returns: * TRUE if successful, FALSE otherwise */ BOOL GetDeviceData( LPCTSTR pDeviceName, DWORD dwClass, DWORD dwDataType, PVOID* ppDeviceData, PDWORD pdwSize, BOOL bAllocate ) { BOOL rc = FALSE; if (dwClass == CLASS_MONITOR || dwClass == CLASS_PRINTER || dwClass == CLASS_SCANNER) { rc = IGetDeviceData(pDeviceName, dwClass, dwDataType, ppDeviceData, pdwSize, bAllocate); } else if (dwClass == CLASS_COLORSPACE) { rc = IGetDeviceData(pDeviceName, CLASS_MONITOR, dwDataType, ppDeviceData, pdwSize, bAllocate) || IGetDeviceData(pDeviceName, CLASS_PRINTER, dwDataType, ppDeviceData, pdwSize, bAllocate) || IGetDeviceData(pDeviceName, CLASS_SCANNER, dwDataType, ppDeviceData, pdwSize, bAllocate); } return rc; } /* * IGetDeviceData * Function: * This function retrieves ICM data stored with the different devices. * Arguments: * pDeviceName - pointer to name of the device * dwClass - device type like monitor, printer etc. * ppDeviceData - pointer to pointer to buffer to receive data * pdwSize - pointer to size of buffer. On return it is size of * data returned/size needed. * bAllocate - If TRUE, allocate memory for data * Returns: * TRUE if successful, FALSE otherwise */ BOOL IGetDeviceData( LPCTSTR pDeviceName, DWORD dwClass, DWORD dwDataType, PVOID* ppDeviceData, PDWORD pdwSize, BOOL bAllocate ) { PFNOPENDEVICE fnOpenDevice; PFNCLOSEDEVICE fnCloseDevice; PFNGETDEVICEDATA fnGetData; HANDLE hDevice; DWORD dwSize; LPTSTR pDataKey; LPTSTR pDataValue; BOOL rc = FALSE; // Set up function pointers so we can write common code switch (dwClass) { case CLASS_PRINTER: fnOpenDevice = (PFNOPENDEVICE)OpenPrtr; fnCloseDevice = (PFNCLOSEDEVICE)ClosePrtr; fnGetData = (PFNGETDEVICEDATA)GetPrtrData; break; case CLASS_MONITOR: fnOpenDevice = (PFNOPENDEVICE)OpenMonitor; fnCloseDevice = (PFNCLOSEDEVICE)CloseMonitor; fnGetData = (PFNGETDEVICEDATA)GetMonitorData; break; case CLASS_SCANNER: fnOpenDevice = (PFNOPENDEVICE)OpenScanner; fnCloseDevice = (PFNCLOSEDEVICE)CloseScanner; fnGetData = (PFNGETDEVICEDATA)GetScannerData; break; default: return FALSE; } // Set up registry keywords. switch (dwDataType) { case DEVICE_PROFILE_DATA: pDataKey = gszICMProfileListKey; // The way to store printer profile is different than others... trim it. if (dwClass == CLASS_PRINTER) { pDataValue = gszFiles; } else { pDataValue = gszICMProfileListValue; } break; case DEVICE_PROFILE_ENUMMODE: pDataKey = gszICMDeviceDataKey; pDataValue = gszICMProfileEnumMode; break; default: return FALSE; } // Open the device and get a handle to it if (!(*fnOpenDevice)((PTSTR)pDeviceName, &hDevice, NULL)) { return FALSE; } if (bAllocate || (ppDeviceData == NULL)) { DWORD retcode; // We need to allocate memory. Find out how much we need, and // allocate it. dwSize = 0; retcode = (*fnGetData)(hDevice, pDataKey, pDataValue, NULL, NULL, 0, &dwSize); if ((retcode != ERROR_SUCCESS) && // Win 95 returns this (retcode != ERROR_MORE_DATA)) // NT returns this { VERBOSE((__TEXT("GetDeviceData failed for %s\n"), pDeviceName)); goto EndGetDeviceData; } *pdwSize = dwSize; if (ppDeviceData == NULL) { // Caller wants to know the data size. rc = TRUE; goto EndGetDeviceData; } else { // Allocate buffer. *ppDeviceData = MemAlloc(dwSize); if (!*ppDeviceData) { WARNING((__TEXT("Error allocating memory\n"))); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto EndGetDeviceData; } } } // Get the data if ((*fnGetData)(hDevice, pDataKey, pDataValue, NULL, (PBYTE)*ppDeviceData, *pdwSize, pdwSize) == ERROR_SUCCESS) { rc = TRUE; } EndGetDeviceData: (*fnCloseDevice)(hDevice); return rc; } /* * SetDeviceData * Function: * This function is a wrapper for ISetDeviceData. For devices like monitor, * printer & scanner it calls the internal function. If we are asked * to set the device data for a "colorspace device", we try monitor, printer * and scanner till one succeeds or they all fail. This is done so that we * we can associate sRGB like profiles with any device. * Arguments: * pDeviceName - pointer to name of the device * dwClass - device type like monitor, printer etc. * pDeviceData - pointer buffer containing data * dwSize - size of data * Returns: * TRUE if successful, FALSE otherwise */ BOOL SetDeviceData( LPCTSTR pDeviceName, DWORD dwClass, DWORD dwDataType, PVOID pDeviceData, DWORD dwSize ) { BOOL rc = FALSE; if (dwClass == CLASS_MONITOR || dwClass == CLASS_PRINTER || dwClass == CLASS_SCANNER) { rc = ISetDeviceData(pDeviceName, dwClass, dwDataType, pDeviceData, dwSize); } else if (dwClass == CLASS_COLORSPACE) { rc = ISetDeviceData(pDeviceName, CLASS_MONITOR, dwDataType, pDeviceData, dwSize) || ISetDeviceData(pDeviceName, CLASS_PRINTER, dwDataType, pDeviceData, dwSize) || ISetDeviceData(pDeviceName, CLASS_SCANNER, dwDataType, pDeviceData, dwSize); } return rc; } /* * ISetDeviceData * Function: * This function sets ICM data stored with the different devices. * Arguments: * pDeviceName - pointer to name of the device * dwClass - device type like monitor, printer etc. * pDeviceData - pointer buffer containing data * dwSize - size of data * Returns: * TRUE if successful, FALSE otherwise */ BOOL ISetDeviceData( LPCTSTR pDeviceName, DWORD dwClass, DWORD dwDataType, PVOID pDeviceData, DWORD dwSize ) { PRINTER_DEFAULTS pd; PFNOPENDEVICE fnOpenDevice; PFNCLOSEDEVICE fnCloseDevice; PFNSETDEVICEDATA fnSetData; HANDLE hDevice; LPTSTR pDataKey; LPTSTR pDataValue; DWORD dwRegType = REG_BINARY; BOOL rc = FALSE; // Set up function pointers so we can write common code switch (dwClass) { case CLASS_PRINTER: fnOpenDevice = (PFNOPENDEVICE)OpenPrtr; fnCloseDevice = (PFNCLOSEDEVICE)ClosePrtr; fnSetData = (PFNSETDEVICEDATA)SetPrtrData; pd.pDatatype = __TEXT("RAW"); pd.pDevMode = NULL; pd.DesiredAccess = PRINTER_ACCESS_ADMINISTER; break; case CLASS_MONITOR: fnOpenDevice = (PFNOPENDEVICE)OpenMonitor; fnCloseDevice = (PFNCLOSEDEVICE)CloseMonitor; fnSetData = (PFNSETDEVICEDATA)SetMonitorData; break; case CLASS_SCANNER: fnOpenDevice = (PFNOPENDEVICE)OpenScanner; fnCloseDevice = (PFNCLOSEDEVICE)CloseScanner; fnSetData = (PFNSETDEVICEDATA)SetScannerData; break; default: return FALSE; } // Set up registry keywords. switch (dwDataType) { case DEVICE_PROFILE_DATA: pDataKey = gszICMProfileListKey; // The way to store printer profile is different than others... trim it. if (dwClass == CLASS_PRINTER) { pDataValue = gszFiles; dwRegType = REG_MULTI_SZ; } else { pDataValue = gszICMProfileListValue; } break; case DEVICE_PROFILE_ENUMMODE: pDataKey = gszICMDeviceDataKey; pDataValue = gszICMProfileEnumMode; break; default: return FALSE; } // Open the device and get a handle to it if (!(*fnOpenDevice)((PTSTR)pDeviceName, &hDevice, (PTSTR)&pd)) { WARNING((__TEXT("Error opening device %s\n"), pDeviceName)); return FALSE; } // Set the data if ((*fnSetData)(hDevice, pDataKey, pDataValue, dwRegType, (PBYTE)pDeviceData, dwSize) == ERROR_SUCCESS) { rc = TRUE; } #if !defined(_WIN95_) // If this is printer class, need some more data for profile list. if ((rc == TRUE) && (dwClass == CLASS_PRINTER) && (dwDataType == DEVICE_PROFILE_DATA)) { if (((*fnSetData)(hDevice, pDataKey, gszDirectory, REG_SZ, (PBYTE)gszColorDir, (lstrlen(gszColorDir) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) || ((*fnSetData)(hDevice, pDataKey, gszModule, REG_SZ, (PBYTE)gszMSCMS, (lstrlen(gszMSCMS) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS)) { rc = FALSE; } } #endif (*fnCloseDevice)(hDevice); return rc; } /* * IsStringInMultiSz * Function: * This functions checks if a given multi-sz string has the given string * as one of the strings, and returns TRUE if it does. * Arguments: * pMultiSzString - multi sz string to look in * pString - string to find * Returns: * TRUE */ BOOL IsStringInMultiSz( PTSTR pMultiSzString, PTSTR pString ) { BOOL rc = FALSE; // return code while (*pMultiSzString) { if (!lstrcmpi(pMultiSzString, pString)) { rc = TRUE; break; } pMultiSzString += lstrlen(pMultiSzString) + 1; } return rc; } /* * RemoveStringFromMultiSz * Function: * This functions removes a given string from a multi-sz string. * Arguments: * pMultiSzString - multi sz string to look in * pString - string to remove * dwSize - size in bytes of multi-sz string * Returns: * TRUE */ DWORD RemoveStringFromMultiSz( PTSTR pMultiSzString, PTSTR pString, DWORD dwSize ) { DWORD dwCount = dwSize; // count of bytes remaining while (*pMultiSzString) { dwCount -= (lstrlen(pMultiSzString) + 1) * sizeof(TCHAR); if (!lstrcmpi(pMultiSzString, pString)) { break; } pMultiSzString += lstrlen(pMultiSzString) + 1; } MyCopyMemory((PBYTE)pMultiSzString, (PBYTE)(pMultiSzString + lstrlen(pString) + 1), dwCount); return dwSize - sizeof(TCHAR) * (lstrlen(pString) + 1); } /* * DoesProfileMatchEnumRecord * Function: * This functions checks if a profile matches the criteria given in * the enumeration record. Note that it does not check if the profile * belongs to the device specified by pDeviceName. So that check must * have happened before itself. * Arguments: * pProfileName - profile to look at * pEnumRecord - pointer to criteria to check against * Returns: * MATCH or EXACT_MATCH if the profile matches the criteria, NOMATCH otherwise */ #define SET(pEnumRecord, bit) ((pEnumRecord)->dwFields & (bit)) MATCHTYPE DoesProfileMatchEnumRecord( PTSTR pProfileName, PENUMTYPE pEnumRecord ) { PROFILEHEADER header; // color profile header PROFILE prof; // profile object for opening profile HPROFILE hProfile = NULL; // handle to opened profile MATCHTYPE rc = NOMATCH; // return code // Open a handle to the profile prof.dwType = PROFILE_FILENAME; prof.pProfileData = (PVOID)pProfileName; prof.cbDataSize = (lstrlen(pProfileName) + 1) * sizeof(TCHAR); hProfile = OpenColorProfile(&prof, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); if (!hProfile) { WARNING((__TEXT("Error opening profile %s\n"), pProfileName)); goto EndDoesProfileMatchEnumRecord; } // Get the profile header if (!GetColorProfileHeader(hProfile, &header)) { ERR((__TEXT("Error getting color profile header for %s\n"), pProfileName)); goto EndDoesProfileMatchEnumRecord; } if ((!SET(pEnumRecord, ET_CMMTYPE) || (pEnumRecord->dwCMMType == header.phCMMType)) && (!SET(pEnumRecord, ET_CLASS) || (pEnumRecord->dwClass == header.phClass)) && (!SET(pEnumRecord, ET_DATACOLORSPACE) || (pEnumRecord->dwDataColorSpace == header.phDataColorSpace)) && (!SET(pEnumRecord, ET_CONNECTIONSPACE) || (pEnumRecord->dwConnectionSpace == header.phConnectionSpace)) && (!SET(pEnumRecord, ET_SIGNATURE) || (pEnumRecord->dwSignature == header.phSignature)) && (!SET(pEnumRecord, ET_PLATFORM) || (pEnumRecord->dwPlatform == header.phPlatform)) && (!SET(pEnumRecord, ET_PROFILEFLAGS) || (pEnumRecord->dwProfileFlags == header.phProfileFlags)) && (!SET(pEnumRecord, ET_MANUFACTURER) || (pEnumRecord->dwManufacturer == header.phManufacturer)) && (!SET(pEnumRecord, ET_MODEL) || (pEnumRecord->dwModel == header.phModel)) && (!SET(pEnumRecord, ET_ATTRIBUTES) || (pEnumRecord->dwAttributes[0] == header.phAttributes[0] && pEnumRecord->dwAttributes[1] == header.phAttributes[1])) && (!SET(pEnumRecord, ET_RENDERINGINTENT) || (pEnumRecord->dwRenderingIntent == header.phRenderingIntent)) && (!SET(pEnumRecord, ET_CREATOR) || (pEnumRecord->dwCreator == header.phCreator))) { rc = EXACT_MATCH; } // Check for resolution, media type and halftoning match if (rc != NOMATCH && SET(pEnumRecord, ET_RESOLUTION | ET_MEDIATYPE | ET_DITHERMODE)) { rc = CheckResMedHftnMatch(hProfile, pEnumRecord); } EndDoesProfileMatchEnumRecord: if (hProfile) { CloseColorProfile(hProfile); } return rc; } /* * CheckResMedHftnMatch * Function: * This functions checks if a profile matches the resolution, * media type and halftoning criteria specified by the enumeration record. * It allows an exact match, as well as an ambiguous match. If the * profile doesn't specify the criteria, it is considered to ambiguously * match the specification. * is desired. * Arguments: * hProfile - handle identifying profile * pEnumRecord - pointer to criteria to check against * Returns: * MATCH or EXACT_MATCH if the profile matches the criteria, NOMATCH otherwise */ MATCHTYPE CheckResMedHftnMatch( HPROFILE hProfile, PENUMTYPE pEnumRecord ) { PDEVICESETTINGS pDevSettings = NULL; PPLATFORMENTRY pPlatform; PSETTINGCOMBOS pCombo; PSETTINGS pSetting; DWORD dwMSData[4]; DWORD dwSize, i, iMax, j, jMax; MATCHTYPE rc = MATCH; // Assume ambiguous match BOOL bReference; // Check if the profile has the new device settings tag dwSize = 0; GetColorProfileElement(hProfile, TAG_DEVICESETTINGS, 0, &dwSize, NULL, &bReference); if (dwSize > 0) { if (!(pDevSettings = (PDEVICESETTINGS)GlobalAllocPtr(GHND, dwSize))) { WARNING((__TEXT("Error allocating memory\n"))); return NOMATCH; } if (GetColorProfileElement(hProfile, TAG_DEVICESETTINGS, 0, &dwSize, (PBYTE)pDevSettings, &bReference)) { pPlatform = &pDevSettings->PlatformEntry[0]; // Navigate to the place where Microsoft specific settings are kept i = 0; iMax = FIX_ENDIAN(pDevSettings->nPlatforms); while ((i < iMax) && (pPlatform->PlatformID != ID_MSFT_REVERSED)) { i++; pPlatform = (PPLATFORMENTRY)((PBYTE)pPlatform + FIX_ENDIAN(pPlatform->dwSize)); } if (i >= iMax) { // There are no MS specific settings, assume this profile is valid // for all settings (ambigous match) goto EndCheckResMedHftnMatch; } // Found MS specific data. Now go through each combination of settings pCombo = &pPlatform->SettingCombos[0]; iMax = FIX_ENDIAN(pPlatform->nSettingCombos); for (i = 0; i < iMax; i++) { // Go through each setting in the combination pSetting = &pCombo->Settings[0]; jMax = FIX_ENDIAN(pCombo->nSettings); for (j = 0; j < jMax; j++) { if (pSetting->dwSettingType == ID_MEDIATYPE_REVERSED) { if (SET(pEnumRecord, ET_MEDIATYPE) && !DwordMatches(pSetting, pEnumRecord->dwMediaType)) { goto NextCombo; } } else if (pSetting->dwSettingType == ID_DITHER_REVERSED) { if (SET(pEnumRecord, ET_DITHERMODE) && !DwordMatches(pSetting, pEnumRecord->dwDitheringMode)) { goto NextCombo; } } else if (pSetting->dwSettingType == ID_RESLN_REVERSED) { if (SET(pEnumRecord, ET_RESOLUTION) && !QwordMatches(pSetting, &pEnumRecord->dwResolution[0])) { goto NextCombo; } } pSetting = (PSETTINGS)((PBYTE)pSetting + sizeof(SETTINGS) - sizeof(DWORD) + FIX_ENDIAN(pSetting->dwSizePerValue) * FIX_ENDIAN(pSetting->nValues)); } // This combination worked! rc = EXACT_MATCH; goto EndCheckResMedHftnMatch; NextCombo: pCombo = (PSETTINGCOMBOS)((PBYTE)pCombo + FIX_ENDIAN(pCombo->dwSize)); } rc = NOMATCH; goto EndCheckResMedHftnMatch; } else { rc = NOMATCH; goto EndCheckResMedHftnMatch; } } else { // Check if the old MSxx tags are present dwSize = sizeof(dwMSData); if (SET(pEnumRecord, ET_MEDIATYPE)) { if (GetColorProfileElement(hProfile, TAG_MS01, 0, &dwSize, dwMSData, &bReference)) { rc = EXACT_MATCH; // Assume exact match if (pEnumRecord->dwMediaType != FIX_ENDIAN(dwMSData[2])) { return NOMATCH; } } } dwSize = sizeof(dwMSData); if (SET(pEnumRecord, ET_DITHERMODE)) { if (GetColorProfileElement(hProfile, TAG_MS02, 0, &dwSize, dwMSData, &bReference)) { rc = EXACT_MATCH; // Assume exact match if (pEnumRecord->dwDitheringMode != FIX_ENDIAN(dwMSData[2])) { return NOMATCH; } } } dwSize = sizeof(dwMSData); if (SET(pEnumRecord, ET_RESOLUTION)) { if (GetColorProfileElement(hProfile, TAG_MS03, 0, &dwSize, dwMSData, &bReference)) { rc = EXACT_MATCH; // Assume exact match if (pEnumRecord->dwResolution[0] != FIX_ENDIAN(dwMSData[2]) || pEnumRecord->dwResolution[1] != FIX_ENDIAN(dwMSData[3])) { return NOMATCH; } } } } EndCheckResMedHftnMatch: if (pDevSettings) { GlobalFreePtr(pDevSettings); } return rc; } BOOL DwordMatches( PSETTINGS pSetting, DWORD dwValue ) { DWORD i, iMax; PDWORD pValue; dwValue = FIX_ENDIAN(dwValue); // so we don't have to do this in the loop // Go through all the values. If any of them match, return TRUE. pValue = &pSetting->Value[0]; iMax = FIX_ENDIAN(pSetting->nValues); for (i = 0; i < iMax; i++) { if (dwValue == *pValue) { return TRUE; } pValue++; // We know that it is a DWORD } return FALSE; } BOOL QwordMatches( PSETTINGS pSetting, PDWORD pdwValue ) { DWORD i, iMax, dwValue1, dwValue2; PDWORD pValue; dwValue1 = FIX_ENDIAN(*pdwValue); // so we don't have to do this in the loop dwValue2 = FIX_ENDIAN(*(pdwValue + 1)); // Go through all the values. If any of them match, return TRUE. pValue = &pSetting->Value[0]; iMax = FIX_ENDIAN(pSetting->nValues); for (i = 0; i < iMax; i++) { if ((dwValue1 == *pValue) && (dwValue2 == *(pValue + 1))) { return TRUE; } pValue += 2; // We know that it is a QWORD } return FALSE; } /* * OpenPrtr * Function: * On Memphis, we cannot call OpenPrinter() because it calls into 16-bit * code, so if we call this function from GDI-16, we deadlock. So we * look into the registry directly. * Arguments: * pDeviceName - pointer to name of the device * phDevice - pointer that receives the handle. * pDummy - dummy parameter * Returns: * TRUE if successful, FALSE otherwise */ BOOL WINAPI OpenPrtr( PTSTR pDeviceName, LPHANDLE phDevice, PTSTR pDummy ) { #if !defined(_WIN95_) return OpenPrinter(pDeviceName, phDevice, (LPPRINTER_DEFAULTS)pDummy); #else HKEY hkDevice = NULL; // printers branch of registry HKEY hkPrtr = NULL; // Friendly name branch of registry DWORD dwErr; // error code BOOL rc = FALSE; // return code *phDevice = NULL; if (((dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, gszRegPrinter, &hkDevice)) != ERROR_SUCCESS) || ((dwErr = RegOpenKey(hkDevice, pDeviceName, &hkPrtr)) != ERROR_SUCCESS) || ((dwErr = RegOpenKey(hkPrtr, gszPrinterData, (HKEY*)phDevice)) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open printer data branch of registry for %s: %d\n"), pDeviceName, dwErr)); SetLastError(dwErr); goto EndOpenPrtr; } rc = TRUE; EndOpenPrtr: if (hkDevice) { RegCloseKey(hkDevice); } if (hkPrtr) { RegCloseKey(hkPrtr); } return rc; #endif } /* * ClosePrtr * Function: * This function closes the printer handle opened by OpenPrtr. * Arguments: * hDevice - open handle * Returns: * TRUE if successful, FALSE otherwise */ BOOL WINAPI ClosePrtr( HANDLE hDevice ) { #if !defined(_WIN95_) return ClosePrinter(hDevice); #else DWORD dwErr; dwErr = RegCloseKey((HKEY)hDevice); SetLastError(dwErr); return dwErr == ERROR_SUCCESS; #endif } /* * GetPrtrData * Function: * This functions returns ICM data stored with the printer instance * Arguments: * hDevice - open printer handle * pKey - registry key for compatibility with GetPrinterDataEx * pName - name of registry value * pdwType - pointer to dword that receives type of value * pData - pointer to buffer to receive data * dwSize - size of buffer * pdwNeeded - on return, this has size of buffer filled/needed * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI GetPrtrData( HANDLE hDevice, PTSTR pKey, PTSTR pName, PDWORD pdwType, PBYTE pData, DWORD dwSize, PDWORD pdwNeeded ) { #if !defined(_WIN95_) return GetPrinterDataEx(hDevice, pKey, pName, pdwType, pData, dwSize, pdwNeeded); #else * pdwNeeded = dwSize; return RegQueryValueEx((HKEY)hDevice, pName, 0, NULL, pData, pdwNeeded); #endif } /* * SetPrtrData * Function: * This functions stores ICM data with the printer instance * Arguments: * hDevice - open printer handle * pKey - registry key for compatibility with SetPrinterDataEx * pName - name of registry value * dwType - type of value * pData - pointer to data buffer * dwSize - size of buffer * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI SetPrtrData( HANDLE hDevice, PTSTR pKey, PTSTR pName, DWORD dwType, PBYTE pData, DWORD dwSize ) { #if !defined(_WIN95_) return SetPrinterDataEx(hDevice, pKey, pName, dwType, pData, dwSize); #else return RegSetValueEx((HKEY)hDevice, pName, 0, dwType, pData, dwSize); #endif } /* * OpenMonitor * Function: * This function returns a handle to the monitor * Arguments: * pDeviceName - pointer to name of the device * phDevice - pointer that receives the handle. * pDummy - dummy parameter * Returns: * TRUE if successful, FALSE otherwise */ BOOL WINAPI OpenMonitor( PTSTR pDeviceName, LPHANDLE phDevice, PTSTR pDummy ) { #ifdef _WIN95_ // For Windows 9x platform. HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; HKEY hkICM = NULL; HKEY hkDriver = NULL; // software branch of registry DWORD dwSize; // size of buffer TCHAR szName[MAX_PATH]; // buffer BOOL rc = FALSE; // return value SP_DEVINFO_DATA spdid; int i; // instance counter if (!LoadSetupAPIDll()) { WARNING((__TEXT("Error loading setupapi.dll: %d\n"), GetLastError())); return FALSE; } hDevInfo = (*fpSetupDiGetClassDevs)((LPGUID)&GUID_DEVCLASS_MONITOR, NULL, NULL, DIGCF_PRESENT); if (hDevInfo == INVALID_HANDLE_VALUE) { WARNING((__TEXT("Error getting hDevInfo: %d\n"), GetLastError())); goto EndOpenMonitor; } i = 0; while (!rc) { ZeroMemory(&spdid, sizeof(SP_DEVINFO_DATA)); spdid.cbSize = sizeof(SP_DEVINFO_DATA); if (!(*fpSetupDiEnumDeviceInfo)(hDevInfo, i, &spdid)) { if (i == 0 && !lstrcmpi(pDeviceName, gszDisplay)) { // PnP support not in - open ICM key in registry TCHAR szICMMonitorData[] = __TEXT("ICMMonitorData"); WARNING((__TEXT("PnP support absent - Using DISPLAY\n"))); // Open the registry path where monitor data is kept if ((RegOpenKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM) != ERROR_SUCCESS) || (RegCreateKey(hkICM, szICMMonitorData, &hkDriver) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICMMonitorData branch of registry\n"))); goto EndOpenMonitor; } rc = TRUE; } break; } // Get PnP ID. Check and see if the monitor name matches it. dwSize = sizeof(szName); if ((*fpSetupDiGetDeviceInstanceId)(hDevInfo, &spdid, szName, dwSize, NULL) && !lstrcmp(szName, pDeviceName)) { hkDriver = (*fpSetupDiOpenDevRegKey)(hDevInfo, &spdid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS); if (hkDriver == INVALID_HANDLE_VALUE) { WARNING((__TEXT("Could not open monitor s/w key for all access\n"))); hkDriver = (*fpSetupDiOpenDevRegKey)(hDevInfo, &spdid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ); if (hkDriver == INVALID_HANDLE_VALUE) { WARNING((__TEXT("Error opening s/w registry key for read access: %x\n"), GetLastError())); goto EndOpenMonitor; } } rc = TRUE; } i++; } EndOpenMonitor: if (hkICM) { RegCloseKey(hkICM); } if (hDevInfo != INVALID_HANDLE_VALUE) { (*fpSetupDiDestroyDeviceInfoList)(hDevInfo); } *phDevice = (HANDLE)hkDriver; return rc; #else // For Windows NT (later than 5.0) platform TCHAR szRegPath[MAX_PATH]; HKEY hkDriver = NULL; // Copy device class root key. lstrcpy(szRegPath, gszDeviceClass); lstrcat(szRegPath, gszMonitorGUID); if (!lstrcmpi(pDeviceName, gszDisplay)) { WARNING((__TEXT("PnP support absent - Using DISPLAY\n"))); // PnP support not in - just open "0000" device. lstrcat(szRegPath, TEXT("\\0000")); } else if (_tcsstr(pDeviceName, gszMonitorGUID)) { // Extract monitor number from DeviceName TCHAR* pDeviceNumber = _tcsrchr(pDeviceName, TEXT('\\')); if (pDeviceNumber) { lstrcat(szRegPath, pDeviceNumber); } else { lstrcat(szRegPath, TEXT("\\0000")); } } else { // This is not valid monitor name. goto EndOpenMonitor; } // Open the registry path where monitor data is kept if (RegOpenKey(HKEY_LOCAL_MACHINE, szRegPath, &hkDriver) != ERROR_SUCCESS) { WARNING((__TEXT("Cannot open %s key\n"), szRegPath)); hkDriver = NULL; } EndOpenMonitor: *phDevice = (HANDLE)hkDriver; return (hkDriver != NULL); #endif // _WIN95_ } /* * CloseMonitor * Function: * This function closes the monitor handle opened by OpenMonitor * Arguments: * hDevice - open handle * Returns: * TRUE if successful, FALSE otherwise */ BOOL WINAPI CloseMonitor( HANDLE hDevice ) { DWORD dwErr; dwErr = RegCloseKey((HKEY)hDevice); SetLastError(dwErr); return (dwErr == ERROR_SUCCESS); } /* * GetMonitorData * Function: * This functions returns ICM data stored with the monitor instance * Arguments: * hDevice - open monitor handle * pKey - registry key for compatibility with GetPrinterDataEx * pName - name of registry value * pdwType - pointer to dword that receives type of value * pData - pointer to buffer to receive data * dwSize - size of buffer * pdwNeeded - on return, this has size of buffer filled/needed * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI GetMonitorData( HANDLE hDevice, PTSTR pKey, PTSTR pName, PDWORD pdwType, PBYTE pData, DWORD dwSize, PDWORD pdwNeeded ) { DWORD dwType, dwTemp; DWORD rc; *pdwNeeded = dwSize; rc = RegQueryValueEx((HKEY)hDevice, pName, 0, &dwType, pData, pdwNeeded); if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA) { if (dwType == REG_SZ) { PTSTR pFilename; // Old style value, convert to double null terminated binary if (pData) { pFilename = GetFilenameFromPath((PTSTR)pData); if (pFilename != (PTSTR)pData) { lstrcpy((PTSTR)pData, pFilename); } *pdwNeeded = lstrlen((PTSTR)pData) * sizeof(TCHAR); } *pdwNeeded += sizeof(TCHAR); // for double NULL termination if ((dwSize >= *pdwNeeded) && pData) { *((PTSTR)pData + lstrlen((PTSTR)pData) + 1) = '\0'; // Set the profile name in new format RegSetValueEx((HKEY)hDevice, pName, 0, REG_BINARY, pData, (lstrlen((PTSTR)pData) + 2) * sizeof(TCHAR)); } } else if (*pdwNeeded == 1) { // If we have picked up the data and it is a 1 byte non-zero // value, it is an 1 based index in a list of // predefined profiles. Deal with this case. // If pData is NULL, then we don't know if it is non-zero or // not, so we assume it is and ask for a large enough buffer. if (!pData || *pData != 0) { // Old style 1-based index value if ((dwSize >= MAX_PATH) && pData) { HKEY hkICM = NULL; HKEY hkDevice = NULL; REGDATA regData; // Make sure buggy inf doesn't crash us if (pData[0] > sizeof(gszDispProfiles) / sizeof(gszDispProfiles[0])) { WARNING((__TEXT("Predefined profile index too large: %d\n"), pData[0])); goto EndCompatMode; } lstrcpy((PTSTR)pData, gszDispProfiles[pData[0] - 1]); *((PTSTR)pData + lstrlen((PTSTR)pData) + 1) = '\0'; // We need to update reference count as it wasn't set up // using the new API // Open the registry path where profiles are kept if ((RegCreateKey(HKEY_LOCAL_MACHINE, gszICMRegPath, &hkICM) != ERROR_SUCCESS) || (RegCreateKey(hkICM, __TEXT("mntr"), &hkDevice) != ERROR_SUCCESS)) { WARNING((__TEXT("Cannot open ICM\\device branch of registry\n"))); goto EndCompatMode; } // If registry data exists, then the profile is already installed, // in which case, increment use count, otherwise add entry dwTemp = sizeof(REGDATA); if (RegQueryValueEx(hkDevice, (PTSTR)pData, 0, NULL, (PBYTE)®Data, &dwTemp) == ERROR_SUCCESS) { regData.dwRefCount++; } else { regData.dwRefCount = 1; regData.dwManuID = 'enon'; // it is our profile regData.dwModelID = 'enon'; } if (RegSetValueEx(hkDevice, (PTSTR)pData, 0, REG_BINARY, (PBYTE)®Data, sizeof(REGDATA)) != ERROR_SUCCESS) { WARNING((__TEXT("Error setting registry value\n"))); goto EndCompatMode; } // Set the profile name in new format RegSetValueEx((HKEY)hDevice, pName, 0, REG_BINARY, pData, (lstrlen((PTSTR)pData) + 2) * sizeof(TCHAR)); EndCompatMode: if (hkICM) { RegCloseKey(hkICM); } if (hkDevice) { RegCloseKey(hkDevice); } } *pdwNeeded = MAX_PATH; } } } if ((rc == ERROR_SUCCESS) && (*pdwNeeded > dwSize)) { rc = ERROR_MORE_DATA; } return rc; } /* * SetMonitorData * Function: * This functions stores ICM data with the monitor instance * Arguments: * hDevice - open monitor handle * pKey - registry key for compatibility with SetPrinterDataEx * pName - name of registry value * dwType - type of value * pData - pointer to data buffer * dwSize - size of buffer * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI SetMonitorData( HANDLE hDevice, PTSTR pKey, PTSTR pName, DWORD dwType, PBYTE pData, DWORD dwSize ) { return RegSetValueEx((HKEY)hDevice, pName, 0, dwType, pData, dwSize); } /* * OpenScanner * Function: * This function returns a handle to the scanner * Arguments: * pDeviceName - pointer to name of the device * phDevice - pointer that receives the handle. * pDummy - dummy parameter * Returns: * TRUE */ BOOL WINAPI OpenScanner( PTSTR pDeviceName, LPHANDLE phDevice, PTSTR pDummy ) { PFNSTICREATEINSTANCE pStiCreateInstance; PSCANNERDATA psd = NULL; HRESULT hres; BOOL bRc = FALSE; if (!(psd = (PSCANNERDATA)MemAlloc(sizeof(SCANNERDATA)))) { WARNING((__TEXT("Error allocating memory for scanner data\n"))); return FALSE; } if (!(psd->pDeviceName = MemAlloc((lstrlen(pDeviceName) + 1) * sizeof(WCHAR)))) { WARNING((__TEXT("Error allocating memory for scanner name\n"))); goto EndOpenScanner; } #ifdef UNICODE lstrcpy(psd->pDeviceName, pDeviceName); #else if (!ConvertToUnicode(pDeviceName, &psd->pDeviceName, FALSE)) { WARNING((__TEXT("Error converting scanner name to Unicode\n"))); goto EndOpenScanner; } #endif if (!(psd->hModule = LoadLibrary(gszStiDll))) { WARNING((__TEXT("Error loading sti.dll: %d\n"), GetLastError())); goto EndOpenScanner; } if (!(pStiCreateInstance = (PFNSTICREATEINSTANCE)GetProcAddress(psd->hModule, gszStiCreateInstance))) { WARNING((__TEXT("Error getting proc StiCreateInstance\n"))); goto EndOpenScanner; } hres = (*pStiCreateInstance)(GetModuleHandle(NULL), STI_VERSION, &psd->pSti, NULL); if (FAILED(hres)) { WARNING((__TEXT("Error creating sti instance: %d\n"), hres)); goto EndOpenScanner; } *phDevice = (HANDLE)psd; bRc = TRUE; EndOpenScanner: if (!bRc && psd) { CloseScanner((HANDLE)psd); } return bRc; } /* * CloseScanner * Function: * This function closes the monitor handle opened by OpenMonitor * Arguments: * hDevice - handle of device * Returns: * TRUE */ BOOL WINAPI CloseScanner( HANDLE hDevice ) { PSCANNERDATA psd = (PSCANNERDATA)hDevice; if (psd) { if (psd->pSti) { psd->pSti->lpVtbl->Release(psd->pSti); } if (psd->pDeviceName) { MemFree(psd->pDeviceName); } if (psd->hModule) { FreeLibrary(psd->hModule); } MemFree(psd); } return TRUE; } /* * GetScannerData * Function: * This functions returns ICM data stored with the scannerr instance * Arguments: * hDevice - open scanner handle * pKey - registry key for compatibility with GetPrinterDataEx * pName - name of registry value * pdwType - pointer to dword that receives type of value * pData - pointer to buffer to receive data * dwSize - size of buffer * pdwNeeded - on return, this has size of buffer filled/needed * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI GetScannerData( HANDLE hDevice, PTSTR pKey, PTSTR pName, PDWORD pdwType, PBYTE pData, DWORD dwSize, PDWORD pdwNeeded ) { PSCANNERDATA psd = (PSCANNERDATA)hDevice; HRESULT hres; #ifndef UNICODE PWSTR pwszName; // STI interface "ALWAYS" expects Unicode. hres = ConvertToUnicode(pName, &pwszName, TRUE); if (!hres) { return (ERROR_INVALID_PARAMETER); } pName = (PSTR)pwszName; #endif * pdwNeeded = dwSize; hres = psd->pSti->lpVtbl->GetDeviceValue(psd->pSti, psd->pDeviceName, (PWSTR)pName, pdwType, pData, pdwNeeded); #ifndef UNICODE MemFree(pwszName); #endif return hres; } /* * SetScannerData * Function: * This functions stores ICM data with the scanner instance * Arguments: * hDevice - open scanner handle * pKey - registry key for compatibility with SetPrinterDataEx * pName - name of registry value * dwType - type of value * pData - pointer to data buffer * dwSize - size of buffer * Returns: * ERROR_SUCCESS if successful, error code otherwise */ DWORD WINAPI SetScannerData( HANDLE hDevice, PTSTR pKey, PTSTR pName, DWORD dwType, PBYTE pData, DWORD dwSize ) { PSCANNERDATA psd = (PSCANNERDATA)hDevice; HRESULT hres; #ifndef UNICODE PWSTR pwszName; // STI interface "ALWAYS" expects Unicode. hres = ConvertToUnicode(pName, &pwszName, TRUE); if (!hres) { return (ERROR_INVALID_PARAMETER); } pName = (PSTR)pwszName; #endif hres = psd->pSti->lpVtbl->SetDeviceValue(psd->pSti, psd->pDeviceName, (PWSTR)pName, dwType, pData, dwSize); #ifndef UNICODE MemFree(pwszName); #endif return hres; } // Internal functions BOOL WINAPI InternalGetDeviceConfig( LPCTSTR pDeviceName, DWORD dwDeviceClass, DWORD dwConfigType, PVOID pConfigData, PDWORD pdwSize ) { DWORD dwDataType; DWORD dwSizeRequired = 0; BOOL rc = FALSE; switch (dwConfigType) { case MSCMS_PROFILE_ENUM_MODE: dwDataType = DEVICE_PROFILE_ENUMMODE; break; default: WARNING((__TEXT("Invalid parameter to InternalGetDeviceConfig\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Query the size of the data. if (GetDeviceData(pDeviceName, dwDeviceClass, dwDataType, NULL, &dwSizeRequired, FALSE)) { if ((dwSizeRequired <= *pdwSize) && (pConfigData != NULL)) { // If buffer is enough, get the data. if (GetDeviceData(pDeviceName, dwDeviceClass, dwDataType, (PVOID*)&pConfigData, pdwSize, FALSE)) { rc = TRUE; } else { WARNING((__TEXT("Failed on GetDeviceData to query data\n"))); SetLastError(ERROR_INVALID_PARAMETER); } } else { // Return nessesary buffer size to caller. *pdwSize = dwSizeRequired; SetLastError(ERROR_INSUFFICIENT_BUFFER); } } else { WARNING((__TEXT("Failed on GetDeviceData to query data size\n"))); SetLastError(ERROR_INVALID_PARAMETER); } return rc; } BOOL WINAPI InternalSetDeviceConfig( LPCTSTR pDeviceName, DWORD dwDeviceClass, DWORD dwConfigType, PVOID pConfigData, DWORD dwSize ) { DWORD dwDataType; switch (dwConfigType) { case MSCMS_PROFILE_ENUM_MODE: dwDataType = DEVICE_PROFILE_ENUMMODE; break; default: WARNING((__TEXT("Invalid parameter to InternalGetDeviceConfig\n"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Save the data. return (SetDeviceData(pDeviceName, dwDeviceClass, dwDataType, pConfigData, dwSize)); } #ifdef _WIN95_ // Win9x specific functions are here. BOOL LoadSetupAPIDll( VOID ) { EnterCriticalSection(&critsec); if (ghModSetupAPIDll == NULL) { ghModSetupAPIDll = LoadLibrary(TEXT("setupapi.dll")); if (ghModSetupAPIDll) { fpSetupDiOpenDevRegKey = (FP_SetupDiOpenDevRegKey) GetProcAddress(ghModSetupAPIDll, "SetupDiOpenDevRegKey"); fpSetupDiDestroyDeviceInfoList = (FP_SetupDiDestroyDeviceInfoList) GetProcAddress(ghModSetupAPIDll, "SetupDiDestroyDeviceInfoList"); fpSetupDiEnumDeviceInfo = (FP_SetupDiEnumDeviceInfo) GetProcAddress(ghModSetupAPIDll, "SetupDiEnumDeviceInfo"); fpSetupDiGetDeviceInstanceId = (FP_SetupDiGetDeviceInstanceId) GetProcAddress(ghModSetupAPIDll, "SetupDiGetDeviceInstanceIdA"); fpSetupDiGetClassDevs = (FP_SetupDiGetClassDevs) GetProcAddress(ghModSetupAPIDll, "SetupDiGetClassDevsA"); if ((fpSetupDiOpenDevRegKey == NULL) || (fpSetupDiDestroyDeviceInfoList == NULL) || (fpSetupDiEnumDeviceInfo == NULL) || (fpSetupDiGetDeviceInstanceId == NULL) || (fpSetupDiGetClassDevs == NULL)) { WARNING((__TEXT("Could not find Export function in setupapi.dll\n"))); FreeLibrary(ghModSetupAPIDll); ghModSetupAPIDll = NULL; } } } LeaveCriticalSection(&critsec); return (!!ghModSetupAPIDll); } #else // Win NT specific functions are here. VOID ChangeICMSetting( LPCTSTR pMachineName, LPCTSTR pDeviceName, DWORD dwICMMode ) { PRINTER_INFO_8* ppi8; PRINTER_INFO_9* ppi9; PRINTER_DEFAULTS pd; HANDLE hPrinter; DWORD dwSize; BYTE temp[2 * 1024]; // sufficient for devmode pd.pDatatype = NULL; pd.pDevMode = NULL; pd.DesiredAccess = PRINTER_ALL_ACCESS; if (!OpenPrinter((PTSTR)pDeviceName, &hPrinter, &pd)) return; // Get and update system devmode ppi8 = (PRINTER_INFO_8*)&temp; if (GetPrinter(hPrinter, 8, (PBYTE)ppi8, sizeof(temp), &dwSize) && ppi8->pDevMode) { switch (dwICMMode) { case ICM_ON: case ICM_OFF: ppi8->pDevMode->dmFields |= DM_ICMMETHOD; if (dwICMMode == ICM_ON) ppi8->pDevMode->dmICMMethod = DMICMMETHOD_SYSTEM; else ppi8->pDevMode->dmICMMethod = DMICMMETHOD_NONE; SetPrinter(hPrinter, 8, (PBYTE)ppi8, 0); break; } } // If the user has a per-user devmode, update this as well ppi9 = (PRINTER_INFO_9*)&temp; if (GetPrinter(hPrinter, 9, (PBYTE)ppi9, sizeof(temp), &dwSize) && ppi9->pDevMode) { switch (dwICMMode) { case ICM_ON: case ICM_OFF: ppi9->pDevMode->dmFields |= DM_ICMMETHOD; if (dwICMMode == ICM_ON) ppi9->pDevMode->dmICMMethod = DMICMMETHOD_SYSTEM; else ppi9->pDevMode->dmICMMethod = DMICMMETHOD_NONE; SetPrinter(hPrinter, 9, (PBYTE)ppi9, 0); break; } } ClosePrinter(hPrinter); } #endif // _WIN95_