|
|
/*****************************************************************************
Source File: Shell Extension Classes.CPP
This file implements the Shell Extension classes.
Copyright (c) 1996, 1997 by Microsoft Corporation. All Rights Reserved.
A Pretty Penny Enterprises Production
Change History:
10-28-96 A-RobKj (Pretty Penny Enterprises) began coding 12-04-96 A-RobKj Added the printer tab support 12-13-96 A-RobKj Modified for faster Icon extraction 01-07-97 KjelgaardR@ACM.Org Removed IContextMenu functions in favor of an exported ManageColorProfile procedure. This supplies a language-independent "Open" item that works with either mouse button. 01-08-97 KjelgaardR@acm.org Added utility routine to determine if a printer is a color model. Modified printer UI to only add pages for color printers.
******************************************************************************/
#include "ICMUI.H"
#include <shlobj.h>
#include <string.h>
#include <initguid.h>
#include <shfusion.h>
#include "ShellExt.H"
#include "Resource.H"
// Declare some storage space for the global statics
int CGlobals::m_icDLLReferences = 0; HMODULE CGlobals::m_hmThisDll = NULL; CStringArray CGlobals::m_csaProfiles; BOOL CGlobals::m_bIsValid = FALSE;
// Some global useful procs- an error reporter
void CGlobals::Report(int idError, HWND m_hwndParent) { CString csMessage, csTitle;
csMessage.Load(idError); csTitle.Load(MessageBoxTitle);
MessageBox(m_hwndParent, csMessage, csTitle, MB_OK|MB_ICONEXCLAMATION); }
int CGlobals::ReportEx(int idError, HWND m_hwndParent, BOOL bSystemMessage, UINT uType, DWORD dwNumMsg, ...) { CString csMessage, csTitle; va_list argList;
va_start(argList,dwNumMsg); csMessage.LoadAndFormat(idError,NULL,bSystemMessage,dwNumMsg,&argList); csTitle.Load(MessageBoxTitle); va_end(argList);
return (MessageBox(m_hwndParent, csMessage, csTitle, uType)); }
// A profile status checker
BOOL CGlobals::IsInstalled(CString& csProfile) { // if (!m_bIsValid) {
ENUMTYPE et = {sizeof et, ENUM_TYPE_VERSION, 0, NULL};
CProfile::Enumerate(et, m_csaProfiles); m_bIsValid = TRUE; // }
for (unsigned u = 0; u < m_csaProfiles.Count(); u++) if (!lstrcmpi(csProfile.NameOnly(), m_csaProfiles[u].NameOnly())) break;
return u < m_csaProfiles.Count(); }
// Utility routine to report if a printer is color or monochrome
BOOL CGlobals::ThisIsAColorPrinter(LPCTSTR lpstrName) { HDC hdcThis = CGlobals::GetPrinterHDC(lpstrName); BOOL bReturn = FALSE; if (hdcThis) { bReturn = 2 < (unsigned) GetDeviceCaps(hdcThis, NUMCOLORS); DeleteDC(hdcThis); } return bReturn; }
// Utility to determine the hdc for a printer
// The caller is responsible for calling
// DeleteDC() on the result
HDC CGlobals::GetPrinterHDC(LPCTSTR lpstrName) {
HANDLE hPrinter; // Get a handle on it...
LPTSTR lpstrMe = const_cast <LPTSTR> (lpstrName);
if (!OpenPrinter(lpstrMe, &hPrinter, NULL)) { _RPTF2(_CRT_WARN, "Unable to open printer '%s'- error %d\n", lpstrName, GetLastError()); return FALSE; }
// First, use DocumentProperties to find the correct DEVMODE size- we
// must use the DEVMODE to force color on, in case the user's defaults
// have turned it off...
unsigned short lcbNeeded = (unsigned short) DocumentProperties(NULL, hPrinter, lpstrMe, NULL, NULL, 0);
if (lcbNeeded <= 0) { _RPTF2(_CRT_WARN, "Document Properties (get size) for '%s' returned %d\n", lpstrName, lcbNeeded); ClosePrinter(hPrinter); return FALSE; }
HDC hdcThis = NULL;
union { LPBYTE lpb; LPDEVMODE lpdm; };
lpb = new BYTE[lcbNeeded];
if (lpb) {
ZeroMemory(lpb,lcbNeeded); lpdm -> dmSize = lcbNeeded; lpdm -> dmFields = DM_COLOR; lpdm -> dmColor = DMCOLOR_COLOR; if (IDOK == DocumentProperties(NULL, hPrinter, lpstrMe, lpdm, lpdm, DM_IN_BUFFER | DM_OUT_BUFFER)) {
// Turn off ICM, since not nessesary here.
//
lpdm -> dmICMMethod = DMICMMETHOD_NONE;
// Finally, we can create the DC!
// Note: we're not actually creating a DC, just an IC
hdcThis = CreateIC(NULL, lpstrName, NULL, lpdm); } else { _RPTF2(_CRT_WARN, "DocumentProperties (retrieve) on '%s' failed- error %d\n", lpstrName, GetLastError()); } delete lpb; } else _RPTF2(_CRT_WARN, "ThisIsAColorPrinter(%s) failed to get %d bytes\n", lpstrName, lcbNeeded);
ClosePrinter(hPrinter);
return hdcThis; }
// Required Shell Extension DLL interfaces
STDAPI DllCanUnloadNow() { return CGlobals::CanUnload(); }
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvOut) { return CIcmUiFactory::KeyToTheFactory(rclsid, riid, ppvOut); }
extern "C" int APIENTRY DllMain(HMODULE hmThis, DWORD dwReason, LPVOID lpvReserved) { #if defined(DEBUG) || defined(_DEBUG)
static HANDLE hfWarnings; // Log file
#endif
switch (dwReason) {
case DLL_PROCESS_ATTACH:
SHFusionInitializeFromModuleID(hmThis, SHFUSION_CPL_RESOURCE_ID); // Save the handle
CGlobals::SetHandle(hmThis); #if defined(DEBUG) || defined(_DEBUG)
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); hfWarnings = CreateFileA("C:\\ICMUIWarn.Txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfWarnings!= INVALID_HANDLE_VALUE) { SetFilePointer(hfWarnings, 0, NULL, FILE_END); _CrtSetReportFile(_CRT_WARN, hfWarnings); } _RPTF1(_CRT_WARN, "ICMUI DLL being loaded- handle %X\n", hmThis); #endif
break;
case DLL_PROCESS_DETACH:
#if defined(DEBUG) || defined(_DEBUG)
_RPTF0(_CRT_WARN, "ICMUI DLL being unloaded\n");
if (hfWarnings != INVALID_HANDLE_VALUE) CloseHandle(hfWarnings); #endif
SHFusionUninitialize();
}
return 1; }
// CIcmUiFactory member functions- these are used to provide external access
// to the class factory. The shell uses these to initialize extensions for
// both context menus and property sheets, both of which we provide,
// fortunately in the same object...
CIcmUiFactory::CIcmUiFactory(REFCLSID rclsid) { m_ulcReferences = 0; CGlobals::Attach(); if (IsEqualIID(rclsid, CLSID_ICM)) m_utThis = IsProfile; else if (IsEqualIID(rclsid, CLSID_PRINTERUI)) m_utThis = IsPrinter; else if (IsEqualIID(rclsid, CLSID_SCANNERUI)) m_utThis = IsScanner; else m_utThis = IsMonitor; }
STDMETHODIMP CIcmUiFactory::QueryInterface(REFIID riid, void **ppvObject) {
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) { *ppvObject = this; AddRef(); return NOERROR; } // Asked for an interface we ain't got!
*ppvObject = NULL; return E_NOINTERFACE; }
// IClassFactory interface functions
STDMETHODIMP CIcmUiFactory::CreateInstance(LPUNKNOWN punk, REFIID riid, void **ppvInstance) {
*ppvInstance = NULL;
if (punk) // We don't allow aggregation
return CLASS_E_NOAGGREGATION;
// We simply create a new ICM UI object, and return an interface to it.
// This will get queried by the shell for IExtShellInit, and the init job
// will be done.
CICMUserInterface *pcicmui = new CICMUserInterface(m_utThis);
if (!pcicmui) return E_OUTOFMEMORY;
// Let's be paranoid- if the QueryInterface failes, kill the ICMUI object,
// so we can still be unloaded!
HRESULT hrReturn = pcicmui -> QueryInterface(riid, ppvInstance);
if (!*ppvInstance) delete pcicmui;
return hrReturn; }
// Key to the factory is a static function that allows outsiders to instance
// the class factory. So, the caller will first instance the factory, then
// instance implementations of the interfaces it needs using the factory
// instance it receives from here.
HRESULT CIcmUiFactory::KeyToTheFactory(REFCLSID rclsid, REFIID riid, void **ppvObject) { *ppvObject = NULL;
if (!IsEqualIID(rclsid, CLSID_ICM) && !IsEqualIID(rclsid, CLSID_MONITORUI) && !IsEqualIID(rclsid, CLSID_SCANNERUI) && !IsEqualIID(rclsid, CLSID_PRINTERUI)) return CLASS_E_CLASSNOTAVAILABLE;
CIcmUiFactory *pciuf = new CIcmUiFactory(rclsid);
if (!pciuf) return E_OUTOFMEMORY;
HRESULT hrReturn = pciuf -> QueryInterface(riid, ppvObject);
if (!*ppvObject) delete pciuf;
return hrReturn; }
/******************************************************************************
ICM UI class methods- these do the true interface work of the DLL.
******************************************************************************/
CICMUserInterface::CICMUserInterface(UITYPE utThis) { m_lpdoTarget = NULL; m_ulcReferences = 0; m_utThis = utThis; CGlobals::Attach(); _RPTF2(_CRT_WARN, "CICMUserInterface(%d) constructed @ %lX\n", utThis, this); } // QueryInterface gets a bit long, but not too badly. The casts are needed
// because we use multiple inheritance- casting the this pointer to a base
// class actually returns a this pointer for that base class' part of the
// instance. Unlike single inheritance, the this pointer for the
// CICMUserInterface class does not directly reference ANY of the base
// classes.
STDMETHODIMP CICMUserInterface::QueryInterface(REFIID riid, void **ppvObject) { *ppvObject = NULL; // Assume the worst
// Since the device UI support a different set of functions, let's be
// particular about which interfaces we claim to support when...
if (m_utThis > IsProfile) { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellExtInit)) *ppvObject = (IShellExtInit *) this; if (IsEqualIID(riid, IID_IShellPropSheetExt)) *ppvObject = (IShellPropSheetExt *) this; } else { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IContextMenu)) *ppvObject = (IContextMenu *) this;
if (IsEqualIID(riid, IID_IShellExtInit)) *ppvObject = (IShellExtInit *) this;
if (IsEqualIID(riid, IID_IExtractIcon)) *ppvObject = (IExtractIcon *) this;
if (IsEqualIID(riid, IID_IPersistFile) || IsEqualIID(riid, IID_IPersist)) *ppvObject = (IPersistFile *) this;
if (IsEqualIID(riid, IID_IShellPropSheetExt)) *ppvObject = (IShellPropSheetExt *) this; }
if (*ppvObject) ((IUnknown *) *ppvObject) -> AddRef();
_RPTF2(_CRT_WARN, "CICMUserInterace::QueryInterface(%lX) returns %lX\n", this, ppvObject);
return *ppvObject ? NOERROR : E_NOINTERFACE; }
// IShellExtInit member function- this interface needs only one
STDMETHODIMP CICMUserInterface::Initialize(LPCITEMIDLIST pcidlFolder, LPDATAOBJECT pdoTarget, HKEY hKeyID) {
_RPTF0(_CRT_WARN, "CICMUserInterface::Initialize\n");
// The target data object is an HDROP, or list of files from the shell.
if (m_lpdoTarget) { m_lpdoTarget -> Release(); m_lpdoTarget = NULL; }
if (pdoTarget) { m_lpdoTarget = pdoTarget; m_lpdoTarget -> AddRef(); }
return NOERROR; }
// IExtractIcon interface functions- for now, we'll default to providing a
// default icon from our DLL. We provide one icon for installed profiles,
// and a second for uninstalled ones.
STDMETHODIMP CICMUserInterface::GetIconLocation(UINT uFlags, LPTSTR lpstrTarget, UINT uccTarget, int *piIndex, UINT *puFlags) {
*puFlags = (GIL_NOTFILENAME|GIL_DONTCACHE); // Make shell call our Extract function
// And don't cache in the callee.
return S_FALSE; }
STDMETHODIMP CICMUserInterface::Extract(LPCTSTR lpstrFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) {
*phiconSmall = *phiconLarge = LoadIcon(CGlobals::Instance(), MAKEINTRESOURCE(CGlobals::IsInstalled(m_csFile) ? DefaultIcon : UninstalledIcon));
return NOERROR; }
// IPersistFile functions- there's only one worth implementing
STDMETHODIMP CICMUserInterface::Load(LPCOLESTR lpwstrFileName, DWORD dwMode) { // This interface is used to initialize the icon handler- it will
// receive the profile name, which we will save for later use.
// The CString assigment operator handles any encoding converions needed
// encoding conversions for us.
m_csFile = lpwstrFileName;
return m_csFile.IsEmpty() ? E_OUTOFMEMORY : NO_ERROR; }
// IContextMenu functions-
STDMETHODIMP CICMUserInterface::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) {
// Only CMF_NORMAL and CMF_EXPLORE case will be handled.
//
// CMF_CANRENAME - This flag is set if the calling application supports
// renaming of items. A context menu extension or drag-and-drop
// handler should ignore this flag. A namespace extension should
// add a rename item to the menu if applicable.
// CMF_DEFAULTONLY - This flag is set when the user is activating the default action,
// typically by double-clicking. This flag provides a hint for the
// context menu extension to add nothing if it does not modify the
// default item in the menu. A context menu extension or drag-and-drop
// handler should not add any menu items if this value is specified.
// A namespace extension should add only the default item (if any).
// CMF_EXPLORE - This flag is set when Windows Explorer's tree window is present.
// Context menu handlers should ignore this value.
// CMF_INCLUDESTATIC - This flag is set when a static menu is being constructed.
// Only the browser should use this flag. All other context menu
// extensions should ignore this flag.
// CMF_NODEFAULT - This flag is set if no item in the menu should be the default item.
// A context menu extension or drag-and-drop handler should ignore this
// flag. A namespace extension should not set any of the menu items to
// the default.
// CMF_NORMAL - Indicates normal operation. A context menu extension, namespace extension,
// or drag-and-drop handler can add all menu items.
// CMF_NOVERBS - This flag is set for items displayed in the "Send To:" menu.
// Context menu handlers should ignore this value.
// CMF_VERBSONLY - This flag is set if the context menu is for a shortcut object.
// Context menu handlers should ignore this value.
if (((uFlags & 0x000F) == CMF_NORMAL) || (uFlags & CMF_EXPLORE)) { //
// Load the profile(s) in the list.
//
{ FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgm; HRESULT hres = m_lpdoTarget ? m_lpdoTarget -> GetData(&fmte, &stgm) : E_FAIL;
if (!SUCCEEDED(hres)) return NOERROR; // Why bother reporting a failure, here?
UINT ucFiles = stgm.hGlobal ? DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
if (!ucFiles) { ReleaseStgMedium(&stgm); return NOERROR; // Shouldn't happen, but it's not important
} else if (ucFiles == 1) m_bMultiSelection = FALSE; else m_bMultiSelection = TRUE;
// Assume in installed context, but we will scan the selected item
// is really installed everything.
m_bInstalledContext = TRUE;
TCHAR acFile[_MAX_PATH];
for (UINT u = 0; u < ucFiles; u++) {
DragQueryFile((HDROP) stgm.hGlobal, u, acFile, sizeof acFile/ sizeof acFile[0]);
CString csFile = acFile;
m_bInstalledContext = (m_bInstalledContext && CGlobals::IsInstalled(csFile)); }
ReleaseStgMedium(&stgm); }
UINT idCmd = idCmdFirst;
CString csInstallMenu, csAssociateMenu;
// If every profile(s) are already installed on this system,
// display "Uninstall Profile", otherwise display "Install Profile"
csInstallMenu.Load(m_bInstalledContext ? UninstallProfileMenuString : InstallProfileMenuString); ::InsertMenu(hMenu,indexMenu,MF_STRING|MF_BYPOSITION,idCmd,csInstallMenu);
// Set "Install Profile" or "Uninstall Profile" as default.
SetMenuDefaultItem(hMenu,indexMenu,TRUE);
// Increment Menu pos. and item id.
indexMenu++; idCmd++;
// Add "Associate..." menu item
csAssociateMenu.Load(AssociateMenuString); ::InsertMenu(hMenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd++,csAssociateMenu);
// But if we have multi selection, disable "Associate..."
if (m_bMultiSelection) ::EnableMenuItem(hMenu,(idCmd-1),MF_GRAYED); return (idCmd - idCmdFirst); // return number of menu inserted.
}
return NOERROR; }
STDMETHODIMP CICMUserInterface::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) {
// If HIWORD(lpcmi->lpVerb) then we have been called programmatically and
// lpVerb us a command that should be invoked. Otherwise, the shell has
// called us, abd LOWORD(lpcmi->lpVerb) is the menu ID the user has selected.
// Actually, it's (menu ID - icmdFirst) from QueryContextMenu().
if (!HIWORD((ULONG)(ULONG_PTR)lpcmi->lpVerb)) {
FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgm; HRESULT hres = m_lpdoTarget ? m_lpdoTarget -> GetData(&fmte, &stgm) : E_FAIL;
if (!SUCCEEDED(hres)) return NOERROR; // Why bother reporting a failure, here?
UINT ucFiles = stgm.hGlobal ? DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
if (!ucFiles) { ReleaseStgMedium(&stgm); return NOERROR; // Shouldn't happen, but it's not important
}
UINT idCmd = LOWORD(lpcmi->lpVerb);
// Walk through every selected item to install/uninstall.
for (UINT u = 0; u < ucFiles; u++) {
TCHAR acFile[_MAX_PATH];
DragQueryFile((HDROP) stgm.hGlobal, u, acFile, sizeof acFile/ sizeof acFile[0]);
switch (idCmd) {
case 0: { // Install/Uninstall was selected.
// during the installation or un-installation,
// change the cursor icon to IDC_APPSTARTING.
HCURSOR hCursorOld = SetCursor(LoadCursor(NULL,IDC_APPSTARTING));
CProfile csProfile(acFile);
if (m_bInstalledContext) {
// All selected profile is already installed, then
// Uninstall every profile(s) are selected if installed.
if (csProfile.IsInstalled()) { csProfile.Uninstall(FALSE); // never delete file from disk.
} } else {
// Some of selected profile is not installed, then
// Install every profile(s) are selected if not installed, yet.
if (!csProfile.IsInstalled()) { csProfile.Install(); } }
SetCursor(hCursorOld);
break; }
case 1: { // "Associate..." was selected.
CString csProfileName;
// Get profile "friendly" name.
{ CProfile csProfile(acFile); csProfileName = csProfile.GetName(); } // de-constructer for csProfile should be here.
// Create PropertySheet with "Profile Information" and
// "Associate Device" pages
PROPSHEETHEADER psh; HPROPSHEETPAGE hpsp[2];
CProfileInformationPage *pcpip = new CProfileInformationPage(CGlobals::Instance(), acFile); CProfileAssociationPage *pcpap = new CProfileAssociationPage(CGlobals::Instance(), acFile); if( (pcpip!=NULL)&&(pcpap!=NULL) ) { hpsp[0] = pcpip->Handle(); hpsp[1] = pcpap->Handle(); ZeroMemory(&psh, sizeof(PROPSHEETHEADER)); // fill the property sheet structure.
psh.dwSize = sizeof(PROPSHEETHEADER); psh.hInstance = CGlobals::Instance(); psh.hwndParent = NULL; psh.nStartPage = 1; // Active "Associate Device" page.
psh.nPages = 2; psh.phpage = hpsp; psh.pszCaption = csProfileName; PropertySheet(&psh); delete pcpip; delete pcpap; break; } else { if(pcpip) delete pcpip; if(pcpap) delete pcpap; return E_OUTOFMEMORY; } } } // switch (idCmd)
} // for (UINT u = 0; u < ucFiles; u++)
ReleaseStgMedium(&stgm);
} // if (!HIWORD(lpcmi->lpVerb))
return NOERROR; }
/* Supprisingly the code casts the unicode string to an
* asciiz string and passes it. One assumes that nobody * actually interprets the string pointer as ascii on the * way to its destination where it is reinterpreted * as a pointer to a unicode string. */ STDMETHODIMP CICMUserInterface::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax) { CString csReturnString;
switch (idCmd) { case 0: { // Install/Uninstall was selected.
if(m_bMultiSelection) { csReturnString.Load(m_bInstalledContext ? UninstallMultiProfileContextMenuString : InstallMultiProfileContextMenuString); } else { csReturnString.Load(m_bInstalledContext ? UninstallProfileContextMenuString : InstallProfileContextMenuString); } lstrcpyn((LPTSTR)pszName, csReturnString, cchMax); break; }
case 1: { // Associate... was seleted.
if (!m_bMultiSelection) { csReturnString.Load(AssociateContextMenuString); lstrcpyn((LPTSTR)pszName, csReturnString, cchMax); } break; } }
return NOERROR; }
// IPropSheetExt functions- again, we only need to implement one of these
// Since we now support two different interfaces, the actual implementation
// is done in a private method germane to the desired interface.
STDMETHODIMP CICMUserInterface::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) { _RPTF0(_CRT_WARN, "CICMUserInterface::AddPages\n");
HRESULT hResult = NOERROR; switch (m_utThis) { case IsProfile: { hResult = AddProfileTab(lpfnAddPage, lParam); if (hResult == NOERROR) { hResult = AddAssociateTab(lpfnAddPage, lParam); } break; }
case IsMonitor: { hResult = AddMonitorTab(lpfnAddPage, lParam); break; }
case IsPrinter: { hResult = AddPrinterTab(lpfnAddPage, lParam); break; }
case IsScanner: { hResult = AddScannerTab(lpfnAddPage, lParam); break; } }
return hResult; }
// This member function handles the ICC profile information sheet.
// In this case, the data object given via IShellExtInit::Initialize is
// an HDROP (list of fully qualified file names).
HRESULT CICMUserInterface::AddProfileTab(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) { _RPTF0(_CRT_WARN, "CICMUserInterface::AddProfileTab\n");
TCHAR acFile[_MAX_PATH];
// Load the profile(s) in the list.
if(m_lpdoTarget) { FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgm; HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
if (!SUCCEEDED(hres) || !stgm.hGlobal) return NOERROR; // Why bother reporting a failure, here?
UINT ucFiles = stgm.hGlobal ? DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
if (ucFiles != 1) { ReleaseStgMedium(&stgm); return NOERROR; }
DragQueryFile((HDROP) stgm.hGlobal, 0, acFile, sizeof acFile/ sizeof acFile[0]);
ReleaseStgMedium(&stgm); }
// Create the property sheet- it will get deleted if it is not in
// use when the shell tries to unload the extension
CProfileInformationPage *pcpip = new CProfileInformationPage(CGlobals::Instance(), acFile);
if ((pcpip != NULL && pcpip -> Handle()) && !(*lpfnAddPage)(pcpip -> Handle(), lParam)) DestroyPropertySheetPage(pcpip -> Handle());
return NOERROR; }
// This member function handles the associate device tab
HRESULT CICMUserInterface::AddAssociateTab(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) {
_RPTF0(_CRT_WARN, "CICMUserInterface::AddAssociateTab\n");
TCHAR acFile[_MAX_PATH];
// Load the profile(s) in the list.
if(m_lpdoTarget) { FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgm; HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
if (!SUCCEEDED(hres) || !stgm.hGlobal) return NOERROR; // Why bother reporting a failure, here?
UINT ucFiles = stgm.hGlobal ? DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
if (ucFiles != 1) { ReleaseStgMedium(&stgm); return NOERROR; }
DragQueryFile((HDROP) stgm.hGlobal, 0, acFile, sizeof acFile/ sizeof acFile[0]);
ReleaseStgMedium(&stgm); }
// Create the property sheet- it will get deleted if it is not in
// use when the shell tries to unload the extension
CProfileAssociationPage *pcpap = new CProfileAssociationPage(CGlobals::Instance(), acFile);
if ((pcpap != NULL && pcpap -> Handle()) && !(*lpfnAddPage)(pcpap -> Handle(), lParam)) DestroyPropertySheetPage(pcpap -> Handle());
return NOERROR; }
// This member function handles the monitor color management tab
// In this case, no data object is given.
// Private monitor enumeration function
HRESULT CICMUserInterface::AddMonitorTab(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) {
// Create the property sheet- it will get deleted if it is not in
// use when the shell tries to unload the extension
CString csMonitorDevice; CString csMonitorFriendlyName;
STGMEDIUM stgm; STGMEDIUM *pstgm = (STGMEDIUM *) NULL;
if (m_lpdoTarget) {
FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Display Device")), (DVTARGETDEVICE FAR *) NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; // Get device name from IDataObject.
HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
if (!SUCCEEDED(hres) || !stgm.hGlobal) { return NOERROR; // Why bother reporting a failure, here?
}
// The storage contains Display device path (\\.\DisplayX) in UNICODE.
pstgm = &stgm; LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(pstgm->hGlobal); CString csMonitorDevicePath = lpDeviceName;
// Query the device id, friendly name and other on the display device.
DISPLAY_DEVICE ddPriv;
ddPriv.cb = sizeof(ddPriv);
if (!EnumDisplayDevices((LPCTSTR)csMonitorDevicePath, 0, &ddPriv, 0)) { return NOERROR; // Why bother reporting a failure, here?
}
#if HIDEYUKN_DBG
MessageBox(NULL,csMonitorDevicePath,TEXT(""),MB_OK); MessageBox(NULL,(LPCTSTR)ddPriv.DeviceID,TEXT(""),MB_OK); MessageBox(NULL,(LPCTSTR)ddPriv.DeviceString,TEXT(""),MB_OK); #endif
// Use deviceId (PnP Id) as device name, and set friendly name
csMonitorDevice = (LPTSTR)(ddPriv.DeviceID); csMonitorFriendlyName = (LPTSTR)(ddPriv.DeviceString); } else { // if we don't have IDataObject, enumerate monitor,
// then use 1st entry.
CMonitorList cml; cml.Enumerate(); _ASSERTE(cml.Count()); // At least, we should have one Monitor.
csMonitorDevice = csMonitorFriendlyName = cml.DeviceName(0); }
CMonitorProfileManagement *pcmpm = new CMonitorProfileManagement(csMonitorDevice, csMonitorFriendlyName, CGlobals::Instance());
if ((pcmpm != NULL) && !(*lpfnAddPage)(pcmpm -> Handle(), lParam)) DestroyPropertySheetPage(pcmpm -> Handle());
if (pstgm) { GlobalUnlock(pstgm->hGlobal); ReleaseStgMedium(pstgm); }
return NOERROR; }
// Private scanner enumeration function
HRESULT CICMUserInterface::AddScannerTab(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) {
// Create the property sheet- it will get deleted if it is not in
// use when the shell tries to unload the extension
CString csScannerDevice;
STGMEDIUM stgm; STGMEDIUM *pstgm = (STGMEDIUM *) NULL;
if (m_lpdoTarget) { FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("STIDeviceName")), (DVTARGETDEVICE FAR *) NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
// Get device name from IDataObject.
HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
if (!SUCCEEDED(hres) || !stgm.hGlobal) { return NOERROR; // Why bother reporting a failure, here?
}
// The storage contains Scanner in UNICODE string.
pstgm = &stgm; LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(pstgm->hGlobal); csScannerDevice = lpDeviceName;
#if HIDEYUKN_DBG
MessageBox(NULL,csScannerDevice,TEXT(""),MB_OK); #endif
} else {
// if we don't have IDataObject, enumerate monitor,
// then use 1st entry.
CScannerList csl; csl.Enumerate(); _ASSERTE(csl.Count()); csScannerDevice = csl.DeviceName(0); }
CScannerProfileManagement *pcspm = new CScannerProfileManagement(csScannerDevice, CGlobals::Instance());
if ((pcspm != NULL) && !(*lpfnAddPage)(pcspm -> Handle(), lParam)) DestroyPropertySheetPage(pcspm -> Handle());
if (pstgm) { GlobalUnlock(pstgm->hGlobal); ReleaseStgMedium(pstgm); }
return NOERROR; }
// The following is a helper function- it takes a Shell ID List array,
// representing a printer in a printers folder, and a CString. It
// initializes the CString with the correct name of the printer.
static void RetrievePrinterName(LPIDA lpida, CString& csTarget) {
// Extract the container (Printers Folder) and target (Printer)
// IDs from the array.
LPCITEMIDLIST pciilContainer = (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[0]);
LPCITEMIDLIST pciilTarget = (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[1]);
if (!pciilContainer || !pciilTarget) return;
// Get a pointer to the printers folder.
LPSHELLFOLDER psfDesktop, psfPrinterFolder;
if (FAILED(SHGetDesktopFolder(&psfDesktop))) return;
if (FAILED(psfDesktop -> BindToObject(pciilContainer, NULL, IID_IShellFolder, (void **) &psfPrinterFolder))) { psfDesktop -> Release(); return; }
// Retrieve the printer's display name
STRRET strret;
if (FAILED(psfPrinterFolder -> GetDisplayNameOf(pciilTarget, SHGDN_FORPARSING, &strret))) { psfPrinterFolder -> Release(); psfDesktop -> Release(); return; }
// Copy the display name- the CString class now handles any encoding
// issues
switch (strret.uType) {
case STRRET_WSTR:
// This is a Unicode string which was IMalloc'd
csTarget = strret.pOleStr;
IMalloc *pim;
if (SUCCEEDED(CoGetMalloc(1, &pim))) { pim -> Free(strret.pOleStr); pim -> Release(); }
break;
case STRRET_CSTR:
// This is an ANSI string in the buffer
csTarget = strret.cStr; break;
case STRRET_OFFSET:
// This is an ANSI string at the given offset into the SHITEMID
// which pciilTarget points to.
csTarget = (LPCSTR) pciilTarget + strret.uOffset;
} psfPrinterFolder -> Release(); psfDesktop -> Release(); }
// Private member function for handling the printer profile manamgment tab.
HRESULT CICMUserInterface::AddPrinterTab(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) {
// The list is formatted as a Shell IDList Array
FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Shell IDList Array")), (DVTARGETDEVICE FAR *) NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgm; HRESULT hres = m_lpdoTarget ? m_lpdoTarget -> GetData(&fmte, &stgm) : 0;
if (!SUCCEEDED(hres) || !stgm.hGlobal) return NOERROR; // Why bother reporting a failure, here?
CString csPrinter;
RetrievePrinterName((LPIDA) stgm.hGlobal, csPrinter);
#if HIDEYUKN_DBG
MessageBox(NULL,csPrinter,TEXT(""),MB_OK); #endif
// If this is not a color printer, forget it...
if (!CGlobals::ThisIsAColorPrinter(csPrinter)) { ReleaseStgMedium(&stgm); return NOERROR; }
// Create the property sheet- it will get deleted if it is not in use when
// the shell tries to unload the extension
CPrinterProfileManagement *pcppm = new CPrinterProfileManagement(csPrinter, CGlobals::Instance());
ReleaseStgMedium(&stgm);
if (!pcppm) return E_OUTOFMEMORY;
if (!(*lpfnAddPage)(pcppm -> Handle(), lParam)) DestroyPropertySheetPage(pcppm -> Handle());
return NOERROR; }
PSTR GetFilenameFromPath( PSTR pPathName ) { DWORD dwLen; // length of pathname
dwLen = lstrlenA(pPathName);
//
// Go to the end of the pathname, and start going backwards till
// you reach the beginning or a backslash
//
pPathName += dwLen;
while (dwLen-- && --pPathName) { if (*pPathName == '\\') { pPathName++; break; } }
//
// if *pPathName is zero, then we had a string that ends in a backslash
//
return *pPathName ? pPathName : NULL; }
|