Source code of Windows XP (NT5)
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.

470 lines
12 KiB

  1. ///////////////////
  2. // (C) COPYRIGHT MICROSOFT CORP., 1998-1999
  3. //
  4. // FILE: SHELLEXT.CPP
  5. //
  6. // DESCRIPTION: Implements IContextMenu and IShellPropSheetExt interfaces for
  7. // the WIA test camera device
  8. //
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. // Define a language-independent name for our menu verb
  12. static const CHAR g_PathVerbA[] = "ChangeRoot";
  13. static const WCHAR g_PathVerbW[] = L"ChangeRoot";
  14. /*****************************************************************************
  15. CShellExt::CShellExt
  16. ******************************************************************************/
  17. CShellExt :: CShellExt ()
  18. {
  19. }
  20. /*****************************************************************************
  21. CShellExt::~CShellExt
  22. ******************************************************************************/
  23. CShellExt::~CShellExt ()
  24. {
  25. }
  26. /*****************************************************************************
  27. CShellExt::Initialize
  28. Called by the shell when the user invokes context menu or property sheet for
  29. one of our items. For context menus the dataobject may include more than one
  30. selected item.
  31. ******************************************************************************/
  32. STDMETHODIMP CShellExt::Initialize (LPCITEMIDLIST pidlFolder,
  33. LPDATAOBJECT lpdobj,
  34. HKEY hkeyProgID)
  35. {
  36. LONG lType = 0;
  37. HRESULT hr = NOERROR;
  38. if (!lpdobj)
  39. {
  40. return E_INVALIDARG;
  41. }
  42. // For singular selections, the WIA namespace should always provide a
  43. // dataobject that also supports IWiaItem
  44. if (FAILED(lpdobj->QueryInterface (IID_IWiaItem, reinterpret_cast<LPVOID*>(&m_pItem))))
  45. {
  46. // failing that, get the list of selected items from the data object
  47. UINT uItems = 0;
  48. LPWSTR szName;
  49. LPWSTR szToken;
  50. IWiaItem *pDevice;
  51. szName = GetNamesFromDataObject (lpdobj, &uItems);
  52. // we only support singular objects
  53. if (uItems != 1)
  54. {
  55. hr = E_FAIL;
  56. }
  57. else
  58. {
  59. // The name is of this format: <device id>::<item name>
  60. LPWSTR szToken = wcstok (szName, L":");
  61. if (!szToken)
  62. {
  63. hr = E_FAIL;
  64. }
  65. // Our extension only supports root items, so make sure there's no item
  66. // name
  67. else if (wcstok (NULL, L":"))
  68. {
  69. hr = E_FAIL;
  70. }
  71. else
  72. {
  73. hr = CreateDeviceFromId (szToken, &m_pItem);
  74. }
  75. }
  76. if (szName)
  77. {
  78. delete [] szName;
  79. }
  80. }
  81. if (SUCCEEDED(hr))
  82. {
  83. m_pItem->GetItemType (&lType);
  84. if (!(lType & WiaItemTypeRoot))
  85. {
  86. hr = E_FAIL; // we only support changing the property on the root item
  87. }
  88. }
  89. return hr;
  90. }
  91. /*****************************************************************************
  92. CShellExt::AddPages
  93. Called by the shell to get our property pages.
  94. ******************************************************************************/
  95. STDMETHODIMP CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage,LPARAM lParam)
  96. {
  97. HPROPSHEETPAGE hpsp;
  98. PROPSHEETPAGE psp;
  99. HRESULT hr = E_FAIL;
  100. // We only have 1 page.
  101. psp.dwSize = sizeof(psp);
  102. psp.dwFlags = PSP_DEFAULT;
  103. psp.hInstance = g_hInst;
  104. psp.pszTemplate = MAKEINTRESOURCE(IDD_DEVICE_PROPPAGE);
  105. psp.pfnDlgProc = PropPageProc;
  106. psp.lParam = reinterpret_cast<LPARAM>(this);
  107. hpsp = CreatePropertySheetPage (&psp);
  108. if (hpsp)
  109. {
  110. hr = (*lpfnAddPage) (hpsp, lParam) ? NOERROR:E_FAIL;
  111. if (SUCCEEDED(hr))
  112. {
  113. InternalAddRef (); // the propsheetpage will release us when it is destroyed
  114. }
  115. }
  116. return E_FAIL;
  117. }
  118. /*****************************************************************************
  119. CShellExt::QueryContextMenu
  120. Called by the shell to get our context menu strings for the selected item.
  121. ******************************************************************************/
  122. STDMETHODIMP CShellExt::QueryContextMenu (HMENU hmenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)
  123. {
  124. MENUITEMINFO mii;
  125. TCHAR szPathRoot[MAX_PATH];
  126. // insert our only menu item at index indexMenu. Remember the cmd value.
  127. LoadString (g_hInst, ID_CHANGEROOT, szPathRoot, MAX_PATH);
  128. mii.cbSize = sizeof(mii);
  129. mii.fMask = MIIM_STRING | MIIM_ID;
  130. mii.fState = MFS_ENABLED;
  131. mii.wID = idCmdFirst;
  132. mii.dwTypeData = szPathRoot;
  133. m_idCmd = 0; // we only have 1 item.
  134. if (InsertMenuItem (hmenu, indexMenu, TRUE, &mii))
  135. {
  136. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
  137. }
  138. return E_FAIL;
  139. }
  140. /*****************************************************************************
  141. CShellExt::InvokeCommand
  142. Called by the shell when the user clicks one of our menu items
  143. ******************************************************************************/
  144. STDMETHODIMP CShellExt::InvokeCommand (LPCMINVOKECOMMANDINFO lpici)
  145. {
  146. UINT_PTR idCmd = reinterpret_cast<UINT_PTR>(lpici->lpVerb);
  147. if (idCmd == m_idCmd)
  148. {
  149. return GetNewRootPath (lpici->hwnd);
  150. }
  151. // it wasn't our verb.
  152. return E_FAIL;
  153. }
  154. /*****************************************************************************
  155. CShellExt::GetCommandString
  156. Called by the shell to get our language independent verb name.
  157. ******************************************************************************/
  158. STDMETHODIMP CShellExt::GetCommandString (UINT_PTR idCmd, UINT uType,UINT* pwReserved,LPSTR pszName,UINT cchMax)
  159. {
  160. if (idCmd != m_idCmd)
  161. {
  162. // not our verb
  163. return E_FAIL;
  164. }
  165. switch (uType)
  166. {
  167. case GCS_VALIDATEA:
  168. if (pszName)
  169. {
  170. lstrcpyA (pszName, g_PathVerbA);
  171. }
  172. return S_OK;
  173. case GCS_VALIDATEW:
  174. if (pszName)
  175. {
  176. lstrcpyW (reinterpret_cast<LPWSTR>(pszName), g_PathVerbW);
  177. }
  178. return S_OK;
  179. case GCS_VERBA:
  180. lstrcpyA (pszName, g_PathVerbA);
  181. break;
  182. case GCS_VERBW:
  183. lstrcpyW (reinterpret_cast<LPWSTR>(pszName), g_PathVerbW);
  184. break;
  185. default:
  186. return E_FAIL;
  187. }
  188. return NOERROR;
  189. }
  190. /*****************************************************************************
  191. GetSetRootPath
  192. Utility function for setting or retrieving the test camera's root path property
  193. ******************************************************************************/
  194. HRESULT GetSetRootPath (IWiaItem *pCamera, LPTSTR pszPath, BOOL bSet)
  195. {
  196. IWiaPropertyStorage *pps;
  197. HRESULT hr;
  198. PROPVARIANT pv;
  199. PROPSPEC ps;
  200. ps.ulKind = PRSPEC_PROPID;
  201. ps.propid = WIA_DPP_TCAM_ROOT_PATH;
  202. hr = pCamera->QueryInterface (IID_IWiaPropertyStorage, reinterpret_cast<LPVOID*>(&pps));
  203. if (SUCCEEDED(hr))
  204. {
  205. if (!bSet) // retrieval
  206. {
  207. *pszPath = TEXT('\0');
  208. hr = pps->ReadMultiple (1, &ps, &pv);
  209. if (SUCCEEDED(hr))
  210. {
  211. #ifdef UNICODE
  212. lstrcpy (pszPath, pv.bstrVal);
  213. #else
  214. WideCharToMultiByte (CP_ACP, 0,
  215. pv.bstrVal, -1,
  216. pszPath, MAX_PATH,
  217. NULL, NULL);
  218. #endif //UNICODE
  219. SysFreeString (pv.bstrVal);
  220. }
  221. }
  222. else // assignment
  223. {
  224. pv.vt = VT_BSTR;
  225. #ifdef UNICODE
  226. pv.bstrVal =SysAllocString ( pszPath);
  227. #else
  228. INT len = lstrlen(pszPath);
  229. LPWSTR pwszVal = new WCHAR[len+1];
  230. MultiByteToWideChar (CP_ACP, 0, pszPath, -1, pwszVal, len+1);
  231. pv.bstrVal =SysAllocString ( pwszVal);
  232. delete [] pwszVal;
  233. #endif // UNICODE
  234. hr = pps->WriteMultiple (1, &ps, &pv, 2);
  235. SysFreeString (pv.bstrVal);
  236. }
  237. }
  238. if (pps)
  239. {
  240. pps->Release ();
  241. }
  242. return hr;
  243. }
  244. /*****************************************************************************
  245. CShellExt::PropPageProc
  246. Dialog procedure for the simple property sheet page.
  247. ******************************************************************************/
  248. INT_PTR CALLBACK CShellExt::PropPageProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  249. {
  250. CShellExt *pThis;
  251. pThis = reinterpret_cast<CShellExt *>(GetWindowLongPtr (hwnd, DWLP_USER));
  252. TCHAR szPath[MAX_PATH] ;
  253. INT_PTR iRet = TRUE;
  254. switch (msg)
  255. {
  256. case WM_INITDIALOG:
  257. pThis = reinterpret_cast<CShellExt *>(reinterpret_cast<LPPROPSHEETPAGE>(lp)->lParam);
  258. SetWindowLongPtr (hwnd, DWLP_USER, reinterpret_cast<LONG_PTR>(pThis));
  259. GetSetRootPath (pThis->m_pItem, szPath, FALSE);
  260. SetDlgItemText (hwnd, IDC_IMAGEROOT, szPath);
  261. break;
  262. case WM_COMMAND:
  263. if (LOWORD(wp) == IDC_IMAGEROOT && HIWORD(wp) == EN_CHANGE)
  264. {
  265. SendMessage(GetParent(hwnd),
  266. PSM_CHANGED,
  267. reinterpret_cast<WPARAM>(hwnd), 0);
  268. return TRUE;
  269. }
  270. iRet = FALSE;
  271. break;
  272. case WM_NOTIFY:
  273. {
  274. LONG lCode = PSNRET_NOERROR;
  275. LPPSHNOTIFY psn = reinterpret_cast<LPPSHNOTIFY>(lp);
  276. switch (psn->hdr.code)
  277. {
  278. case PSN_APPLY:
  279. // Set the new root path property
  280. GetDlgItemText (hwnd, IDC_IMAGEROOT, szPath, MAX_PATH);
  281. if (FAILED(GetSetRootPath (pThis->m_pItem, szPath, TRUE)))
  282. {
  283. ::ShowMessage (hwnd, IDS_ERRCAPTION, IDS_ERRPROPSET);
  284. lCode = PSNRET_INVALID_NOCHANGEPAGE;
  285. }
  286. break;
  287. case PSN_KILLACTIVE:
  288. // Validate the new path is valid
  289. GetDlgItemText (hwnd, IDC_IMAGEROOT, szPath, MAX_PATH);
  290. if (! (GetFileAttributes(szPath) & FILE_ATTRIBUTE_DIRECTORY))
  291. {
  292. ::ShowMessage (hwnd, IDS_ERRCAPTION, IDS_BADPATH);
  293. lCode = TRUE;
  294. }
  295. break;
  296. default:
  297. iRet = FALSE;
  298. break;
  299. }
  300. SetWindowLongPtr (hwnd, DWLP_MSGRESULT, lCode);
  301. }
  302. break;
  303. case WM_CLOSE:
  304. pThis->InternalRelease ();
  305. break;
  306. default:
  307. iRet = FALSE;
  308. break;
  309. }
  310. return iRet;
  311. }
  312. /*****************************************************************************
  313. BrowseCallback
  314. Sets the default selection in the folder selection dialog to the current
  315. camera image root path
  316. ******************************************************************************/
  317. INT CALLBACK BrowseCallback (HWND hwnd, UINT msg, LPARAM lp, WPARAM wp)
  318. {
  319. IWiaItem *pItem = reinterpret_cast<IWiaItem*>(lp);
  320. if (BFFM_INITIALIZED == msg)
  321. {
  322. TCHAR szPath[MAX_PATH];
  323. // find the current root path
  324. GetSetRootPath (pItem, szPath, FALSE);
  325. // send the appropriate message to the dialog
  326. SendMessage (hwnd,
  327. BFFM_SETSELECTION,
  328. reinterpret_cast<LPARAM>(szPath),
  329. TRUE);
  330. }
  331. return 0;
  332. }
  333. /*****************************************************************************
  334. CShellExt::GetNewRootPath
  335. Invoked in response to choosing our context menu item
  336. ******************************************************************************/
  337. HRESULT CShellExt::GetNewRootPath(HWND hwnd)
  338. {
  339. BROWSEINFO bi;
  340. LPITEMIDLIST pidl;
  341. TCHAR szPath[MAX_PATH];
  342. TCHAR szTitle[MAX_PATH];
  343. // Init the dialog with the current path
  344. LoadString (g_hInst, IDS_BROWSETITLE, szTitle, MAX_PATH);
  345. bi.hwndOwner = hwnd;
  346. bi.pidlRoot = NULL;
  347. bi.pszDisplayName = szPath;
  348. bi.lpszTitle = szTitle;
  349. bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_EDITBOX;
  350. bi.lpfn = NULL;
  351. pidl = SHBrowseForFolder (&bi);
  352. if (pidl)
  353. {
  354. SHGetPathFromIDList (pidl, szPath);
  355. if (FAILED(GetSetRootPath (m_pItem, szPath, TRUE)))
  356. {
  357. ::ShowMessage (hwnd, IDS_ERRCAPTION, IDS_ERRPROPSET);
  358. return S_FALSE;
  359. }
  360. return NOERROR;
  361. }
  362. return S_FALSE; // don't return failure code to the shell.
  363. }