Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1151 lines
38 KiB

  1. /*****************************************************************************
  2. Source File: Shell Extension Classes.CPP
  3. This file implements the Shell Extension classes.
  4. Copyright (c) 1996, 1997 by Microsoft Corporation. All Rights Reserved.
  5. A Pretty Penny Enterprises Production
  6. Change History:
  7. 10-28-96 A-RobKj (Pretty Penny Enterprises) began coding
  8. 12-04-96 A-RobKj Added the printer tab support
  9. 12-13-96 A-RobKj Modified for faster Icon extraction
  10. 01-07-97 KjelgaardR@ACM.Org Removed IContextMenu functions in favor of
  11. an exported ManageColorProfile procedure. This supplies a
  12. language-independent "Open" item that works with either mouse
  13. button.
  14. 01-08-97 KjelgaardR@acm.org Added utility routine to determine if a printer
  15. is a color model. Modified printer UI to only add pages for color
  16. printers.
  17. ******************************************************************************/
  18. #include "ICMUI.H"
  19. #include <shlobj.h>
  20. #include <string.h>
  21. #include <initguid.h>
  22. #include <shfusion.h>
  23. #include "ShellExt.H"
  24. #include "Resource.H"
  25. // Declare some storage space for the global statics
  26. int CGlobals::m_icDLLReferences = 0;
  27. HMODULE CGlobals::m_hmThisDll = NULL;
  28. CStringArray CGlobals::m_csaProfiles;
  29. BOOL CGlobals::m_bIsValid = FALSE;
  30. // Some global useful procs- an error reporter
  31. void CGlobals::Report(int idError, HWND m_hwndParent) {
  32. CString csMessage, csTitle;
  33. csMessage.Load(idError);
  34. csTitle.Load(MessageBoxTitle);
  35. MessageBox(m_hwndParent, csMessage, csTitle, MB_OK|MB_ICONEXCLAMATION);
  36. }
  37. int CGlobals::ReportEx(int idError, HWND m_hwndParent,
  38. BOOL bSystemMessage, UINT uType, DWORD dwNumMsg, ...) {
  39. CString csMessage, csTitle;
  40. va_list argList;
  41. va_start(argList,dwNumMsg);
  42. csMessage.LoadAndFormat(idError,NULL,bSystemMessage,dwNumMsg,&argList);
  43. csTitle.Load(MessageBoxTitle);
  44. va_end(argList);
  45. return (MessageBox(m_hwndParent, csMessage, csTitle, uType));
  46. }
  47. // A profile status checker
  48. BOOL CGlobals::IsInstalled(CString& csProfile) {
  49. // if (!m_bIsValid) {
  50. ENUMTYPE et = {sizeof et, ENUM_TYPE_VERSION, 0, NULL};
  51. CProfile::Enumerate(et, m_csaProfiles);
  52. m_bIsValid = TRUE;
  53. // }
  54. for (unsigned u = 0; u < m_csaProfiles.Count(); u++)
  55. if (!lstrcmpi(csProfile.NameOnly(), m_csaProfiles[u].NameOnly()))
  56. break;
  57. return u < m_csaProfiles.Count();
  58. }
  59. // Utility routine to report if a printer is color or monochrome
  60. BOOL CGlobals::ThisIsAColorPrinter(LPCTSTR lpstrName) {
  61. HDC hdcThis = CGlobals::GetPrinterHDC(lpstrName);
  62. BOOL bReturn = FALSE;
  63. if (hdcThis) {
  64. bReturn = 2 < (unsigned) GetDeviceCaps(hdcThis, NUMCOLORS);
  65. DeleteDC(hdcThis);
  66. }
  67. return bReturn;
  68. }
  69. // Utility to determine the hdc for a printer
  70. // The caller is responsible for calling
  71. // DeleteDC() on the result
  72. HDC CGlobals::GetPrinterHDC(LPCTSTR lpstrName) {
  73. HANDLE hPrinter; // Get a handle on it...
  74. LPTSTR lpstrMe = const_cast <LPTSTR> (lpstrName);
  75. if (!OpenPrinter(lpstrMe, &hPrinter, NULL)) {
  76. _RPTF2(_CRT_WARN, "Unable to open printer '%s'- error %d\n", lpstrName,
  77. GetLastError());
  78. return FALSE;
  79. }
  80. // First, use DocumentProperties to find the correct DEVMODE size- we
  81. // must use the DEVMODE to force color on, in case the user's defaults
  82. // have turned it off...
  83. unsigned short lcbNeeded = (unsigned short) DocumentProperties(NULL, hPrinter, lpstrMe, NULL,
  84. NULL, 0);
  85. if (lcbNeeded <= 0) {
  86. _RPTF2(_CRT_WARN,
  87. "Document Properties (get size) for '%s' returned %d\n", lpstrName,
  88. lcbNeeded);
  89. ClosePrinter(hPrinter);
  90. return FALSE;
  91. }
  92. HDC hdcThis = NULL;
  93. union {
  94. LPBYTE lpb;
  95. LPDEVMODE lpdm;
  96. };
  97. lpb = new BYTE[lcbNeeded];
  98. if (lpb) {
  99. ZeroMemory(lpb,lcbNeeded);
  100. lpdm -> dmSize = lcbNeeded;
  101. lpdm -> dmFields = DM_COLOR;
  102. lpdm -> dmColor = DMCOLOR_COLOR;
  103. if (IDOK == DocumentProperties(NULL, hPrinter, lpstrMe, lpdm, lpdm,
  104. DM_IN_BUFFER | DM_OUT_BUFFER)) {
  105. // Turn off ICM, since not nessesary here.
  106. //
  107. lpdm -> dmICMMethod = DMICMMETHOD_NONE;
  108. // Finally, we can create the DC!
  109. // Note: we're not actually creating a DC, just an IC
  110. hdcThis = CreateIC(NULL, lpstrName, NULL, lpdm);
  111. } else {
  112. _RPTF2(_CRT_WARN,
  113. "DocumentProperties (retrieve) on '%s' failed- error %d\n",
  114. lpstrName, GetLastError());
  115. }
  116. delete lpb;
  117. }
  118. else
  119. _RPTF2(_CRT_WARN, "ThisIsAColorPrinter(%s) failed to get %d bytes\n",
  120. lpstrName, lcbNeeded);
  121. ClosePrinter(hPrinter);
  122. return hdcThis;
  123. }
  124. // Required Shell Extension DLL interfaces
  125. STDAPI DllCanUnloadNow() {
  126. return CGlobals::CanUnload();
  127. }
  128. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvOut) {
  129. return CIcmUiFactory::KeyToTheFactory(rclsid, riid, ppvOut);
  130. }
  131. extern "C" int APIENTRY DllMain(HMODULE hmThis, DWORD dwReason,
  132. LPVOID lpvReserved) {
  133. #if defined(DEBUG) || defined(_DEBUG)
  134. static HANDLE hfWarnings; // Log file
  135. #endif
  136. switch (dwReason) {
  137. case DLL_PROCESS_ATTACH:
  138. SHFusionInitializeFromModuleID(hmThis, SHFUSION_CPL_RESOURCE_ID);
  139. // Save the handle
  140. CGlobals::SetHandle(hmThis);
  141. #if defined(DEBUG) || defined(_DEBUG)
  142. _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
  143. _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  144. hfWarnings = CreateFileA("C:\\ICMUIWarn.Txt", GENERIC_WRITE, 0,
  145. NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  146. if (hfWarnings!= INVALID_HANDLE_VALUE) {
  147. SetFilePointer(hfWarnings, 0, NULL, FILE_END);
  148. _CrtSetReportFile(_CRT_WARN, hfWarnings);
  149. }
  150. _RPTF1(_CRT_WARN, "ICMUI DLL being loaded- handle %X\n", hmThis);
  151. #endif
  152. break;
  153. case DLL_PROCESS_DETACH:
  154. #if defined(DEBUG) || defined(_DEBUG)
  155. _RPTF0(_CRT_WARN, "ICMUI DLL being unloaded\n");
  156. if (hfWarnings != INVALID_HANDLE_VALUE)
  157. CloseHandle(hfWarnings);
  158. #endif
  159. SHFusionUninitialize();
  160. }
  161. return 1;
  162. }
  163. // CIcmUiFactory member functions- these are used to provide external access
  164. // to the class factory. The shell uses these to initialize extensions for
  165. // both context menus and property sheets, both of which we provide,
  166. // fortunately in the same object...
  167. CIcmUiFactory::CIcmUiFactory(REFCLSID rclsid) {
  168. m_ulcReferences = 0;
  169. CGlobals::Attach();
  170. if (IsEqualIID(rclsid, CLSID_ICM))
  171. m_utThis = IsProfile;
  172. else if (IsEqualIID(rclsid, CLSID_PRINTERUI))
  173. m_utThis = IsPrinter;
  174. else if (IsEqualIID(rclsid, CLSID_SCANNERUI))
  175. m_utThis = IsScanner;
  176. else
  177. m_utThis = IsMonitor;
  178. }
  179. STDMETHODIMP CIcmUiFactory::QueryInterface(REFIID riid, void **ppvObject) {
  180. if (IsEqualIID(riid, IID_IUnknown) ||
  181. IsEqualIID(riid, IID_IClassFactory)) {
  182. *ppvObject = this;
  183. AddRef();
  184. return NOERROR;
  185. }
  186. // Asked for an interface we ain't got!
  187. *ppvObject = NULL;
  188. return E_NOINTERFACE;
  189. }
  190. // IClassFactory interface functions
  191. STDMETHODIMP CIcmUiFactory::CreateInstance(LPUNKNOWN punk, REFIID riid,
  192. void **ppvInstance) {
  193. *ppvInstance = NULL;
  194. if (punk) // We don't allow aggregation
  195. return CLASS_E_NOAGGREGATION;
  196. // We simply create a new ICM UI object, and return an interface to it.
  197. // This will get queried by the shell for IExtShellInit, and the init job
  198. // will be done.
  199. CICMUserInterface *pcicmui = new CICMUserInterface(m_utThis);
  200. if (!pcicmui)
  201. return E_OUTOFMEMORY;
  202. // Let's be paranoid- if the QueryInterface failes, kill the ICMUI object,
  203. // so we can still be unloaded!
  204. HRESULT hrReturn = pcicmui -> QueryInterface(riid, ppvInstance);
  205. if (!*ppvInstance)
  206. delete pcicmui;
  207. return hrReturn;
  208. }
  209. // Key to the factory is a static function that allows outsiders to instance
  210. // the class factory. So, the caller will first instance the factory, then
  211. // instance implementations of the interfaces it needs using the factory
  212. // instance it receives from here.
  213. HRESULT CIcmUiFactory::KeyToTheFactory(REFCLSID rclsid, REFIID riid,
  214. void **ppvObject) {
  215. *ppvObject = NULL;
  216. if (!IsEqualIID(rclsid, CLSID_ICM) &&
  217. !IsEqualIID(rclsid, CLSID_MONITORUI) &&
  218. !IsEqualIID(rclsid, CLSID_SCANNERUI) &&
  219. !IsEqualIID(rclsid, CLSID_PRINTERUI))
  220. return CLASS_E_CLASSNOTAVAILABLE;
  221. CIcmUiFactory *pciuf = new CIcmUiFactory(rclsid);
  222. if (!pciuf)
  223. return E_OUTOFMEMORY;
  224. HRESULT hrReturn = pciuf -> QueryInterface(riid, ppvObject);
  225. if (!*ppvObject)
  226. delete pciuf;
  227. return hrReturn;
  228. }
  229. /******************************************************************************
  230. ICM UI class methods- these do the true interface work of the DLL.
  231. ******************************************************************************/
  232. CICMUserInterface::CICMUserInterface(UITYPE utThis) {
  233. m_lpdoTarget = NULL;
  234. m_ulcReferences = 0;
  235. m_utThis = utThis;
  236. CGlobals::Attach();
  237. _RPTF2(_CRT_WARN, "CICMUserInterface(%d) constructed @ %lX\n", utThis, this);
  238. }
  239. // QueryInterface gets a bit long, but not too badly. The casts are needed
  240. // because we use multiple inheritance- casting the this pointer to a base
  241. // class actually returns a this pointer for that base class' part of the
  242. // instance. Unlike single inheritance, the this pointer for the
  243. // CICMUserInterface class does not directly reference ANY of the base
  244. // classes.
  245. STDMETHODIMP CICMUserInterface::QueryInterface(REFIID riid,
  246. void **ppvObject) {
  247. *ppvObject = NULL; // Assume the worst
  248. // Since the device UI support a different set of functions, let's be
  249. // particular about which interfaces we claim to support when...
  250. if (m_utThis > IsProfile) {
  251. if (IsEqualIID(riid, IID_IUnknown) ||
  252. IsEqualIID(riid, IID_IShellExtInit))
  253. *ppvObject = (IShellExtInit *) this;
  254. if (IsEqualIID(riid, IID_IShellPropSheetExt))
  255. *ppvObject = (IShellPropSheetExt *) this;
  256. }
  257. else {
  258. if (IsEqualIID(riid, IID_IUnknown) ||
  259. IsEqualIID(riid, IID_IContextMenu))
  260. *ppvObject = (IContextMenu *) this;
  261. if (IsEqualIID(riid, IID_IShellExtInit))
  262. *ppvObject = (IShellExtInit *) this;
  263. if (IsEqualIID(riid, IID_IExtractIcon))
  264. *ppvObject = (IExtractIcon *) this;
  265. if (IsEqualIID(riid, IID_IPersistFile) ||
  266. IsEqualIID(riid, IID_IPersist))
  267. *ppvObject = (IPersistFile *) this;
  268. if (IsEqualIID(riid, IID_IShellPropSheetExt))
  269. *ppvObject = (IShellPropSheetExt *) this;
  270. }
  271. if (*ppvObject)
  272. ((IUnknown *) *ppvObject) -> AddRef();
  273. _RPTF2(_CRT_WARN, "CICMUserInterace::QueryInterface(%lX) returns %lX\n",
  274. this, ppvObject);
  275. return *ppvObject ? NOERROR : E_NOINTERFACE;
  276. }
  277. // IShellExtInit member function- this interface needs only one
  278. STDMETHODIMP CICMUserInterface::Initialize(LPCITEMIDLIST pcidlFolder,
  279. LPDATAOBJECT pdoTarget,
  280. HKEY hKeyID) {
  281. _RPTF0(_CRT_WARN, "CICMUserInterface::Initialize\n");
  282. // The target data object is an HDROP, or list of files from the shell.
  283. if (m_lpdoTarget) {
  284. m_lpdoTarget -> Release();
  285. m_lpdoTarget = NULL;
  286. }
  287. if (pdoTarget) {
  288. m_lpdoTarget = pdoTarget;
  289. m_lpdoTarget -> AddRef();
  290. }
  291. return NOERROR;
  292. }
  293. // IExtractIcon interface functions- for now, we'll default to providing a
  294. // default icon from our DLL. We provide one icon for installed profiles,
  295. // and a second for uninstalled ones.
  296. STDMETHODIMP CICMUserInterface::GetIconLocation(UINT uFlags,
  297. LPTSTR lpstrTarget,
  298. UINT uccTarget,
  299. int *piIndex,
  300. UINT *puFlags) {
  301. *puFlags = (GIL_NOTFILENAME|GIL_DONTCACHE); // Make shell call our Extract function
  302. // And don't cache in the callee.
  303. return S_FALSE;
  304. }
  305. STDMETHODIMP CICMUserInterface::Extract(LPCTSTR lpstrFile, UINT nIconIndex,
  306. HICON *phiconLarge, HICON *phiconSmall,
  307. UINT nIconSize) {
  308. *phiconSmall = *phiconLarge = LoadIcon(CGlobals::Instance(),
  309. MAKEINTRESOURCE(CGlobals::IsInstalled(m_csFile) ? DefaultIcon : UninstalledIcon));
  310. return NOERROR;
  311. }
  312. // IPersistFile functions- there's only one worth implementing
  313. STDMETHODIMP CICMUserInterface::Load(LPCOLESTR lpwstrFileName,
  314. DWORD dwMode) {
  315. // This interface is used to initialize the icon handler- it will
  316. // receive the profile name, which we will save for later use.
  317. // The CString assigment operator handles any encoding converions needed
  318. // encoding conversions for us.
  319. m_csFile = lpwstrFileName;
  320. return m_csFile.IsEmpty() ? E_OUTOFMEMORY : NO_ERROR;
  321. }
  322. // IContextMenu functions-
  323. STDMETHODIMP CICMUserInterface::QueryContextMenu(HMENU hMenu, UINT indexMenu,
  324. UINT idCmdFirst, UINT idCmdLast,
  325. UINT uFlags) {
  326. // Only CMF_NORMAL and CMF_EXPLORE case will be handled.
  327. //
  328. // CMF_CANRENAME - This flag is set if the calling application supports
  329. // renaming of items. A context menu extension or drag-and-drop
  330. // handler should ignore this flag. A namespace extension should
  331. // add a rename item to the menu if applicable.
  332. // CMF_DEFAULTONLY - This flag is set when the user is activating the default action,
  333. // typically by double-clicking. This flag provides a hint for the
  334. // context menu extension to add nothing if it does not modify the
  335. // default item in the menu. A context menu extension or drag-and-drop
  336. // handler should not add any menu items if this value is specified.
  337. // A namespace extension should add only the default item (if any).
  338. // CMF_EXPLORE - This flag is set when Windows Explorer's tree window is present.
  339. // Context menu handlers should ignore this value.
  340. // CMF_INCLUDESTATIC - This flag is set when a static menu is being constructed.
  341. // Only the browser should use this flag. All other context menu
  342. // extensions should ignore this flag.
  343. // CMF_NODEFAULT - This flag is set if no item in the menu should be the default item.
  344. // A context menu extension or drag-and-drop handler should ignore this
  345. // flag. A namespace extension should not set any of the menu items to
  346. // the default.
  347. // CMF_NORMAL - Indicates normal operation. A context menu extension, namespace extension,
  348. // or drag-and-drop handler can add all menu items.
  349. // CMF_NOVERBS - This flag is set for items displayed in the "Send To:" menu.
  350. // Context menu handlers should ignore this value.
  351. // CMF_VERBSONLY - This flag is set if the context menu is for a shortcut object.
  352. // Context menu handlers should ignore this value.
  353. if (((uFlags & 0x000F) == CMF_NORMAL) || (uFlags & CMF_EXPLORE))
  354. {
  355. //
  356. // Load the profile(s) in the list.
  357. //
  358. {
  359. FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL,
  360. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  361. STGMEDIUM stgm;
  362. HRESULT hres = m_lpdoTarget ?
  363. m_lpdoTarget -> GetData(&fmte, &stgm) : E_FAIL;
  364. if (!SUCCEEDED(hres))
  365. return NOERROR; // Why bother reporting a failure, here?
  366. UINT ucFiles = stgm.hGlobal ?
  367. DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
  368. if (!ucFiles) {
  369. ReleaseStgMedium(&stgm);
  370. return NOERROR; // Shouldn't happen, but it's not important
  371. }
  372. else if (ucFiles == 1)
  373. m_bMultiSelection = FALSE;
  374. else
  375. m_bMultiSelection = TRUE;
  376. // Assume in installed context, but we will scan the selected item
  377. // is really installed everything.
  378. m_bInstalledContext = TRUE;
  379. TCHAR acFile[_MAX_PATH];
  380. for (UINT u = 0; u < ucFiles; u++) {
  381. DragQueryFile((HDROP) stgm.hGlobal, u, acFile,
  382. sizeof acFile/ sizeof acFile[0]);
  383. CString csFile = acFile;
  384. m_bInstalledContext = (m_bInstalledContext && CGlobals::IsInstalled(csFile));
  385. }
  386. ReleaseStgMedium(&stgm);
  387. }
  388. UINT idCmd = idCmdFirst;
  389. CString csInstallMenu, csAssociateMenu;
  390. // If every profile(s) are already installed on this system,
  391. // display "Uninstall Profile", otherwise display "Install Profile"
  392. csInstallMenu.Load(m_bInstalledContext ? UninstallProfileMenuString : InstallProfileMenuString);
  393. ::InsertMenu(hMenu,indexMenu,MF_STRING|MF_BYPOSITION,idCmd,csInstallMenu);
  394. // Set "Install Profile" or "Uninstall Profile" as default.
  395. SetMenuDefaultItem(hMenu,indexMenu,TRUE);
  396. // Increment Menu pos. and item id.
  397. indexMenu++; idCmd++;
  398. // Add "Associate..." menu item
  399. csAssociateMenu.Load(AssociateMenuString);
  400. ::InsertMenu(hMenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd++,csAssociateMenu);
  401. // But if we have multi selection, disable "Associate..."
  402. if (m_bMultiSelection)
  403. ::EnableMenuItem(hMenu,(idCmd-1),MF_GRAYED);
  404. return (idCmd - idCmdFirst); // return number of menu inserted.
  405. }
  406. return NOERROR;
  407. }
  408. STDMETHODIMP CICMUserInterface::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) {
  409. // If HIWORD(lpcmi->lpVerb) then we have been called programmatically and
  410. // lpVerb us a command that should be invoked. Otherwise, the shell has
  411. // called us, abd LOWORD(lpcmi->lpVerb) is the menu ID the user has selected.
  412. // Actually, it's (menu ID - icmdFirst) from QueryContextMenu().
  413. if (!HIWORD((ULONG)(ULONG_PTR)lpcmi->lpVerb)) {
  414. FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL,
  415. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  416. STGMEDIUM stgm;
  417. HRESULT hres = m_lpdoTarget ?
  418. m_lpdoTarget -> GetData(&fmte, &stgm) : E_FAIL;
  419. if (!SUCCEEDED(hres))
  420. return NOERROR; // Why bother reporting a failure, here?
  421. UINT ucFiles = stgm.hGlobal ?
  422. DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
  423. if (!ucFiles) {
  424. ReleaseStgMedium(&stgm);
  425. return NOERROR; // Shouldn't happen, but it's not important
  426. }
  427. UINT idCmd = LOWORD(lpcmi->lpVerb);
  428. // Walk through every selected item to install/uninstall.
  429. for (UINT u = 0; u < ucFiles; u++) {
  430. TCHAR acFile[_MAX_PATH];
  431. DragQueryFile((HDROP) stgm.hGlobal, u, acFile,
  432. sizeof acFile/ sizeof acFile[0]);
  433. switch (idCmd) {
  434. case 0: { // Install/Uninstall was selected.
  435. // during the installation or un-installation,
  436. // change the cursor icon to IDC_APPSTARTING.
  437. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL,IDC_APPSTARTING));
  438. CProfile csProfile(acFile);
  439. if (m_bInstalledContext) {
  440. // All selected profile is already installed, then
  441. // Uninstall every profile(s) are selected if installed.
  442. if (csProfile.IsInstalled()) {
  443. csProfile.Uninstall(FALSE); // never delete file from disk.
  444. }
  445. }
  446. else {
  447. // Some of selected profile is not installed, then
  448. // Install every profile(s) are selected if not installed, yet.
  449. if (!csProfile.IsInstalled()) {
  450. csProfile.Install();
  451. }
  452. }
  453. SetCursor(hCursorOld);
  454. break;
  455. }
  456. case 1: { // "Associate..." was selected.
  457. CString csProfileName;
  458. // Get profile "friendly" name.
  459. {
  460. CProfile csProfile(acFile);
  461. csProfileName = csProfile.GetName();
  462. } // de-constructer for csProfile should be here.
  463. // Create PropertySheet with "Profile Information" and
  464. // "Associate Device" pages
  465. PROPSHEETHEADER psh;
  466. HPROPSHEETPAGE hpsp[2];
  467. CProfileInformationPage *pcpip =
  468. new CProfileInformationPage(CGlobals::Instance(), acFile);
  469. CProfileAssociationPage *pcpap =
  470. new CProfileAssociationPage(CGlobals::Instance(), acFile);
  471. if( (pcpip!=NULL)&&(pcpap!=NULL) ) {
  472. hpsp[0] = pcpip->Handle();
  473. hpsp[1] = pcpap->Handle();
  474. ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
  475. // fill the property sheet structure.
  476. psh.dwSize = sizeof(PROPSHEETHEADER);
  477. psh.hInstance = CGlobals::Instance();
  478. psh.hwndParent = NULL;
  479. psh.nStartPage = 1; // Active "Associate Device" page.
  480. psh.nPages = 2;
  481. psh.phpage = hpsp;
  482. psh.pszCaption = csProfileName;
  483. PropertySheet(&psh);
  484. delete pcpip; delete pcpap;
  485. break;
  486. } else {
  487. if(pcpip) delete pcpip;
  488. if(pcpap) delete pcpap;
  489. return E_OUTOFMEMORY;
  490. }
  491. }
  492. } // switch (idCmd)
  493. } // for (UINT u = 0; u < ucFiles; u++)
  494. ReleaseStgMedium(&stgm);
  495. } // if (!HIWORD(lpcmi->lpVerb))
  496. return NOERROR;
  497. }
  498. /* Supprisingly the code casts the unicode string to an
  499. * asciiz string and passes it. One assumes that nobody
  500. * actually interprets the string pointer as ascii on the
  501. * way to its destination where it is reinterpreted
  502. * as a pointer to a unicode string.
  503. */
  504. STDMETHODIMP CICMUserInterface::GetCommandString(UINT_PTR idCmd, UINT uFlags,
  505. UINT FAR *reserved, LPSTR pszName,
  506. UINT cchMax) {
  507. CString csReturnString;
  508. switch (idCmd) {
  509. case 0: { // Install/Uninstall was selected.
  510. if(m_bMultiSelection) {
  511. csReturnString.Load(m_bInstalledContext ? UninstallMultiProfileContextMenuString : InstallMultiProfileContextMenuString);
  512. } else {
  513. csReturnString.Load(m_bInstalledContext ? UninstallProfileContextMenuString : InstallProfileContextMenuString);
  514. }
  515. lstrcpyn((LPTSTR)pszName, csReturnString, cchMax);
  516. break;
  517. }
  518. case 1: { // Associate... was seleted.
  519. if (!m_bMultiSelection) {
  520. csReturnString.Load(AssociateContextMenuString);
  521. lstrcpyn((LPTSTR)pszName, csReturnString, cchMax);
  522. }
  523. break;
  524. }
  525. }
  526. return NOERROR;
  527. }
  528. // IPropSheetExt functions- again, we only need to implement one of these
  529. // Since we now support two different interfaces, the actual implementation
  530. // is done in a private method germane to the desired interface.
  531. STDMETHODIMP CICMUserInterface::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage,
  532. LPARAM lParam) {
  533. _RPTF0(_CRT_WARN, "CICMUserInterface::AddPages\n");
  534. HRESULT hResult = NOERROR;
  535. switch (m_utThis) {
  536. case IsProfile: {
  537. hResult = AddProfileTab(lpfnAddPage, lParam);
  538. if (hResult == NOERROR) {
  539. hResult = AddAssociateTab(lpfnAddPage, lParam);
  540. }
  541. break;
  542. }
  543. case IsMonitor: {
  544. hResult = AddMonitorTab(lpfnAddPage, lParam);
  545. break;
  546. }
  547. case IsPrinter: {
  548. hResult = AddPrinterTab(lpfnAddPage, lParam);
  549. break;
  550. }
  551. case IsScanner: {
  552. hResult = AddScannerTab(lpfnAddPage, lParam);
  553. break;
  554. }
  555. }
  556. return hResult;
  557. }
  558. // This member function handles the ICC profile information sheet.
  559. // In this case, the data object given via IShellExtInit::Initialize is
  560. // an HDROP (list of fully qualified file names).
  561. HRESULT CICMUserInterface::AddProfileTab(LPFNADDPROPSHEETPAGE lpfnAddPage,
  562. LPARAM lParam) {
  563. _RPTF0(_CRT_WARN, "CICMUserInterface::AddProfileTab\n");
  564. TCHAR acFile[_MAX_PATH];
  565. // Load the profile(s) in the list.
  566. if(m_lpdoTarget) {
  567. FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL,
  568. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  569. STGMEDIUM stgm;
  570. HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
  571. if (!SUCCEEDED(hres) || !stgm.hGlobal)
  572. return NOERROR; // Why bother reporting a failure, here?
  573. UINT ucFiles = stgm.hGlobal ?
  574. DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
  575. if (ucFiles != 1) {
  576. ReleaseStgMedium(&stgm);
  577. return NOERROR;
  578. }
  579. DragQueryFile((HDROP) stgm.hGlobal, 0, acFile,
  580. sizeof acFile/ sizeof acFile[0]);
  581. ReleaseStgMedium(&stgm);
  582. }
  583. // Create the property sheet- it will get deleted if it is not in
  584. // use when the shell tries to unload the extension
  585. CProfileInformationPage *pcpip =
  586. new CProfileInformationPage(CGlobals::Instance(), acFile);
  587. if ((pcpip != NULL && pcpip -> Handle()) && !(*lpfnAddPage)(pcpip -> Handle(), lParam))
  588. DestroyPropertySheetPage(pcpip -> Handle());
  589. return NOERROR;
  590. }
  591. // This member function handles the associate device tab
  592. HRESULT CICMUserInterface::AddAssociateTab(LPFNADDPROPSHEETPAGE lpfnAddPage,
  593. LPARAM lParam) {
  594. _RPTF0(_CRT_WARN, "CICMUserInterface::AddAssociateTab\n");
  595. TCHAR acFile[_MAX_PATH];
  596. // Load the profile(s) in the list.
  597. if(m_lpdoTarget) {
  598. FORMATETC fmte = {CF_HDROP, (DVTARGETDEVICE FAR *)NULL,
  599. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  600. STGMEDIUM stgm;
  601. HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
  602. if (!SUCCEEDED(hres) || !stgm.hGlobal)
  603. return NOERROR; // Why bother reporting a failure, here?
  604. UINT ucFiles = stgm.hGlobal ?
  605. DragQueryFile((HDROP) stgm.hGlobal, 0xFFFFFFFFL , 0, 0) : 0;
  606. if (ucFiles != 1) {
  607. ReleaseStgMedium(&stgm);
  608. return NOERROR;
  609. }
  610. DragQueryFile((HDROP) stgm.hGlobal, 0, acFile,
  611. sizeof acFile/ sizeof acFile[0]);
  612. ReleaseStgMedium(&stgm);
  613. }
  614. // Create the property sheet- it will get deleted if it is not in
  615. // use when the shell tries to unload the extension
  616. CProfileAssociationPage *pcpap =
  617. new CProfileAssociationPage(CGlobals::Instance(), acFile);
  618. if ((pcpap != NULL && pcpap -> Handle()) && !(*lpfnAddPage)(pcpap -> Handle(), lParam))
  619. DestroyPropertySheetPage(pcpap -> Handle());
  620. return NOERROR;
  621. }
  622. // This member function handles the monitor color management tab
  623. // In this case, no data object is given.
  624. // Private monitor enumeration function
  625. HRESULT CICMUserInterface::AddMonitorTab(LPFNADDPROPSHEETPAGE lpfnAddPage,
  626. LPARAM lParam) {
  627. // Create the property sheet- it will get deleted if it is not in
  628. // use when the shell tries to unload the extension
  629. CString csMonitorDevice;
  630. CString csMonitorFriendlyName;
  631. STGMEDIUM stgm;
  632. STGMEDIUM *pstgm = (STGMEDIUM *) NULL;
  633. if (m_lpdoTarget) {
  634. FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Display Device")),
  635. (DVTARGETDEVICE FAR *) NULL,
  636. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  637. // Get device name from IDataObject.
  638. HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
  639. if (!SUCCEEDED(hres) || !stgm.hGlobal) {
  640. return NOERROR; // Why bother reporting a failure, here?
  641. }
  642. // The storage contains Display device path (\\.\DisplayX) in UNICODE.
  643. pstgm = &stgm;
  644. LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(pstgm->hGlobal);
  645. CString csMonitorDevicePath = lpDeviceName;
  646. // Query the device id, friendly name and other on the display device.
  647. DISPLAY_DEVICE ddPriv;
  648. ddPriv.cb = sizeof(ddPriv);
  649. if (!EnumDisplayDevices((LPCTSTR)csMonitorDevicePath, 0, &ddPriv, 0))
  650. {
  651. return NOERROR; // Why bother reporting a failure, here?
  652. }
  653. #if HIDEYUKN_DBG
  654. MessageBox(NULL,csMonitorDevicePath,TEXT(""),MB_OK);
  655. MessageBox(NULL,(LPCTSTR)ddPriv.DeviceID,TEXT(""),MB_OK);
  656. MessageBox(NULL,(LPCTSTR)ddPriv.DeviceString,TEXT(""),MB_OK);
  657. #endif
  658. // Use deviceId (PnP Id) as device name, and set friendly name
  659. csMonitorDevice = (LPTSTR)(ddPriv.DeviceID);
  660. csMonitorFriendlyName = (LPTSTR)(ddPriv.DeviceString);
  661. }
  662. else
  663. {
  664. // if we don't have IDataObject, enumerate monitor,
  665. // then use 1st entry.
  666. CMonitorList cml;
  667. cml.Enumerate();
  668. _ASSERTE(cml.Count()); // At least, we should have one Monitor.
  669. csMonitorDevice = csMonitorFriendlyName = cml.DeviceName(0);
  670. }
  671. CMonitorProfileManagement *pcmpm =
  672. new CMonitorProfileManagement(csMonitorDevice,
  673. csMonitorFriendlyName,
  674. CGlobals::Instance());
  675. if ((pcmpm != NULL) && !(*lpfnAddPage)(pcmpm -> Handle(), lParam))
  676. DestroyPropertySheetPage(pcmpm -> Handle());
  677. if (pstgm) {
  678. GlobalUnlock(pstgm->hGlobal);
  679. ReleaseStgMedium(pstgm);
  680. }
  681. return NOERROR;
  682. }
  683. // Private scanner enumeration function
  684. HRESULT CICMUserInterface::AddScannerTab(LPFNADDPROPSHEETPAGE lpfnAddPage,
  685. LPARAM lParam) {
  686. // Create the property sheet- it will get deleted if it is not in
  687. // use when the shell tries to unload the extension
  688. CString csScannerDevice;
  689. STGMEDIUM stgm;
  690. STGMEDIUM *pstgm = (STGMEDIUM *) NULL;
  691. if (m_lpdoTarget) {
  692. FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("STIDeviceName")),
  693. (DVTARGETDEVICE FAR *) NULL,
  694. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  695. // Get device name from IDataObject.
  696. HRESULT hres = m_lpdoTarget -> GetData(&fmte, &stgm);
  697. if (!SUCCEEDED(hres) || !stgm.hGlobal) {
  698. return NOERROR; // Why bother reporting a failure, here?
  699. }
  700. // The storage contains Scanner in UNICODE string.
  701. pstgm = &stgm;
  702. LPCWSTR lpDeviceName = (LPCWSTR) GlobalLock(pstgm->hGlobal);
  703. csScannerDevice = lpDeviceName;
  704. #if HIDEYUKN_DBG
  705. MessageBox(NULL,csScannerDevice,TEXT(""),MB_OK);
  706. #endif
  707. } else {
  708. // if we don't have IDataObject, enumerate monitor,
  709. // then use 1st entry.
  710. CScannerList csl;
  711. csl.Enumerate();
  712. _ASSERTE(csl.Count());
  713. csScannerDevice = csl.DeviceName(0);
  714. }
  715. CScannerProfileManagement *pcspm =
  716. new CScannerProfileManagement(csScannerDevice, CGlobals::Instance());
  717. if ((pcspm != NULL) && !(*lpfnAddPage)(pcspm -> Handle(), lParam))
  718. DestroyPropertySheetPage(pcspm -> Handle());
  719. if (pstgm) {
  720. GlobalUnlock(pstgm->hGlobal);
  721. ReleaseStgMedium(pstgm);
  722. }
  723. return NOERROR;
  724. }
  725. // The following is a helper function- it takes a Shell ID List array,
  726. // representing a printer in a printers folder, and a CString. It
  727. // initializes the CString with the correct name of the printer.
  728. static void RetrievePrinterName(LPIDA lpida, CString& csTarget) {
  729. // Extract the container (Printers Folder) and target (Printer)
  730. // IDs from the array.
  731. LPCITEMIDLIST pciilContainer =
  732. (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[0]);
  733. LPCITEMIDLIST pciilTarget =
  734. (LPCITEMIDLIST)((LPBYTE) lpida + lpida -> aoffset[1]);
  735. if (!pciilContainer || !pciilTarget)
  736. return;
  737. // Get a pointer to the printers folder.
  738. LPSHELLFOLDER psfDesktop, psfPrinterFolder;
  739. if (FAILED(SHGetDesktopFolder(&psfDesktop)))
  740. return;
  741. if (FAILED(psfDesktop -> BindToObject(pciilContainer, NULL,
  742. IID_IShellFolder, (void **) &psfPrinterFolder))) {
  743. psfDesktop -> Release();
  744. return;
  745. }
  746. // Retrieve the printer's display name
  747. STRRET strret;
  748. if (FAILED(psfPrinterFolder ->
  749. GetDisplayNameOf(pciilTarget, SHGDN_FORPARSING, &strret))) {
  750. psfPrinterFolder -> Release();
  751. psfDesktop -> Release();
  752. return;
  753. }
  754. // Copy the display name- the CString class now handles any encoding
  755. // issues
  756. switch (strret.uType) {
  757. case STRRET_WSTR:
  758. // This is a Unicode string which was IMalloc'd
  759. csTarget = strret.pOleStr;
  760. IMalloc *pim;
  761. if (SUCCEEDED(CoGetMalloc(1, &pim))) {
  762. pim -> Free(strret.pOleStr);
  763. pim -> Release();
  764. }
  765. break;
  766. case STRRET_CSTR:
  767. // This is an ANSI string in the buffer
  768. csTarget = strret.cStr;
  769. break;
  770. case STRRET_OFFSET:
  771. // This is an ANSI string at the given offset into the SHITEMID
  772. // which pciilTarget points to.
  773. csTarget = (LPCSTR) pciilTarget + strret.uOffset;
  774. }
  775. psfPrinterFolder -> Release();
  776. psfDesktop -> Release();
  777. }
  778. // Private member function for handling the printer profile manamgment tab.
  779. HRESULT CICMUserInterface::AddPrinterTab(LPFNADDPROPSHEETPAGE lpfnAddPage,
  780. LPARAM lParam) {
  781. // The list is formatted as a Shell IDList Array
  782. FORMATETC fmte = { (CLIPFORMAT)RegisterClipboardFormat(_TEXT("Shell IDList Array")),
  783. (DVTARGETDEVICE FAR *) NULL,
  784. DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  785. STGMEDIUM stgm;
  786. HRESULT hres = m_lpdoTarget ?
  787. m_lpdoTarget -> GetData(&fmte, &stgm) : 0;
  788. if (!SUCCEEDED(hres) || !stgm.hGlobal)
  789. return NOERROR; // Why bother reporting a failure, here?
  790. CString csPrinter;
  791. RetrievePrinterName((LPIDA) stgm.hGlobal, csPrinter);
  792. #if HIDEYUKN_DBG
  793. MessageBox(NULL,csPrinter,TEXT(""),MB_OK);
  794. #endif
  795. // If this is not a color printer, forget it...
  796. if (!CGlobals::ThisIsAColorPrinter(csPrinter)) {
  797. ReleaseStgMedium(&stgm);
  798. return NOERROR;
  799. }
  800. // Create the property sheet- it will get deleted if it is not in use when
  801. // the shell tries to unload the extension
  802. CPrinterProfileManagement *pcppm =
  803. new CPrinterProfileManagement(csPrinter, CGlobals::Instance());
  804. ReleaseStgMedium(&stgm);
  805. if (!pcppm)
  806. return E_OUTOFMEMORY;
  807. if (!(*lpfnAddPage)(pcppm -> Handle(), lParam))
  808. DestroyPropertySheetPage(pcppm -> Handle());
  809. return NOERROR;
  810. }
  811. PSTR
  812. GetFilenameFromPath(
  813. PSTR pPathName
  814. )
  815. {
  816. DWORD dwLen; // length of pathname
  817. dwLen = lstrlenA(pPathName);
  818. //
  819. // Go to the end of the pathname, and start going backwards till
  820. // you reach the beginning or a backslash
  821. //
  822. pPathName += dwLen;
  823. while (dwLen-- && --pPathName)
  824. {
  825. if (*pPathName == '\\')
  826. {
  827. pPathName++;
  828. break;
  829. }
  830. }
  831. //
  832. // if *pPathName is zero, then we had a string that ends in a backslash
  833. //
  834. return *pPathName ? pPathName : NULL;
  835. }