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.

396 lines
10 KiB

  1. #include "shellprv.h"
  2. #include "clsobj.h"
  3. #include "dpa.h"
  4. #include "ids.h"
  5. #include "ole2dup.h"
  6. /////////////////////////////////////////////////////////////////////////////
  7. // CAutomationCM
  8. class CAutomationCM :
  9. public IContextMenu,
  10. public IShellExtInit,
  11. public IPersistPropertyBag
  12. {
  13. public:
  14. // *** IUnknown methods ***
  15. STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
  16. STDMETHOD_(ULONG,AddRef)(void);
  17. STDMETHOD_(ULONG,Release)(void);
  18. // *** IContextMenu methods ***
  19. STDMETHOD(QueryContextMenu)(THIS_
  20. HMENU hmenu,
  21. UINT indexMenu,
  22. UINT idCmdFirst,
  23. UINT idCmdLast,
  24. UINT uFlags);
  25. STDMETHOD(InvokeCommand)(THIS_
  26. LPCMINVOKECOMMANDINFO pici);
  27. STDMETHOD(GetCommandString)(THIS_
  28. UINT_PTR idCmd,
  29. UINT uType,
  30. UINT * pwReserved,
  31. LPSTR pszName,
  32. UINT cchMax);
  33. // *** IShellExtInit methods ***
  34. STDMETHOD(Initialize)(THIS_
  35. LPCITEMIDLIST pidlFolder,
  36. IDataObject *pdtobj,
  37. HKEY hkeyProgID) { return S_OK; }
  38. // *** IPersist methods ***
  39. STDMETHOD(GetClassID)(THIS_
  40. CLSID *pclsid);
  41. // *** IPersistPropertyBag methods ***
  42. STDMETHOD(InitNew)(THIS);
  43. STDMETHOD(Load)(THIS_
  44. IPropertyBag *pbg,
  45. IErrorLog *plog);
  46. STDMETHOD(Save)(THIS_
  47. IPropertyBag *pbg,
  48. BOOL fClearDirty,
  49. BOOL FSaveAllProperties) { return E_NOTIMPL; }
  50. public:
  51. CAutomationCM() : _cRef(1) { }
  52. private:
  53. ~CAutomationCM();
  54. static BOOL _DestroyVARIANTARG(VARIANTARG *pvarg, LPVOID)
  55. {
  56. ::VariantClear(pvarg);
  57. return TRUE;
  58. }
  59. enum {
  60. // Any method with more than MAXPARAMS parameters should be taken
  61. // outside and shot.
  62. // Note: If you change MAXPARAMS, make sure that szParamN[] is
  63. // big enough in IPersistPropertyBag::Load.
  64. MAXPARAMS = 1000,
  65. };
  66. LONG _cRef;
  67. IDispatch * _pdisp;
  68. BSTR _bsProperties;
  69. DISPID _dispid;
  70. BOOL _fInitialized;
  71. DISPPARAMS _dp;
  72. CDSA<VARIANTARG> _dsaVarg;
  73. TCHAR _szCommandName[MAX_PATH];
  74. TCHAR _szMenuItem[MAX_PATH];
  75. };
  76. STDAPI CAutomationCM_CreateInstance(IUnknown * punkOuter, REFIID riid, void ** ppvOut)
  77. {
  78. // clsobj.c should've filtered out the aggregated scenario already
  79. ASSERT(punkOuter == NULL);
  80. *ppvOut = NULL;
  81. CAutomationCM *pauto = new CAutomationCM;
  82. if (!pauto)
  83. return E_OUTOFMEMORY;
  84. HRESULT hr = pauto->QueryInterface(riid, ppvOut);
  85. pauto->Release();
  86. return hr;
  87. }
  88. CAutomationCM::~CAutomationCM()
  89. {
  90. InitNew();
  91. ASSERT(!_dsaVarg);
  92. }
  93. // *** IUnknown::QueryInterface ***
  94. HRESULT CAutomationCM::QueryInterface(REFIID riid, void **ppv)
  95. {
  96. static const QITAB qit[] = {
  97. QITABENT(CAutomationCM, IContextMenu), // IID_IContextMenu
  98. QITABENT(CAutomationCM, IShellExtInit), // IID_IShellExtInit
  99. QITABENT(CAutomationCM, IPersist), // IID_IPersist (base for IPersistPropertyBag)
  100. QITABENT(CAutomationCM, IPersistPropertyBag), // IID_IPersistPropertyBag
  101. { 0 },
  102. };
  103. return QISearch(this, qit, riid, ppv);
  104. }
  105. // *** IUnknown::AddRef ***
  106. STDMETHODIMP_(ULONG) CAutomationCM::AddRef()
  107. {
  108. return InterlockedIncrement(&_cRef);
  109. }
  110. // *** IUnknown::Release ***
  111. STDMETHODIMP_(ULONG) CAutomationCM::Release()
  112. {
  113. ULONG cRef = InterlockedDecrement(&_cRef);
  114. if (cRef)
  115. return cRef;
  116. delete this;
  117. return 0;
  118. }
  119. // *** IPersist::GetClassID ***
  120. HRESULT CAutomationCM::GetClassID(CLSID *pclsid)
  121. {
  122. *pclsid = CLSID_AutomationCM;
  123. return S_OK;
  124. }
  125. // *** IPersistPropertyBag::InitNew ***
  126. HRESULT CAutomationCM::InitNew()
  127. {
  128. ATOMICRELEASE(_pdisp);
  129. ::SysFreeString(_bsProperties);
  130. _bsProperties = NULL;
  131. // Free the DISPPARAMs
  132. if (_dsaVarg)
  133. {
  134. _dsaVarg.DestroyCallback(_DestroyVARIANTARG, 0);
  135. }
  136. _dp.cArgs = 0;
  137. _fInitialized = FALSE;
  138. return S_OK;
  139. }
  140. //
  141. // Property bag items:
  142. //
  143. // CLSID = object to CoCreate(IID_IDispatch)
  144. // command = display name of command
  145. // method = name of method (GetIDsOfNames)
  146. // param1 .. paramN = parameters (up to MAXPARAMS)
  147. //
  148. // Parameters are passed as BSTRs (or whatever type SHPropertyBagOnRegKey
  149. // returns.)
  150. //
  151. // It is the responsibility of the target IDispatch to coerce the types
  152. // as appropriate.
  153. //
  154. // *** IPersistPropertyBag::Load ***
  155. HRESULT CAutomationCM::Load(IPropertyBag *pbag, IErrorLog *plog)
  156. {
  157. HRESULT hr;
  158. // Erase any old state
  159. InitNew();
  160. // Get the CLSID we are dispatching through
  161. CLSID clsid;
  162. hr = SHPropertyBag_ReadGUID(pbag, L"CLSID", &clsid);
  163. if (SUCCEEDED(hr))
  164. {
  165. // Must use SHExCoCreateInstance to go through the approval/app compat layer
  166. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDispatch, &_pdisp));
  167. }
  168. // Map the method to a DISPID
  169. if (SUCCEEDED(hr))
  170. {
  171. BSTR bs;
  172. hr = SHPropertyBag_ReadBSTR(pbag, L"method", &bs);
  173. if (SUCCEEDED(hr))
  174. {
  175. LPOLESTR pname = bs;
  176. hr = _pdisp->GetIDsOfNames(IID_NULL, &pname, 1, 0, &_dispid);
  177. ::SysFreeString(bs);
  178. }
  179. }
  180. // Read in the parameters
  181. if (SUCCEEDED(hr))
  182. {
  183. if (_dsaVarg.Create(4))
  184. {
  185. WCHAR szParamN[16]; // worst-case: "param1000"
  186. VARIANT var;
  187. while (_dsaVarg.GetItemCount() < MAXPARAMS)
  188. {
  189. wnsprintfW(szParamN, ARRAYSIZE(szParamN), L"param%d",
  190. _dsaVarg.GetItemCount()+1);
  191. VariantInit(&var);
  192. var.vt = VT_BSTR;
  193. if (FAILED(pbag->Read(szParamN, &var, NULL)))
  194. {
  195. // No more parameters
  196. break;
  197. }
  198. if (_dsaVarg.AppendItem((VARIANTARG*)&var) < 0)
  199. {
  200. ::VariantClear(&var);
  201. hr = E_OUTOFMEMORY;
  202. break;
  203. }
  204. }
  205. }
  206. else
  207. {
  208. // Could not create _dsaVarg
  209. hr = E_OUTOFMEMORY;
  210. }
  211. }
  212. // Get the command name
  213. if (SUCCEEDED(hr))
  214. {
  215. hr = SHPropertyBag_ReadStr(pbag, L"command", _szCommandName, ARRAYSIZE(_szCommandName));
  216. if (SUCCEEDED(hr))
  217. {
  218. hr = SHLoadIndirectString(_szCommandName, _szCommandName, ARRAYSIZE(_szCommandName), NULL);
  219. }
  220. }
  221. // Get the properties string (optional)
  222. if (SUCCEEDED(hr))
  223. {
  224. if (SUCCEEDED(SHPropertyBag_ReadBSTR(pbag, L"properties", &_bsProperties)))
  225. {
  226. ASSERT(_bsProperties);
  227. // Ignore failure here; we'll detect it later
  228. SHPropertyBag_ReadStr(pbag, L"propertiestext", _szMenuItem, ARRAYSIZE(_szMenuItem));
  229. SHLoadIndirectString(_szMenuItem, _szMenuItem, ARRAYSIZE(_szMenuItem), NULL);
  230. }
  231. }
  232. _fInitialized = SUCCEEDED(hr);
  233. return hr;
  234. }
  235. // *** IContextMenu::QueryContextMenu ***
  236. HRESULT CAutomationCM::QueryContextMenu(
  237. HMENU hmenu,
  238. UINT indexMenu,
  239. UINT idCmdFirst,
  240. UINT idCmdLast,
  241. UINT uFlags)
  242. {
  243. if (!_fInitialized) return E_FAIL;
  244. HRESULT hr;
  245. // Must have room for two items (the command and possibly also Properties)
  246. if (idCmdFirst + 1 <= idCmdLast)
  247. {
  248. if (InsertMenuW(hmenu, indexMenu, MF_BYPOSITION | MF_STRING,
  249. idCmdFirst, _szCommandName))
  250. {
  251. if (_szMenuItem[0])
  252. {
  253. InsertMenuW(hmenu, indexMenu+1, MF_BYPOSITION | MF_STRING,
  254. idCmdFirst+1, _szMenuItem);
  255. }
  256. }
  257. hr = ResultFromShort(2); // number of items added
  258. }
  259. else
  260. {
  261. hr = E_FAIL; // unable to add items
  262. }
  263. return hr;
  264. }
  265. const LPCSTR c_rgAutoCMCommands[] = {
  266. "open", // command 0
  267. "properties", // command 1 - optional
  268. };
  269. // *** IContextMenu::InvokeCommand ***
  270. HRESULT CAutomationCM::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  271. {
  272. if (!_fInitialized) return E_FAIL;
  273. HRESULT hr;
  274. int iCmd;
  275. if (!IS_INTRESOURCE(pici->lpVerb))
  276. {
  277. // If this loop fails to find a match, iCmd will be out of range
  278. // and will hit the "default:" in the switch statement below.
  279. for (iCmd = 0; iCmd < ARRAYSIZE(c_rgAutoCMCommands) - 1; iCmd++)
  280. {
  281. if (lstrcmpiA(pici->lpVerb, c_rgAutoCMCommands[iCmd]) == 0)
  282. {
  283. break;
  284. }
  285. }
  286. }
  287. else
  288. {
  289. iCmd = PtrToLong(pici->lpVerb);
  290. }
  291. switch (iCmd)
  292. {
  293. case 0: // open
  294. _dp.cArgs = _dsaVarg.GetItemCount();
  295. _dp.rgvarg = _dp.cArgs ? _dsaVarg.GetItemPtr(0) : NULL;
  296. hr = _pdisp->Invoke(_dispid, IID_NULL, 0, DISPATCH_METHOD, &_dp, NULL, NULL, NULL);
  297. break;
  298. case 1:
  299. if (_bsProperties)
  300. {
  301. hr = ShellExecCmdLine(pici->hwnd, _bsProperties,
  302. NULL, SW_SHOWNORMAL, NULL, 0) ? S_OK : E_FAIL;
  303. }
  304. else
  305. {
  306. hr = E_INVALIDARG;
  307. }
  308. break;
  309. default:
  310. hr = E_INVALIDARG;
  311. break;
  312. }
  313. return hr;
  314. }
  315. // *** IContextMenu::GetCommandString ***
  316. HRESULT CAutomationCM::GetCommandString(
  317. UINT_PTR idCmd,
  318. UINT uType,
  319. UINT * pwReserved,
  320. LPSTR pszName,
  321. UINT cchMax)
  322. {
  323. if (!_fInitialized) return E_FAIL;
  324. switch (uType)
  325. {
  326. case GCS_VERBA:
  327. if (idCmd < ARRAYSIZE(c_rgAutoCMCommands))
  328. {
  329. SHAnsiToAnsi(c_rgAutoCMCommands[idCmd], (LPSTR)pszName, cchMax);
  330. return S_OK;
  331. }
  332. break;
  333. case GCS_VERBW:
  334. if (idCmd < ARRAYSIZE(c_rgAutoCMCommands))
  335. {
  336. SHAnsiToUnicode(c_rgAutoCMCommands[idCmd], (LPWSTR)pszName, cchMax);
  337. return S_OK;
  338. }
  339. break;
  340. }
  341. return E_NOTIMPL;
  342. }