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.

397 lines
11 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. ASSERT( 0 != _cRef );
  114. ULONG cRef = InterlockedDecrement(&_cRef);
  115. if ( 0 == cRef )
  116. {
  117. delete this;
  118. }
  119. return cRef;
  120. }
  121. // *** IPersist::GetClassID ***
  122. HRESULT CAutomationCM::GetClassID(CLSID *pclsid)
  123. {
  124. *pclsid = CLSID_AutomationCM;
  125. return S_OK;
  126. }
  127. // *** IPersistPropertyBag::InitNew ***
  128. HRESULT CAutomationCM::InitNew()
  129. {
  130. ATOMICRELEASE(_pdisp);
  131. ::SysFreeString(_bsProperties);
  132. _bsProperties = NULL;
  133. // Free the DISPPARAMs
  134. if (_dsaVarg)
  135. {
  136. _dsaVarg.DestroyCallback(_DestroyVARIANTARG, 0);
  137. }
  138. _dp.cArgs = 0;
  139. _fInitialized = FALSE;
  140. return S_OK;
  141. }
  142. //
  143. // Property bag items:
  144. //
  145. // CLSID = object to CoCreate(IID_IDispatch)
  146. // command = display name of command
  147. // method = name of method (GetIDsOfNames)
  148. // param1 .. paramN = parameters (up to MAXPARAMS)
  149. //
  150. // Parameters are passed as BSTRs (or whatever type SHPropertyBagOnRegKey
  151. // returns.)
  152. //
  153. // It is the responsibility of the target IDispatch to coerce the types
  154. // as appropriate.
  155. //
  156. // *** IPersistPropertyBag::Load ***
  157. HRESULT CAutomationCM::Load(IPropertyBag *pbag, IErrorLog *plog)
  158. {
  159. HRESULT hr;
  160. // Erase any old state
  161. InitNew();
  162. // Get the CLSID we are dispatching through
  163. CLSID clsid;
  164. hr = SHPropertyBag_ReadGUID(pbag, L"CLSID", &clsid);
  165. if (SUCCEEDED(hr))
  166. {
  167. // Must use SHExCoCreateInstance to go through the approval/app compat layer
  168. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDispatch, &_pdisp));
  169. }
  170. // Map the method to a DISPID
  171. if (SUCCEEDED(hr))
  172. {
  173. BSTR bs;
  174. hr = SHPropertyBag_ReadBSTR(pbag, L"method", &bs);
  175. if (SUCCEEDED(hr))
  176. {
  177. LPOLESTR pname = bs;
  178. hr = _pdisp->GetIDsOfNames(IID_NULL, &pname, 1, 0, &_dispid);
  179. ::SysFreeString(bs);
  180. }
  181. }
  182. // Read in the parameters
  183. if (SUCCEEDED(hr))
  184. {
  185. if (_dsaVarg.Create(4))
  186. {
  187. WCHAR szParamN[16]; // worst-case: "param1000"
  188. VARIANT var;
  189. while (_dsaVarg.GetItemCount() < MAXPARAMS)
  190. {
  191. wnsprintfW(szParamN, ARRAYSIZE(szParamN), L"param%d",
  192. _dsaVarg.GetItemCount()+1);
  193. VariantInit(&var);
  194. var.vt = VT_BSTR;
  195. if (FAILED(pbag->Read(szParamN, &var, NULL)))
  196. {
  197. // No more parameters
  198. break;
  199. }
  200. if (_dsaVarg.AppendItem((VARIANTARG*)&var) < 0)
  201. {
  202. ::VariantClear(&var);
  203. hr = E_OUTOFMEMORY;
  204. break;
  205. }
  206. }
  207. }
  208. else
  209. {
  210. // Could not create _dsaVarg
  211. hr = E_OUTOFMEMORY;
  212. }
  213. }
  214. // Get the command name
  215. if (SUCCEEDED(hr))
  216. {
  217. hr = SHPropertyBag_ReadStr(pbag, L"command", _szCommandName, ARRAYSIZE(_szCommandName));
  218. if (SUCCEEDED(hr))
  219. {
  220. hr = SHLoadIndirectString(_szCommandName, _szCommandName, ARRAYSIZE(_szCommandName), NULL);
  221. }
  222. }
  223. // Get the properties string (optional)
  224. if (SUCCEEDED(hr))
  225. {
  226. if (SUCCEEDED(SHPropertyBag_ReadBSTR(pbag, L"properties", &_bsProperties)))
  227. {
  228. ASSERT(_bsProperties);
  229. // Ignore failure here; we'll detect it later
  230. SHPropertyBag_ReadStr(pbag, L"propertiestext", _szMenuItem, ARRAYSIZE(_szMenuItem));
  231. SHLoadIndirectString(_szMenuItem, _szMenuItem, ARRAYSIZE(_szMenuItem), NULL);
  232. }
  233. }
  234. _fInitialized = SUCCEEDED(hr);
  235. return hr;
  236. }
  237. // *** IContextMenu::QueryContextMenu ***
  238. HRESULT CAutomationCM::QueryContextMenu(
  239. HMENU hmenu,
  240. UINT indexMenu,
  241. UINT idCmdFirst,
  242. UINT idCmdLast,
  243. UINT uFlags)
  244. {
  245. if (!_fInitialized) return E_FAIL;
  246. HRESULT hr;
  247. // Must have room for two items (the command and possibly also Properties)
  248. if (idCmdFirst + 1 <= idCmdLast)
  249. {
  250. if (InsertMenuW(hmenu, indexMenu, MF_BYPOSITION | MF_STRING,
  251. idCmdFirst, _szCommandName))
  252. {
  253. if (_szMenuItem[0])
  254. {
  255. InsertMenuW(hmenu, indexMenu+1, MF_BYPOSITION | MF_STRING,
  256. idCmdFirst+1, _szMenuItem);
  257. }
  258. }
  259. hr = ResultFromShort(2); // number of items added
  260. }
  261. else
  262. {
  263. hr = E_FAIL; // unable to add items
  264. }
  265. return hr;
  266. }
  267. const LPCSTR c_rgAutoCMCommands[] = {
  268. "open", // command 0
  269. "properties", // command 1 - optional
  270. };
  271. // *** IContextMenu::InvokeCommand ***
  272. HRESULT CAutomationCM::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  273. {
  274. if (!_fInitialized) return E_FAIL;
  275. HRESULT hr;
  276. int iCmd;
  277. if (!IS_INTRESOURCE(pici->lpVerb))
  278. {
  279. // If this loop fails to find a match, iCmd will be out of range
  280. // and will hit the "default:" in the switch statement below.
  281. for (iCmd = 0; iCmd < ARRAYSIZE(c_rgAutoCMCommands) - 1; iCmd++)
  282. {
  283. if (lstrcmpiA(pici->lpVerb, c_rgAutoCMCommands[iCmd]) == 0)
  284. {
  285. break;
  286. }
  287. }
  288. }
  289. else
  290. {
  291. iCmd = PtrToLong(pici->lpVerb);
  292. }
  293. switch (iCmd)
  294. {
  295. case 0: // open
  296. _dp.cArgs = _dsaVarg.GetItemCount();
  297. _dp.rgvarg = _dp.cArgs ? _dsaVarg.GetItemPtr(0) : NULL;
  298. hr = _pdisp->Invoke(_dispid, IID_NULL, 0, DISPATCH_METHOD, &_dp, NULL, NULL, NULL);
  299. break;
  300. case 1:
  301. if (_bsProperties)
  302. {
  303. hr = ShellExecCmdLine(pici->hwnd, _bsProperties,
  304. NULL, SW_SHOWNORMAL, NULL, 0) ? S_OK : E_FAIL;
  305. }
  306. else
  307. {
  308. hr = E_INVALIDARG;
  309. }
  310. break;
  311. default:
  312. hr = E_INVALIDARG;
  313. break;
  314. }
  315. return hr;
  316. }
  317. // *** IContextMenu::GetCommandString ***
  318. HRESULT CAutomationCM::GetCommandString(
  319. UINT_PTR idCmd,
  320. UINT uType,
  321. UINT * pwReserved,
  322. LPSTR pszName,
  323. UINT cchMax)
  324. {
  325. if (!_fInitialized) return E_FAIL;
  326. switch (uType)
  327. {
  328. case GCS_VERBA:
  329. if (idCmd < ARRAYSIZE(c_rgAutoCMCommands))
  330. {
  331. SHAnsiToAnsi(c_rgAutoCMCommands[idCmd], (LPSTR)pszName, cchMax);
  332. return S_OK;
  333. }
  334. break;
  335. case GCS_VERBW:
  336. if (idCmd < ARRAYSIZE(c_rgAutoCMCommands))
  337. {
  338. SHAnsiToUnicode(c_rgAutoCMCommands[idCmd], (LPWSTR)pszName, cchMax);
  339. return S_OK;
  340. }
  341. break;
  342. }
  343. return E_NOTIMPL;
  344. }