Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

shellext.cpp

Go to the documentation of this file.
00001 /****************************************************************************** 00002 00003 Source File: Shell Extension Classes.CPP 00004 00005 This file implements the Shell Extension classes. 00006 00007 Copyright (c) 1996, 1997 by Microsoft Corporation. All Rights Reserved. 00008 00009 A Pretty Penny Enterprises Production 00010 00011 Change History: 00012 00013 10-28-96 A-RobKj (Pretty Penny Enterprises) began coding 00014 12-04-96 A-RobKj Added the printer tab support 00015 12-13-96 A-RobKj Modified for faster Icon extraction 00016 01-07-97 [email protected] Removed IContextMenu functions in favor of 00017 an exported ManageColorProfile procedure. This supplies a 00018 language-independent "Open" item that works with either mouse 00019 button. 00020 01-08-97 [email protected] Added utility routine to determine if a printer 00021 is a color model. Modified printer UI to only add pages for color 00022 printers. 00023 00024 ******************************************************************************/ 00025 00026 #include "ICMUI.H" 00027 00028 #include <shlobj.h> 00029 #include <string.h> 00030 00031 #include <initguid.h> 00032 #include "ShellExt.H" 00033 #include "Resource.H" 00034 00035 // Declare some storage space for the global statics 00036 00037 int CGlobals::m_icDLLReferences = 0; 00038 HMODULE CGlobals::m_hmThisDll = NULL; 00039 CStringArray CGlobals::m_csaProfiles; 00040 BOOL CGlobals::m_bIsValid = FALSE; 00041 00042 // Some global useful procs- an error reporter 00043 00044 void CGlobals::Report(int idError, HWND m_hwndParent) { 00045 CString csMessage, csTitle; 00046 00047 csMessage.Load(idError); 00048 csTitle.Load(MessageBoxTitle); 00049 00050 MessageBox(m_hwndParent, csMessage, csTitle, MB_OK|MB_ICONEXCLAMATION); 00051 } 00052 00053 int CGlobals::ReportEx(int idError, HWND m_hwndParent, 00054 BOOL bSystemMessage, UINT uType, DWORD dwNumMsg, ...) { 00055 CString csMessage, csTitle; 00056 va_list argList; 00057 00058 va_start(argList,dwNumMsg); 00059 csMessage.LoadAndFormat(idError,NULL,bSystemMessage,dwNumMsg,&argList); 00060 csTitle.Load(MessageBoxTitle); 00061 va_end(argList); 00062 00063 return (MessageBox(m_hwndParent, csMessage, csTitle, uType)); 00064 } 00065 00066 // A profile status checker 00067 00068 BOOL CGlobals::IsInstalled(CString& csProfile) { 00069 // if (!m_bIsValid) { 00070 ENUMTYPE et = {sizeof et, ENUM_TYPE_VERSION, 0, NULL}; 00071 00072 CProfile::Enumerate(et, m_csaProfiles); 00073 m_bIsValid = TRUE; 00074 // } 00075 00076 for (unsigned u = 0; u < m_csaProfiles.Count(); u++) 00077 if (!lstrcmpi(csProfile.NameOnly(), m_csaProfiles[u].NameOnly())) 00078 break; 00079 00080 return u < m_csaProfiles.Count(); 00081 } 00082 00083 // Utility routine to report if a printer is color or monochrome 00084 BOOL CGlobals::ThisIsAColorPrinter(LPCTSTR lpstrName) { 00085 HDC hdcThis = CGlobals::GetPrinterHDC(lpstrName); 00086 BOOL bReturn = FALSE; 00087 if (hdcThis) { 00088 bReturn = 2 < (unsigned) GetDeviceCaps(hdcThis, NUMCOLORS); 00089 DeleteDC(hdcThis); 00090 } 00091 return bReturn; 00092 } 00093 00094 // Utility to determine the hdc for a printer 00095 // The caller is responsible for calling 00096 // DeleteDC() on the result 00097 HDC CGlobals::GetPrinterHDC(LPCTSTR lpstrName) { 00098 00099 HANDLE hPrinter; // Get a handle on it... 00100 LPTSTR lpstrMe = const_cast <LPTSTR> (lpstrName); 00101 00102 if (!OpenPrinter(lpstrMe, &hPrinter, NULL)) { 00103 _RPTF2(_CRT_WARN, "Unable to open printer '%s'- error %d\n", lpstrName, 00104 GetLastError()); 00105 return FALSE; 00106 } 00107 00108 // First, use DocumentProperties to find the correct DEVMODE size- we 00109 // must use the DEVMODE to force color on, in case the user's defaults 00110 // have turned it off... 00111 00112 unsigned short lcbNeeded = (unsigned short) DocumentProperties(NULL, hPrinter, lpstrMe, NULL, 00113 NULL, 0); 00114 00115 if (lcbNeeded <= 0) { 00116 _RPTF2(_CRT_WARN, 00117 "Document Properties (get size) for '%s' returned %d\n", lpstrName, 00118 lcbNeeded); 00119 ClosePrinter(hPrinter); 00120 return FALSE; 00121 } 00122 00123 HDC hdcThis = NULL; 00124 00125 union { 00126 LPBYTE lpb; 00127 LPDEVMODE lpdm; 00128 }; 00129 00130 lpb = new BYTE[lcbNeeded]; 00131 00132 if (lpb) { 00133 00134 ZeroMemory(lpb,lcbNeeded); 00135 lpdm -> dmSize = lcbNeeded; 00136 lpdm -> dmFields = DM_COLOR; 00137 lpdm -> dmColor = DMCOLOR_COLOR; 00138 if (IDOK == DocumentProperties(NULL, hPrinter, lpstrMe, lpdm, lpdm, 00139 DM_IN_BUFFER | DM_OUT_BUFFER)) { 00140 00141 // Turn off ICM, since not nessesary here. 00142 // 00143 lpdm -> dmICMMethod = DMICMMETHOD_NONE; 00144 00145 // Finally, we can create the DC! 00146 // Note: we're not actually creating a DC, just an IC 00147 hdcThis = CreateIC(NULL, lpstrName, NULL, lpdm); 00148 } else { 00149 _RPTF2(_CRT_WARN, 00150 "DocumentProperties (retrieve) on '%s' failed- error %d\n", 00151 lpstrName, GetLastError()); 00152 } 00153 delete lpb; 00154 } 00155 else 00156 _RPTF2(_CRT_WARN, "ThisIsAColorPrinter(%s) failed to get %d bytes\n", 00157 lpstrName, lcbNeeded); 00158 00159 ClosePrinter(hPrinter); 00160 00161 return hdcThis; 00162 } 00163 00164 // Required Shell Extension DLL interfaces 00165 00166 STDAPI DllCanUnloadNow() { 00167 return CGlobals::CanUnload(); 00168 } 00169 00170 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvOut) { 00171 return CIcmUiFactory::KeyToTheFactory(rclsid, riid, ppvOut); 00172 } 00173 00174 extern "C" int APIENTRY DllMain(HMODULE hmThis, DWORD dwReason, 00175 LPVOID lpvReserved) { 00176 #if defined(DEBUG) || defined(_DEBUG) 00177 static HANDLE hfWarnings; // Log file 00178 #endif 00179 switch (dwReason) { 00180 00181 case DLL_PROCESS_ATTACH: 00182 00183 // Save the handle 00184 CGlobals::SetHandle(hmThis); 00185 #if defined(DEBUG) || defined(_DEBUG) 00186 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW); 00187 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); 00188 hfWarnings = CreateFileA("C:\\ICMUIWarn.Txt", GENERIC_WRITE, 0, 00189 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 00190 00191 if (hfWarnings!= INVALID_HANDLE_VALUE) { 00192 SetFilePointer(hfWarnings, 0, NULL, FILE_END); 00193 _CrtSetReportFile(_CRT_WARN, hfWarnings); 00194 } 00195 _RPTF1(_CRT_WARN, "ICMUI DLL being loaded- handle %X\n", hmThis); 00196 break; 00197 00198 case DLL_PROCESS_DETACH: 00199 _RPTF0(_CRT_WARN, "ICMUI DLL being unloaded\n"); 00200 00201 if (hfWarnings != INVALID_HANDLE_VALUE) 00202 CloseHandle(hfWarnings); 00203 #endif 00204 } 00205 00206 return 1; 00207 } 00208 00209 // CIcmUiFactory member functions- these are used to provide external access 00210 // to the class factory. The shell uses these to initialize extensions for 00211 // both context menus and property sheets, both of which we provide, 00212 // fortunately in the same object... 00213 00214 CIcmUiFactory::CIcmUiFactory(REFCLSID rclsid) { 00215 m_ulcReferences = 0; 00216 CGlobals::Attach(); 00217 if (IsEqualIID(rclsid, CLSID_ICM)) 00218 m_utThis = IsProfile; 00219 else if (IsEqualIID(rclsid, CLSID_PRINTERUI)) 00220 m_utThis = IsPrinter; 00221 else if (IsEqualIID(rclsid, CLSID_SCANNERUI)) 00222 m_utThis = IsScanner; 00223 else 00224 m_utThis = IsMonitor; 00225 } 00226 00227 STDMETHODIMP CIcmUiFactory::QueryInterface(REFIID riid, void **ppvObject) { 00228 00229 if (IsEqualIID(riid, IID_IUnknown) || 00230 IsEqualIID(riid, IID_IClassFactory)) { 00231 *ppvObject = this; 00232 AddRef(); 00233 return NOERROR; 00234 } 00235 // Asked for an interface we ain't got! 00236 *ppvObject = NULL; 00237 return E_NOINTERFACE; 00238 } 00239 00240 // IClassFactory interface functions 00241 00242 STDMETHODIMP CIcmUiFactory::CreateInstance(LPUNKNOWN punk, REFIID riid, 00243 void **ppvInstance) { 00244 00245 *ppvInstance = NULL; 00246 00247 if (punk) // We don't allow aggregation 00248 return CLASS_E_NOAGGREGATION; 00249 00250 // We simply create a new ICM UI object, and return an interface to it. 00251 // This will get queried by the shell for IExtShellInit, and the init job 00252 // will be done. 00253 00254 CICMUserInterface *pcicmui = new CICMUserInterface(m_utThis); 00255 00256 if (!pcicmui) 00257 return E_OUTOFMEMORY; 00258 00259 // Let's be paranoid- if the QueryInterface failes, kill the ICMUI object, 00260 // so we can still be unloaded! 00261 00262 HRESULT hrReturn = pcicmui -> QueryInterface(riid, ppvInstance); 00263 00264 if (!*ppvInstance) 00265 delete pcicmui; 00266 00267 return hrReturn; 00268 } 00269 00270 00271 // Key to the factory is a static function that allows outsiders to instance 00272 // the class factory. So, the caller will first instance the factory, then 00273 // instance implementations of the interfaces it needs using the factory 00274 // instance it receives from here. 00275 00276 HRESULT CIcmUiFactory::KeyToTheFactory(REFCLSID rclsid, REFIID riid, 00277 void **ppvObject) { 00278 00279 *ppvObject = NULL; 00280 00281 if (!IsEqualIID(rclsid, CLSID_ICM) && 00282 !IsEqualIID(rclsid, CLSID_MONITORUI) && 00283 !IsEqualIID(rclsid, CLSID_SCANNERUI) && 00284 !IsEqualIID(rclsid, CLSID_PRINTERUI)) 00285 return CLASS_E_CLASSNOTAVAILABLE; 00286 00287 CIcmUiFactory *pciuf = new CIcmUiFactory(rclsid); 00288 00289 if (!pciuf) 00290 return E_OUTOFMEMORY; 00291 00292 HRESULT hrReturn = pciuf -> QueryInterface(riid, ppvObject); 00293 00294 if (!*ppvObject) 00295 delete pciuf; 00296 00297 return hrReturn; 00298 } 00299 00300 /****************************************************************************** 00301 00302 ICM UI class methods- these do the true interface work of the DLL. 00303 00304 ******************************************************************************/ 00305 00306 00307 CICMUserInterface::CICMUserInterface(UITYPE utThis) { 00308 m_lpdoTarget = NULL; 00309 m_ulcReferences = 0; 00310 m_utThis = utThis; 00311 CGlobals::Attach(); 00312 _RPTF2(_CRT_WARN, "CICMUserInterface(%d) constructed @ %lX\n", utThis, this); 00313 } 00314 // QueryInterface gets a bit long, but not too badly. The casts are needed 00315 // because we use multiple inheritance- casting the this pointer to a base 00316 // class actually returns a this pointer for that base class' part of the 00317 // instance. Unlike single inheritance, the this pointer for the 00318 // CICMUserInterface class does not directly reference ANY of the base 00319 // classes. 00320 00321 STDMETHODIMP CICMUserInterface::QueryInterface(REFIID riid, 00322 void **ppvObject) { 00323 *ppvObject = NULL; // Assume the worst 00324 // Since the device UI support a different set of functions, let's be 00325 // particular about which interfaces we claim to support when... 00326 if (m_utThis > IsProfile) { 00327 if (IsEqualIID(riid, IID_IUnknown) || 00328 IsEqualIID(riid, IID_IShellExtInit)) 00329 *ppvObject = (IShellExtInit *) this; 00330 if (IsEqualIID(riid, IID_IShellPropSheetExt)) 00331 *ppvObject = (IShellPropSheetExt *) this; 00332 } 00333 else { 00334 if (IsEqualIID(riid, IID_IUnknown) || 00335 IsEqualIID(riid, IID_IContextMenu)) 00336 *ppvObject = (IContextMenu *) this; 00337 00338 if (IsEqualIID(riid, IID_IShellExtInit)) 00339 *ppvObject = (IShellExtInit *) this; 00340 00341 if (IsEqualIID(riid, IID_IExtractIcon)) 00342 *ppvObject = (IExtractIcon *) this; 00343 00344 if (IsEqualIID(riid, IID_IPersistFile) || 00345 IsEqualIID(riid, IID_IPersist)) 00346 *ppvObject = (IPersistFile *) this; 00347 00348 if (IsEqualIID(riid, IID_IShellPropSheetExt)) 00349 *ppvObject = (IShellPropSheetExt *) this; 00350 } 00351 00352 if (*ppvObject) 00353 ((IUnknown *) *ppvObject) -> AddRef(); 00354 00355 _RPTF2(_CRT_WARN, "CICMUserInterace::QueryInterface(%lX) returns %lX\n", 00356 this, ppvObject); 00357 00358 return *ppvObject ? NOERROR : E_NOINTERFACE; 00359 } 00360 00361 // IShellExtInit member function- this interface needs only one 00362 00363 STDMETHODIMP CICMUserInterface::Initialize(LPCITEMIDLIST pcidlFolder, 00364 LPDATAOBJECT pdoTarget, 00365 HKEY hKeyID) { 00366 00367 _RPTF0(_CRT_WARN, "CICMUserInterface::Initialize\n"); 00368 00369 // The target data object is an HDROP, or list of files from the shell. 00370 00371 if (m_lpdoTarget) { 00372 m_lpdoTarget -> Release(); 00373 m_lpdoTarget = NULL; 00374 } 00375 00376 if (pdoTarget) { 00377 m_lpdoTarget = pdoTarget; 00378 m_lpdoTarget -> AddRef(); 00379 } 00380 00381 return NOERROR; 00382 } 00383 00384 // IExtractIcon interface functions- for now, we'll default to providing a 00385 // default icon from our DLL. We provide one icon for installed profiles, 00386 // and a second for uninstalled ones. 00387 00388 STDMETHODIMP CICMUserInterface::GetIconLocation(UINT uFlags, 00389 LPTSTR lpstrTarget, 00390 UINT uccTarget, 00391 int *piIndex, 00392 UINT *puFlags) { 00393 00394 *puFlags = (GIL_NOTFILENAME|GIL_DONTCACHE); // Make shell call our Extract function 00395 // And don't cache in the callee. 00396 00397 return S_FALSE; 00398 } 00399 00400 STDMETHODIMP CICMUserInterface::Extract(LPCTSTR lpstrFile, UINT nIconIndex, 00401 HICON *phiconLarge, HICON *phiconSmall, 00402 UINT nIconSize) { 00403 00404 *phiconSmall = *phiconLarge = LoadIcon(CGlobals::Instance(), 00405 MAKEINTRESOURCE(CGlobals::IsInstalled(m_csFile) ? DefaultIcon : UninstalledIcon)); 00406 00407 return NOERROR; 00408 } 00409 00410 // IPersistFile functions- there's only one worth implementing 00411 00412 STDMETHODIMP CICMUserInterface::Load(LPCOLESTR lpwstrFileName, 00413 DWORD dwMode) { 00414 // This interface is used to initialize the icon handler- it will 00415 // receive the profile name, which we will save for later use. 00416 // The CString assigment operator handles any encoding converions needed 00417 // encoding conversions for us. 00418 00419 m_csFile = lpwstrFileName; 00420 00421 return m_csFile.IsEmpty() ? E_OUTOFMEMORY : NO_ERROR; 00422 } 00423 00424 // IContextMenu functions- 00425 00426 STDMETHODIMP CICMUserInterface::QueryContextMenu(HMENU hMenu, UINT indexMenu, 00427 UINT idCmdFirst, UINT idCmdLast, 00428 UINT uFlags) { 00429 00430 // Only CMF_NORMAL and CMF_EXPLORE case will be handled. 00431 // 00432 // CMF_CANRENAME - This flag is set if the calling application supports 00433 // renaming of items. A context menu extension or drag-and-drop 00434 // handler should ignore this flag. A namespace extension should 00435 // add a rename item to the menu if applicable. 00436 // CMF_DEFAULTONLY - This flag is set when the user is activating the default action, 00437 // typically by double-clicking. This flag provides a hint for the 00438 // context menu extension to add nothing if it does not modify the 00439 // default item in the menu. A context menu extension or drag-and-drop 00440 // handler should not add any menu items if this value is specified. 00441 // A namespace extension should add only the default item (if any). 00442 // CMF_EXPLORE - This flag is set when Windows Explorer's tree window is present. 00443 // Context menu handlers should ignore this value. 00444 // CMF_INCLUDESTATIC - This flag is set when a static menu is being constructed. 00445 // Only the browser should use this flag. All other context menu 00446 // extensions should ignore this flag. 00447 // CMF_NODEFAULT - This flag is set if no item in the menu should be the default item. 00448 // A context menu extension or drag-and-drop handler should ignore this 00449 // flag. A namespace extension should not set any of the menu items to 00450 // the default. 00451 // CMF_NORMAL - Indicates normal operation. A context menu extension, namespace extension, 00452 // or drag-and-drop handler can add all menu items. 00453 // CMF_NOVERBS - This flag is set for items displayed in the "Send To:" menu. 00454 // Context menu handlers should ignore this value. 00455 // CMF_VERBSONLY - This flag is set if the context menu is for a shortcut object. 00456 // Context menu handlers should ignore this value. 00457 00458 if (((uFlags & 0x000F) == CMF_NORMAL) || (uFlags & CMF_EXPLORE)) 00459 { 00460 // 00461 // Load the profile(s) in the list. 00462 // 00463 FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, 00464 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00465 STGMEDIUM stgm; 00466 HRESULT hres = m_lpdoTarget ? 00467 m_lpdoTarget -> GetData(&fmte, &stgm) : 0; 00468 00469 if (!SUCCEEDED(hres)) 00470 return NOERROR; // Why bother reporting a failure, here? 00471 00472 UINT ucFiles = stgm.hGlobal ? 00473 DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0; 00474 00475 if (!ucFiles) 00476 return NOERROR; // Shouldn't happen, but it's not important 00477 else if (ucFiles == 1) 00478 m_bMultiSelection = FALSE; 00479 else 00480 m_bMultiSelection = TRUE; 00481 00482 // Assume in installed context, but we will scan the selected item 00483 // is really installed everything. 00484 00485 m_bInstalledContext = TRUE; 00486 00487 TCHAR acFile[_MAX_PATH]; 00488 00489 for (UINT u = 0; u < ucFiles; u++) { 00490 00491 DragQueryFile((HDROP) stgm.hGlobal, u, acFile, 00492 sizeof acFile/ sizeof acFile[0]); 00493 00494 CString csFile = acFile; 00495 00496 m_bInstalledContext = (m_bInstalledContext && CGlobals::IsInstalled(csFile)); 00497 } 00498 00499 UINT idCmd = idCmdFirst; 00500 00501 CString csInstallMenu, csAssociateMenu; 00502 00503 // If every profile(s) are already installed on this system, 00504 // display "Uninstall Profile", otherwise display "Install Profile" 00505 00506 csInstallMenu.Load(m_bInstalledContext ? UninstallProfileMenuString : InstallProfileMenuString); 00507 ::InsertMenu(hMenu,indexMenu,MF_STRING|MF_BYPOSITION,idCmd,csInstallMenu); 00508 00509 // Set "Install Profile" or "Uninstall Profile" as default. 00510 00511 SetMenuDefaultItem(hMenu,indexMenu,TRUE); 00512 00513 // Increment Menu pos. and item id. 00514 00515 indexMenu++; idCmd++; 00516 00517 // Add "Associate..." menu item 00518 00519 csAssociateMenu.Load(AssociateMenuString); 00520 ::InsertMenu(hMenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd++,csAssociateMenu); 00521 00522 // But if we have multi selection, disable "Associate..." 00523 00524 if (m_bMultiSelection) 00525 ::EnableMenuItem(hMenu,(idCmd-1),MF_GRAYED); 00526 return (idCmd - idCmdFirst); // return number of menu inserted. 00527 } 00528 00529 return NOERROR; 00530 } 00531 00532 STDMETHODIMP CICMUserInterface::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { 00533 00534 // If HIWORD(lpcmi->lpVerb) then we have been called programmatically and 00535 // lpVerb us a command that should be invoked. Otherwise, the shell has 00536 // called us, abd LOWORD(lpcmi->lpVerb) is the menu ID the user has selected. 00537 // Actually, it's (menu ID - icmdFirst) from QueryContextMenu(). 00538 00539 if (!HIWORD((ULONG)(ULONG_PTR)lpcmi->lpVerb)) { 00540 00541 FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, 00542 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00543 STGMEDIUM stgm; 00544 HRESULT hres = m_lpdoTarget ? 00545 m_lpdoTarget -> GetData(&fmte, &stgm) : 0; 00546 00547 if (!SUCCEEDED(hres)) 00548 return NOERROR; // Why bother reporting a failure, here? 00549 00550 UINT ucFiles = stgm.hGlobal ? 00551 DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0; 00552 00553 if (!ucFiles) 00554 return NOERROR; // Shouldn't happen, but it's not important 00555 00556 UINT idCmd = LOWORD(lpcmi->lpVerb); 00557 00558 // Walk through every selected item to install/uninstall. 00559 00560 for (UINT u = 0; u < ucFiles; u++) { 00561 00562 TCHAR acFile[_MAX_PATH]; 00563 00564 DragQueryFile((HDROP) stgm.hGlobal, u, acFile, 00565 sizeof acFile/ sizeof acFile[0]); 00566 00567 switch (idCmd) { 00568 00569 case 0: { // Install/Uninstall was selected. 00570 00571 // during the installation or un-installation, 00572 // change the cursor icon to IDC_APPSTARTING. 00573 00574 HCURSOR hCursorOld = SetCursor(LoadCursor(NULL,IDC_APPSTARTING)); 00575 00576 CProfile csProfile(acFile); 00577 00578 if (m_bInstalledContext) { 00579 00580 // All selected profile is already installed, then 00581 // Uninstall every profile(s) are selected if installed. 00582 00583 if (csProfile.IsInstalled()) { 00584 csProfile.Uninstall(FALSE); // never delete file from disk. 00585 } 00586 } 00587 else { 00588 00589 // Some of selected profile is not installed, then 00590 // Install every profile(s) are selected if not installed, yet. 00591 00592 if (!csProfile.IsInstalled()) { 00593 csProfile.Install(); 00594 } 00595 } 00596 00597 SetCursor(hCursorOld); 00598 00599 break; 00600 } 00601 00602 case 1: { // "Associate..." was selected. 00603 00604 CString csProfileName; 00605 00606 // Get profile "friendly" name. 00607 { 00608 CProfile csProfile(acFile); 00609 csProfileName = csProfile.GetName(); 00610 } // de-constructer for csProfile should be here. 00611 00612 // Create PropertySheet with "Profile Information" and 00613 // "Associate Device" pages 00614 00615 PROPSHEETHEADER psh; 00616 HPROPSHEETPAGE hpsp[2]; 00617 00618 CProfileInformationPage *pcpip = 00619 new CProfileInformationPage(CGlobals::Instance(), acFile); 00620 CProfileAssociationPage *pcpap = 00621 new CProfileAssociationPage(CGlobals::Instance(), acFile); 00622 if( (pcpip!=NULL)&&(pcpap!=NULL) ) { 00623 hpsp[0] = pcpip->Handle(); 00624 hpsp[1] = pcpap->Handle(); 00625 00626 ZeroMemory(&psh, sizeof(PROPSHEETHEADER)); 00627 00628 // fill the property sheet structure. 00629 00630 psh.dwSize = sizeof(PROPSHEETHEADER); 00631 psh.hInstance = CGlobals::Instance(); 00632 psh.hwndParent = NULL; 00633 psh.nStartPage = 1; // Active "Associate Device" page. 00634 psh.nPages = 2; 00635 psh.phpage = hpsp; 00636 psh.pszCaption = csProfileName; 00637 00638 PropertySheet(&psh); 00639 00640 delete pcpip; delete pcpap; 00641 break; 00642 } else { 00643 if(pcpip) delete pcpip; 00644 if(pcpap) delete pcpap; 00645 return E_OUTOFMEMORY; 00646 } 00647 } 00648 } // switch (idCmd) 00649 } // for (UINT u = 0; u < ucFiles; u++) 00650 } // if (!HIWORD(lpcmi->lpVerb)) 00651 00652 return NOERROR; 00653 } 00654 00655 /* Supprisingly the code casts the unicode string to an 00656 * asciiz string and passes it. One assumes that nobody 00657 * actually interprets the string pointer as ascii on the 00658 * way to its destination where it is reinterpreted 00659 * as a pointer to a unicode string. 00660 */ 00661 STDMETHODIMP CICMUserInterface::GetCommandString(UINT_PTR idCmd, UINT uFlags, 00662 UINT FAR *reserved, LPSTR pszName, 00663 UINT cchMax) { 00664 CString csReturnString; 00665 00666 switch (idCmd) { 00667 case 0: { // Install/Uninstall was selected. 00668 if(m_bMultiSelection) { 00669 csReturnString.Load(m_bInstalledContext ? UninstallMultiProfileContextMenuString : InstallMultiProfileContextMenuString); 00670 } else { 00671 csReturnString.Load(m_bInstalledContext ? UninstallProfileContextMenuString : InstallProfileContextMenuString); 00672 } 00673 lstrcpyn((LPTSTR)pszName, csReturnString, cchMax); 00674 break; 00675 } 00676 00677 case 1: { // Associate... was seleted. 00678 if (!m_bMultiSelection) { 00679 csReturnString.Load(AssociateContextMenuString); 00680 lstrcpyn((LPTSTR)pszName, csReturnString, cchMax); 00681 } 00682 break; 00683 } 00684 } 00685 00686 return NOERROR; 00687 } 00688 00689 // IPropSheetExt functions- again, we only need to implement one of these 00690 // Since we now support two different interfaces, the actual implementation 00691 // is done in a private method germane to the desired interface. 00692 00693 STDMETHODIMP CICMUserInterface::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, 00694 LPARAM lParam) { 00695 _RPTF0(_CRT_WARN, "CICMUserInterface::AddPages\n"); 00696 00697 HRESULT hResult = NOERROR; 00698 00699 switch (m_utThis) { 00700 case IsProfile: { 00701 hResult = AddProfileTab(lpfnAddPage, lParam); 00702 if (hResult == NOERROR) { 00703 hResult = AddAssociateTab(lpfnAddPage, lParam); 00704 } 00705 break; 00706 } 00707 00708 case IsMonitor: { 00709 hResult = AddMonitorTab(lpfnAddPage, lParam); 00710 break; 00711 } 00712 00713 case IsPrinter: { 00714 hResult = AddPrinterTab(lpfnAddPage, lParam); 00715 break; 00716 } 00717 00718 case IsScanner: { 00719 hResult = AddScannerTab(lpfnAddPage, lParam); 00720 break; 00721 } 00722 } 00723 00724 return hResult; 00725 } 00726 00727 // This member function handles the ICC profile information sheet. 00728 // In this case, the data object given via IShellExtInit::Initialize is 00729 // an HDROP (list of fully qualified file names). 00730 00731 HRESULT CICMUserInterface::AddProfileTab(LPFNADDPROPSHEETPAGE lpfnAddPage, 00732 LPARAM lParam) { 00733 _RPTF0(_CRT_WARN, "CICMUserInterface::AddProfileTab\n"); 00734 00735 // Load the profile(s) in the list. 00736 00737 FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, 00738 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00739 STGMEDIUM stgm; 00740 HRESULT hres = m_lpdoTarget ? 00741 m_lpdoTarget -> GetData(&fmte, &stgm) : 0; 00742 00743 00744 if (!SUCCEEDED(hres)) 00745 return NOERROR; // Why bother reporting a failure, here? 00746 00747 UINT ucFiles = stgm.hGlobal ? 00748 DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0; 00749 00750 if (ucFiles != 1) 00751 return NOERROR; 00752 00753 TCHAR acFile[_MAX_PATH]; 00754 00755 DragQueryFile((HDROP) stgm.hGlobal, 0, acFile, 00756 sizeof acFile/ sizeof acFile[0]); 00757 00758 // Create the property sheet- it will get deleted if it is not in 00759 // use when the shell tries to unload the extension 00760 00761 CProfileInformationPage *pcpip = 00762 new CProfileInformationPage(CGlobals::Instance(), acFile); 00763 00764 if (!(*lpfnAddPage)(pcpip -> Handle(), lParam)) 00765 DestroyPropertySheetPage(pcpip -> Handle()); 00766 00767 return NOERROR; 00768 } 00769 00770 // This member function handles the associate device tab 00771 00772 HRESULT CICMUserInterface::AddAssociateTab(LPFNADDPROPSHEETPAGE lpfnAddPage, 00773 LPARAM lParam) { 00774 00775 _RPTF0(_CRT_WARN, "CICMUserInterface::AddAssociateTab\n"); 00776 00777 // Load the profile(s) in the list. 00778 00779 FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL, 00780 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00781 STGMEDIUM stgm; 00782 HRESULT hres = m_lpdoTarget ? 00783 m_lpdoTarget -> GetData(&fmte, &stgm) : 0; 00784 00785 if (!SUCCEEDED(hres)) 00786 return NOERROR; // Why bother reporting a failure, here? 00787 00788 UINT ucFiles = stgm.hGlobal ? 00789 DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0; 00790 00791 if (ucFiles != 1) 00792 return NOERROR; 00793 00794 TCHAR acFile[_MAX_PATH]; 00795 00796 DragQueryFile((HDROP) stgm.hGlobal, 0, acFile, 00797 sizeof acFile/ sizeof acFile[0]); 00798 00799 // Create the property sheet- it will get deleted if it is not in 00800 // use when the shell tries to unload the extension 00801 00802 CProfileAssociationPage *pcpap = 00803 new CProfileAssociationPage(CGlobals::Instance(), acFile); 00804 00805 if (!(*lpfnAddPage)(pcpap -> Handle(), lParam)) 00806 DestroyPropertySheetPage(pcpap -> Handle()); 00807 00808 return NOERROR; 00809 } 00810 00811 // This member function handles the monitor color management tab 00812 // In this case, no data object is given. 00813 00814 // Private monitor enumeration function 00815 00816 HRESULT CICMUserInterface::AddMonitorTab(LPFNADDPROPSHEETPAGE lpfnAddPage, 00817 LPARAM lParam) { 00818 00819 // Create the property sheet- it will get deleted if it is not in 00820 // use when the shell tries to unload the extension 00821 00822 CString csMonitorDevice; 00823 CString csMonitorFriendlyName; 00824 00825 if (m_lpdoTarget) { 00826 00827 FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Display Device")), 00828 (DVTARGETDEVICE FAR *) NULL, 00829 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00830 STGMEDIUM stgm; 00831 00832 // Get device name from IDataObject. 00833 00834 HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm); 00835 00836 if (!SUCCEEDED(hres) || !stgm.hGlobal) { 00837 return NOERROR; // Why bother reporting a failure, here? 00838 } 00839 00840 // The storage contains Display device path (\\.\DisplayX) in UNICODE. 00841 00842 LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(stgm.hGlobal); 00843 CString csMonitorDevicePath = lpDeviceName; 00844 GlobalUnlock(stgm.hGlobal); 00845 00846 // Query the device id, friendly name and other on the display device. 00847 00848 DISPLAY_DEVICE ddPriv; 00849 00850 ddPriv.cb = sizeof(ddPriv); 00851 00852 if (!EnumDisplayDevices((LPCTSTR)csMonitorDevicePath, 0, &ddPriv, 0)) 00853 { 00854 return NOERROR; // Why bother reporting a failure, here? 00855 } 00856 00857 #if HIDEYUKN_DBG 00858 MessageBox(NULL,csMonitorDevicePath,TEXT(""),MB_OK); 00859 MessageBox(NULL,(LPCTSTR)ddPriv.DeviceID,TEXT(""),MB_OK); 00860 MessageBox(NULL,(LPCTSTR)ddPriv.DeviceString,TEXT(""),MB_OK); 00861 #endif 00862 00863 // Use deviceId (PnP Id) as device name, and set friendly name 00864 00865 csMonitorDevice = (LPTSTR)(ddPriv.DeviceID); 00866 csMonitorFriendlyName = (LPTSTR)(ddPriv.DeviceString); 00867 } 00868 else 00869 { 00870 // if we don't have IDataObject, enumerate monitor, 00871 // then use 1st entry. 00872 00873 CMonitorList cml; 00874 cml.Enumerate(); 00875 _ASSERTE(cml.Count()); // At least, we should have one Monitor. 00876 csMonitorDevice = csMonitorFriendlyName = cml.DeviceName(0); 00877 } 00878 00879 CMonitorProfileManagement *pcmpm = 00880 new CMonitorProfileManagement(csMonitorDevice, 00881 csMonitorFriendlyName, 00882 CGlobals::Instance()); 00883 00884 00885 if (!(*lpfnAddPage)(pcmpm -> Handle(), lParam)) 00886 DestroyPropertySheetPage(pcmpm -> Handle()); 00887 00888 return NOERROR; 00889 } 00890 00891 // Private scanner enumeration function 00892 00893 HRESULT CICMUserInterface::AddScannerTab(LPFNADDPROPSHEETPAGE lpfnAddPage, 00894 LPARAM lParam) { 00895 00896 // Create the property sheet- it will get deleted if it is not in 00897 // use when the shell tries to unload the extension 00898 00899 CString csScannerDevice; 00900 00901 if (m_lpdoTarget) { 00902 FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("STIDeviceName")), 00903 (DVTARGETDEVICE FAR *) NULL, 00904 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 00905 STGMEDIUM stgm; 00906 00907 // Get device name from IDataObject. 00908 00909 HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm); 00910 00911 if (!SUCCEEDED(hres) || !stgm.hGlobal) { 00912 return NOERROR; // Why bother reporting a failure, here? 00913 } 00914 00915 // The storage contains Scanner in UNICODE string. 00916 00917 LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(stgm.hGlobal); 00918 csScannerDevice = lpDeviceName; 00919 GlobalUnlock(stgm.hGlobal); 00920 00921 #if HIDEYUKN_DBG 00922 MessageBox(NULL,csScannerDevice,TEXT(""),MB_OK); 00923 #endif 00924 00925 } else { 00926 00927 // if we don't have IDataObject, enumerate monitor, 00928 // then use 1st entry. 00929 00930 CScannerList csl; 00931 csl.Enumerate(); 00932 _ASSERTE(csl.Count()); 00933 csScannerDevice = csl.DeviceName(0); 00934 } 00935 00936 CScannerProfileManagement *pcspm = 00937 new CScannerProfileManagement(csScannerDevice, CGlobals::Instance()); 00938 00939 if (!(*lpfnAddPage)(pcspm -> Handle(), lParam)) 00940 DestroyPropertySheetPage(pcspm -> Handle()); 00941 00942 return NOERROR; 00943 } 00944 00945 // The following is a helper function- it takes a Shell ID List array, 00946 // representing a printer in a printers folder, and a CString. It 00947 // initializes the CString with the correct name of the printer. 00948 00949 static void RetrievePrinterName(LPIDA lpida, CString& csTarget) { 00950 00951 // Extract the container (Printers Folder) and target (Printer) 00952 // IDs from the array. 00953 00954 LPCITEMIDLIST pciilContainer = 00955 (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[0]); 00956 00957 LPCITEMIDLIST pciilTarget = 00958 (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[1]); 00959 00960 if (!pciilContainer || !pciilTarget) 00961 return; 00962 00963 // Get a pointer to the printers folder. 00964 00965 LPSHELLFOLDER psfDesktop, psfPrinterFolder; 00966 00967 if (FAILED(SHGetDesktopFolder(&psfDesktop))) 00968 return; 00969 00970 if (FAILED(psfDesktop -> BindToObject(pciilContainer, NULL, 00971 IID_IShellFolder, (void **) &psfPrinterFolder))) { 00972 psfDesktop -> Release(); 00973 return; 00974 } 00975 00976 // Retrieve the printer's display name 00977 00978 STRRET strret; 00979 00980 if (FAILED(psfPrinterFolder -> 00981 GetDisplayNameOf(pciilTarget, SHGDN_FORPARSING, &strret))) { 00982 psfPrinterFolder -> Release(); 00983 psfDesktop -> Release(); 00984 return; 00985 } 00986 00987 // Copy the display name- the CString class now handles any encoding 00988 // issues 00989 00990 switch (strret.uType) { 00991 00992 case STRRET_WSTR: 00993 00994 // This is a Unicode string which was IMalloc'd 00995 00996 csTarget = strret.pOleStr; 00997 00998 IMalloc *pim; 00999 01000 if (SUCCEEDED(CoGetMalloc(1, &pim))) { 01001 pim -> Free(strret.pOleStr); 01002 pim -> Release(); 01003 } 01004 01005 break; 01006 01007 case STRRET_CSTR: 01008 01009 // This is an ANSI string in the buffer 01010 01011 csTarget = strret.cStr; 01012 break; 01013 01014 case STRRET_OFFSET: 01015 01016 // This is an ANSI string at the given offset into the SHITEMID 01017 // which pciilTarget points to. 01018 01019 csTarget = (LPCSTR) pciilTarget + strret.uOffset; 01020 01021 } 01022 psfPrinterFolder -> Release(); 01023 psfDesktop -> Release(); 01024 } 01025 01026 // Private member function for handling the printer profile manamgment tab. 01027 01028 HRESULT CICMUserInterface::AddPrinterTab(LPFNADDPROPSHEETPAGE lpfnAddPage, 01029 LPARAM lParam) { 01030 01031 // The list is formatted as a Shell IDList Array 01032 01033 FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Shell IDList Array")), 01034 (DVTARGETDEVICE FAR *) NULL, 01035 DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 01036 STGMEDIUM stgm; 01037 HRESULT hres = m_lpdoTarget ? 01038 m_lpdoTarget -> GetData(&fmte, &stgm) : 0; 01039 01040 if (!SUCCEEDED(hres) || !stgm.hGlobal) 01041 return NOERROR; // Why bother reporting a failure, here? 01042 01043 CString csPrinter; 01044 01045 RetrievePrinterName((LPIDA) stgm.hGlobal, csPrinter); 01046 01047 #if HIDEYUKN_DBG 01048 MessageBox(NULL,csPrinter,TEXT(""),MB_OK); 01049 #endif 01050 01051 // If this is not a color printer, forget it... 01052 01053 01054 if (!CGlobals::ThisIsAColorPrinter(csPrinter)) 01055 return NOERROR; 01056 01057 // Create the property sheet- it will get deleted if it is not in use when 01058 // the shell tries to unload the extension 01059 01060 01061 CPrinterProfileManagement *pcppm = 01062 new CPrinterProfileManagement(csPrinter, CGlobals::Instance()); 01063 01064 if (!pcppm) 01065 return E_OUTOFMEMORY; 01066 01067 if (!(*lpfnAddPage)(pcppm -> Handle(), lParam)) 01068 DestroyPropertySheetPage(pcppm -> Handle()); 01069 01070 return NOERROR; 01071 } 01072 01073 PSTR 01074 GetFilenameFromPath( 01075 PSTR pPathName 01076 ) 01077 { 01078 DWORD dwLen; // length of pathname 01079 01080 dwLen = lstrlenA(pPathName); 01081 01082 // 01083 // Go to the end of the pathname, and start going backwards till 01084 // you reach the beginning or a backslash 01085 // 01086 01087 pPathName += dwLen; 01088 01089 while (dwLen-- && --pPathName) 01090 { 01091 if (*pPathName == '\\') 01092 { 01093 pPathName++; 01094 break; 01095 } 01096 } 01097 01098 // 01099 // if *pPathName is zero, then we had a string that ends in a backslash 01100 // 01101 01102 return *pPathName ? pPathName : NULL; 01103 }

Generated on Sat May 15 19:41:49 2004 for test by doxygen 1.3.7