|
|
/******************************************************************************
Source File: ICC Profile.CPP
This implements the class we use to encapsulate everything we will ever care to know about a profile, including the classes we need to support associations and the like.
Copyright (c) 1996, 1997 by Microsoft Corporation. All Rights Reserved.
A Pretty Penny Enterprises Production
Change History:
10-31-96 A-RobKj (Pretty Penny Enterprises) began encapsulating it 12-04-96 A-RobKj Added the CProfileArray and CAllDeviceList classes 12-13-96 A-RobKj Modified for faster operation (more lazy evaluation, and common DLL-wide database for installation checks) Also moved CDeviceList derived classes to the header, so I can use them other places, as well... 01-07-97 KjelgaardR@acm.org Fixed CProfileArray::Empty- wasn't setting Next object pointer to NULL after deleting said object (Fixed GP fault). 01-08-97 KjelgaardR@acm.org Modified printer enumeration routine to only enumerate color models (uses Global utility function).
******************************************************************************/
#include "ICMUI.H"
#include <shlobj.h>
#include "shellext.h"
#include "..\mscms\sti.h"
typedef HRESULT (__stdcall *PFNSTICREATEINSTANCE)(HINSTANCE, DWORD, PSTI*, LPDWORD);
TCHAR gszStiDll[] = __TEXT("sti.dll"); char gszStiCreateInstance[] = "StiCreateInstance";
// Printer DeviceEnumeration method
void CPrinterList::Enumerate() {
#if !defined(_WIN95_) // CPrinterList::Enumetate()
// Enumerate all local printers
DWORD dwcNeeded, dwcReturned; EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwcNeeded, &dwcReturned);
union { PBYTE pBuff; PPRINTER_INFO_4 ppi4; };
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, pBuff, dwcNeeded, &dwcNeeded, &dwcReturned) && GetLastError() == ERROR_MORE_DATA) { delete [] pBuff; pBuff = new BYTE[dwcNeeded]; }
if (pBuff) {
for (unsigned u = 0; u < dwcReturned; u++) if (CGlobals::ThisIsAColorPrinter(ppi4[u].pPrinterName)) { m_csaDeviceNames.Add(ppi4[u].pPrinterName); m_csaDisplayNames.Add(ppi4[u].pPrinterName); }
delete [] pBuff; }
// Now, enumerate all the connected printers
EnumPrinters(PRINTER_ENUM_CONNECTIONS, NULL, 4, NULL, 0, &dwcNeeded, &dwcReturned);
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_CONNECTIONS, NULL, 4, pBuff, dwcNeeded, &dwcNeeded, &dwcReturned) && GetLastError() == ERROR_MORE_DATA) { delete [] pBuff; pBuff = new BYTE[dwcNeeded]; }
if (!pBuff) return;
for (unsigned u = 0; u < dwcReturned; u++) if (CGlobals::ThisIsAColorPrinter(ppi4[u].pPrinterName)) { m_csaDeviceNames.Add(ppi4[u].pPrinterName); m_csaDisplayNames.Add(ppi4[u].pPrinterName); }
delete [] pBuff;
#else
// Enumerate all local printers
DWORD dwcNeeded, dwcReturned; EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &dwcNeeded, &dwcReturned);
union { PBYTE pBuff; PPRINTER_INFO_5 ppi5; };
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, pBuff, dwcNeeded, &dwcNeeded, &dwcReturned) && GetLastError() == ERROR_MORE_DATA) { delete [] pBuff; pBuff = new BYTE[dwcNeeded]; }
if (pBuff) {
for (unsigned u = 0; u < dwcReturned; u++) { if (CGlobals::ThisIsAColorPrinter(ppi5[u].pPrinterName)) { m_csaDeviceNames.Add(ppi5[u].pPrinterName); m_csaDisplayNames.Add(ppi5[u].pPrinterName); } }
delete [] pBuff; } #endif
}
// Printer Name Validity Check
BOOL CPrinterList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count()) Enumerate();
for (unsigned u = 0; u < Count(); u++) if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef)) break;
return u < Count(); }
// Private monitor enumeration function- note this is ANSI only...
extern "C" BOOL WINAPI EnumerateMonitors(LPBYTE pBuffer, PDWORD pdwcbNeeded, PDWORD pdwcReturned);
// CMonitor class enumerator
void CMonitorList::Enumerate() {
ULONG ulSerialNumber = 1; ULONG ulDeviceIndex = 0; DISPLAY_DEVICE ddPriv;
ddPriv.cb = sizeof(ddPriv);
// Enumurate display adaptor on the system.
while (EnumDisplayDevices(NULL, ulDeviceIndex, &ddPriv, 0)) { ULONG ulMonitorIndex = 0; DISPLAY_DEVICE ddPrivMonitor;
ddPrivMonitor.cb = sizeof(ddPrivMonitor);
// then, enumurate monitor device, attached the display adaptor.
while (EnumDisplayDevices(ddPriv.DeviceName, ulMonitorIndex, &ddPrivMonitor, 0)) { TCHAR DisplayNameBuf[256]; // number: devicename - 256 is good enough.
// Insert PnP id as device name.
m_csaDeviceNames.Add(ddPrivMonitor.DeviceID);
// If this is primary display device, remember it.
if (ddPriv.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { m_csPrimaryDeviceName = ddPrivMonitor.DeviceID; }
// Build display name.
wsprintf(DisplayNameBuf,TEXT("%d. %s"),ulSerialNumber,ddPrivMonitor.DeviceString); m_csaDisplayNames.Add(DisplayNameBuf);
ulMonitorIndex++; ulSerialNumber++; ddPrivMonitor.cb = sizeof(ddPrivMonitor); }
ulDeviceIndex++; ddPriv.cb = sizeof(ddPriv); } }
// Monitor Name Validity Check
BOOL CMonitorList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count()) Enumerate();
for (unsigned u = 0; u < Count(); u++) if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef)) break;
return u < Count(); }
LPCSTR CMonitorList::DeviceNameToDisplayName(LPCTSTR lpstrRef) {
if (!lpstrRef) return NULL;
if (!Count()) Enumerate();
for (unsigned u = 0; u < Count(); u++) if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef)) return (LPCSTR)(m_csaDisplayNames[u]);
return NULL; }
// Scanner DeviceEnumeration method
void CScannerList::Enumerate() {
PFNSTICREATEINSTANCE pStiCreateInstance; PSTI pSti = NULL; PSTI_DEVICE_INFORMATION pDevInfo; PVOID pBuffer = NULL; HINSTANCE hModule; HRESULT hres; DWORD i, dwItemsReturned; #ifndef UNICODE
char szName[256]; #endif
if (!(hModule = LoadLibrary(gszStiDll))) { _RPTF1(_CRT_WARN, "Error loading sti.dll: %d\n", GetLastError()); return; }
if (!(pStiCreateInstance = (PFNSTICREATEINSTANCE)GetProcAddress(hModule, gszStiCreateInstance))) { _RPTF0(_CRT_WARN, "Error getting proc StiCreateInstance\n"); goto EndEnumerate; }
hres = (*pStiCreateInstance)(GetModuleHandle(NULL), STI_VERSION, &pSti, NULL);
if (FAILED(hres)) { _RPTF1(_CRT_WARN, "Error creating sti instance: %d\n", hres); goto EndEnumerate; }
hres = pSti->GetDeviceList(0, 0, &dwItemsReturned, &pBuffer);
if (FAILED(hres) || !pBuffer) { _RPTF0(_CRT_WARN, "Error getting scanner devices\n"); goto EndEnumerate; }
pDevInfo = (PSTI_DEVICE_INFORMATION) pBuffer;
for (i=0; i<dwItemsReturned; i++, pDevInfo++) { #ifndef UNICODE
DWORD dwLen; // length of Ansi string
BOOL bUsedDefaultChar;
dwLen = (lstrlenW(pDevInfo->pszLocalName) + 1) * sizeof(char);
//
// Convert Unicode name to Ansi
//
if (WideCharToMultiByte(CP_ACP, 0, pDevInfo->szDeviceInternalName, -1, szName, dwLen, NULL, &bUsedDefaultChar) && ! bUsedDefaultChar) { m_csaDeviceNames.Add(szName); } else { _RPTF0(_CRT_WARN, "Error converting internalName to Unicode name\n"); }
if (WideCharToMultiByte(CP_ACP, 0, pDevInfo->pszLocalName, -1, szName, dwLen, NULL, &bUsedDefaultChar) && ! bUsedDefaultChar) { m_csaDisplayNames.Add(szName); } else { _RPTF0(_CRT_WARN, "Error converting deviceName to Unicode name\n"); }
#else
m_csaDeviceNames.Add(pDevInfo->szDeviceInternalName); m_csaDisplayNames.Add(pDevInfo->pszLocalName); #endif
}
EndEnumerate: if (pBuffer) { LocalFree(pBuffer); }
if (pSti) { pSti->Release(); }
if (hModule) { FreeLibrary(hModule); }
return; }
// Scanner Name Validity Check
BOOL CScannerList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count()) Enumerate();
for (unsigned u = 0; u < Count(); u++) if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef)) break;
return u < Count(); }
// CAllDeviceList class enumerator
void CAllDeviceList::Enumerate() {
CMonitorList cml; CPrinterList cpl; CScannerList csl;
cml.Enumerate(); cpl.Enumerate(); csl.Enumerate();
for (unsigned u = 0; u < cpl.Count(); u++) { m_csaDeviceNames.Add(cpl.DeviceName(u)); m_csaDisplayNames.Add(cpl.DisplayName(u)); }
for (u = 0; u < cml.Count(); u++) { m_csaDeviceNames.Add(cml.DeviceName(u)); m_csaDisplayNames.Add(cml.DisplayName(u)); }
for (u = 0; u < csl.Count(); u++) { m_csaDeviceNames.Add(csl.DeviceName(u)); m_csaDisplayNames.Add(csl.DisplayName(u)); } }
// Device Name Validity Check
BOOL CAllDeviceList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count()) Enumerate();
for (unsigned u = 0; u < Count(); u++) if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef)) break;
return u < Count(); }
// CProfile member functions
// The following static functions fills the appropriate array using the
// profiles that match the search criteria goven.
void CProfile::Enumerate(ENUMTYPE& et, CStringArray& csaList) {
// Enumerate the existing profiles
DWORD dwBuffer =0, dwcProfiles;
csaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) { _RPTF2(_CRT_WARN, "CProfile::Enumerate(String)- empty list- dwBuffer %d Error %d\n", dwBuffer, GetLastError()); return; }
union { PBYTE pbBuffer; PTSTR pstrBuffer; };
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) { for (PTSTR pstrMe = pstrBuffer; dwcProfiles--; pstrMe += 1 + lstrlen(pstrMe)) { _RPTF1(_CRT_WARN, "CProfile::Enumerate(String) %s found\n", pstrMe); csaList.Add(pstrMe); } }
delete [] pbBuffer; } }
void CProfile::Enumerate(ENUMTYPE& et, CStringArray& csaList, CStringArray& csaDesc) {
// Enumerate the existing profiles
DWORD dwBuffer =0, dwcProfiles;
csaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) { _RPTF2(_CRT_WARN, "CProfile::Enumerate(String)- empty list- dwBuffer %d Error %d\n", dwBuffer, GetLastError()); return; }
union { PBYTE pbBuffer; PTSTR pstrBuffer; };
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) { for (PTSTR pstrMe = pstrBuffer; dwcProfiles--; pstrMe += 1 + lstrlen(pstrMe)) { _RPTF1(_CRT_WARN, "CProfile::Enumerate(String) %s found\n", pstrMe);
CProfile cp(pstrMe);
if (cp.IsValid()) {
CString csDescription = cp.TagContents('desc', 4);
if (csDescription.IsEmpty()) { csaDesc.Add(pstrMe); } else { csaDesc.Add((LPTSTR)csDescription); }
csaList.Add(pstrMe); } } }
delete [] pbBuffer; } }
void CProfile::Enumerate(ENUMTYPE& et, CProfileArray& cpaList) {
// Enumerate the existing profiles
DWORD dwBuffer = 0, dwcProfiles;
cpaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) { _RPTF2(_CRT_WARN, "CProfile::Enumerate(Profile)- empty list- dwBuffer %d Error %d\n", dwBuffer, GetLastError()); return; }
union { PBYTE pbBuffer; PTSTR pstrBuffer; };
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) { for (PTSTR pstrMe = pstrBuffer; dwcProfiles--; pstrMe += 1 + lstrlen(pstrMe)) { _RPTF1(_CRT_WARN, "CProfile::Enumerate(Profile) %s added\n", pstrMe); cpaList.Add(pstrMe); } }
delete [] pbBuffer; }
}
// This retrieves the color directory name. Since it is a const, we whouldn't
// be calling it too often...
const CString CProfile::ColorDirectory() { TCHAR acDirectory[MAX_PATH]; DWORD dwccDir = MAX_PATH;
GetColorDirectory(NULL, acDirectory, &dwccDir);
return acDirectory; }
// This checks for profile installation
void CProfile::InstallCheck() {
// Enumerate the existing profiles, so we can see if this one's been
// installed, already.
ENUMTYPE et = {sizeof (ENUMTYPE), ENUM_TYPE_VERSION, 0, NULL};
CStringArray csaWork;
Enumerate(et, csaWork);
for (unsigned u = 0; u < csaWork.Count(); u++) if (!lstrcmpi(csaWork[u].NameOnly(), m_csName.NameOnly())) break;
m_bIsInstalled = u < csaWork.Count(); m_bInstallChecked = TRUE; }
// This Checks for Associated Devices
void CProfile::AssociationCheck() {
m_bAssociationsChecked = TRUE;
// If the profile isn't installed, associations are moot...
if (!IsInstalled()) return;
// The final step is to build a list of associations
ENUMTYPE et = {sizeof (ENUMTYPE), ENUM_TYPE_VERSION, ET_DEVICENAME}; CStringArray csaWork;
for (unsigned u = 0; u < DeviceCount(); u++) {
et.pDeviceName = m_pcdlClass -> DeviceName(u);
Enumerate(et, csaWork);
// We track associations by index into the total device list...
for (unsigned uProfile = 0; uProfile < csaWork.Count(); uProfile++) if (!lstrcmpi(csaWork[uProfile].NameOnly(), m_csName.NameOnly())){ m_cuaAssociation.Add(u); // Found one!
break; } } }
// This determines the device list of related class...
void CProfile::DeviceCheck() {
// Enumerate the available devices of this type in the csaDevice Array
m_pcdlClass -> Enumerate(); m_bDevicesChecked = TRUE; }
// Class constructor
CProfile::CProfile(LPCTSTR lpstrTarget) {
_ASSERTE(lpstrTarget && *lpstrTarget);
m_pcdlClass = NULL; m_bIsInstalled = FALSE; m_bInstallChecked = FALSE; m_bDevicesChecked = FALSE; m_bAssociationsChecked = FALSE; // First, let's make sure it's the real McCoy
PROFILE prof = { PROFILE_FILENAME, (LPVOID) lpstrTarget, (1 + lstrlen(lpstrTarget)) * sizeof(TCHAR)};
m_hprof = OpenColorProfile(&prof, PROFILE_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, OPEN_EXISTING);
if (!m_hprof) return;
if (!GetColorProfileHeader(m_hprof, &m_phThis)) { CloseColorProfile(m_hprof); m_hprof = NULL; return; }
m_csName = lpstrTarget;
// Init the DeviceList pointer, because it doesn't cost much...
switch (m_phThis.phClass) { case CLASS_PRINTER:
// Our device list is a printer list
m_pcdlClass = new CPrinterList; break;
case CLASS_SCANNER:
// Our device list is a scanner list
m_pcdlClass = new CScannerList; break;
case CLASS_MONITOR:
// Our device list is a monitor list
#if 1 // ALLOW_MONITOR_PROFILE_TO_ANY_DEVICE
m_pcdlClass = new CAllDeviceList; #else
m_pcdlClass = new CMonitorList; #endif
break;
case CLASS_COLORSPACE:
// List everything we can count
m_pcdlClass = new CAllDeviceList; break;
default: // Use the base device class (i.e., no devices of this type).
m_pcdlClass = new CDeviceList; } }
// Destructor
CProfile::~CProfile() { if (m_hprof) CloseColorProfile(m_hprof); if (m_pcdlClass) delete m_pcdlClass; }
// Tag retrieval function
LPCSTR CProfile::TagContents(TAGTYPE tt, unsigned uOffset) {
DWORD dwcNeeded = sizeof m_acTag; BOOL bIgnore;
if (!GetColorProfileElement(m_hprof, tt, 8 + uOffset, &dwcNeeded, m_acTag, &bIgnore)) return NULL; // Nothing to copy!
else return m_acTag; }
// Profile Installation function
BOOL CProfile::Install() {
if (!InstallColorProfile(NULL, m_csName)) { CGlobals::ReportEx(InstFailedWithName,NULL,FALSE, MB_OK|MB_ICONEXCLAMATION,1,m_csName.NameAndExtension()); return (FALSE); } else { m_bIsInstalled = TRUE; CGlobals::InvalidateList(); SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, (LPCTSTR) m_csName, NULL);
_RPTF1(_CRT_WARN, "CProfile::Install %s succeeded\n", (LPCSTR) m_csName); return (TRUE); } }
// Profile Uninstallation function
void CProfile::Uninstall(BOOL bDelete) {
while (AssociationCount()) { // Dissociate all uses
Dissociate(DeviceName(m_cuaAssociation[0])); m_cuaAssociation.Remove(0); }
if (m_hprof) { CloseColorProfile(m_hprof); m_hprof = NULL; }
if (!UninstallColorProfile(NULL, m_csName.NameAndExtension(), bDelete)) { CGlobals::ReportEx(UninstFailedWithName,NULL,FALSE, MB_OK|MB_ICONEXCLAMATION,1,m_csName.NameAndExtension()); } else { m_bIsInstalled = FALSE; CGlobals::InvalidateList(); SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, (LPCTSTR) m_csName, NULL);
_RPTF1(_CRT_WARN, "CProfile::Uninstall %s succeeded\n", (LPCSTR) m_csName); } }
// Association
void CProfile::Associate(LPCTSTR lpstrDevice) {
// if the profile is not installed, install it first.
BOOL bInstalled = FALSE;
// Install profile, if not installed, yet.
if (!IsInstalled()) { bInstalled = Install(); } else bInstalled = TRUE;
if (bInstalled) { if (!AssociateColorProfileWithDevice(NULL, m_csName.NameAndExtension(), lpstrDevice)) { CGlobals::ReportEx(AssocFailedWithName,NULL,FALSE,1, MB_OK|MB_ICONEXCLAMATION,m_csName.NameAndExtension()); } else _RPTF2(_CRT_WARN, "CProfile::Associate %s with %s succeeded\n", lpstrDevice, (LPCTSTR) m_csName.NameAndExtension()); } }
// Dissociation
void CProfile::Dissociate(LPCTSTR lpstrDevice) { if (!DisassociateColorProfileFromDevice(NULL, m_csName.NameAndExtension(), lpstrDevice)) { CGlobals::ReportEx(DisassocFailedWithName,NULL,FALSE,1, MB_OK|MB_ICONEXCLAMATION,m_csName.NameAndExtension()); } else _RPTF2(_CRT_WARN, "CProfile::Dissociate %s from %s succeeded\n", lpstrDevice, (LPCTSTR) m_csName.NameAndExtension()); }
// CProfileArray class- Same basic implementation, different base type.
CProfile *CProfileArray::Borrow() { CProfile *pcpReturn = m_aStore[0];
memcpy((LPSTR) m_aStore, (LPSTR) (m_aStore + 1), (ChunkSize() - 1) * sizeof m_aStore[0]);
if (m_ucUsed > ChunkSize()) m_aStore[ChunkSize() - 1] = m_pcpaNext -> Borrow(); else m_aStore[ChunkSize() - 1] = (CProfile *) NULL;
m_ucUsed--;
if (m_ucUsed <= ChunkSize() && m_pcpaNext) { delete m_pcpaNext; m_pcpaNext = NULL; }
return pcpReturn; }
CProfileArray::CProfileArray() { m_ucUsed = 0; m_pcpaNext = NULL; memset(m_aStore, 0, sizeof m_aStore); }
CProfileArray::~CProfileArray() { Empty(); }
void CProfileArray::Empty() { if (!m_ucUsed) return;
if (m_pcpaNext) { delete m_pcpaNext; m_pcpaNext = NULL; m_ucUsed = ChunkSize(); }
while (m_ucUsed--) delete m_aStore[m_ucUsed];
m_ucUsed = 0; memset(m_aStore, 0, sizeof m_aStore); }
// Add an item
void CProfileArray::Add(LPCTSTR lpstrNew) { _ASSERTE(lpstrNew && *lpstrNew);
if (m_ucUsed < ChunkSize()) { m_aStore[m_ucUsed++] = new CProfile(lpstrNew); return; }
// Not enough space! Add another record, if there isn't one
if (!m_pcpaNext) m_pcpaNext = new CProfileArray;
// Add the profile to the next array (recursive call!)
// Note: if we failed to get memory above, we simply fail to add the
// object.
if (m_pcpaNext) { m_pcpaNext -> Add(lpstrNew); m_ucUsed++; } }
CProfile *CProfileArray::operator [](unsigned u) const { return u < ChunkSize() ? m_aStore[u] : m_pcpaNext -> operator[](u - ChunkSize()); }
void CProfileArray::Remove(unsigned u) {
if (u > m_ucUsed) return;
if (u >= ChunkSize()) { m_pcpaNext -> Remove(u - ChunkSize()); return; }
delete m_aStore[u];
memmove((LPSTR) (m_aStore + u), (LPSTR) (m_aStore + u + 1), (ChunkSize() - (u + 1)) * sizeof m_aStore[0]);
if (m_ucUsed > ChunkSize()) m_aStore[ChunkSize() - 1] = m_pcpaNext -> Borrow(); else m_aStore[ChunkSize() - 1] = (CProfile *) NULL;
m_ucUsed--;
if (m_ucUsed <= ChunkSize() && m_pcpaNext) { delete m_pcpaNext; m_pcpaNext = NULL; } }
|