jump-list.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. /*
  2. * jump-list.c: support for Windows 7 jump lists.
  3. *
  4. * The Windows 7 jumplist is a customizable list defined by the
  5. * application. It is persistent across application restarts: the OS
  6. * maintains the list when the app is not running. The list is shown
  7. * when the user right-clicks on the taskbar button of a running app
  8. * or a pinned non-running application. We use the jumplist to
  9. * maintain a list of recently started saved sessions, started either
  10. * by doubleclicking on a saved session, or with the command line
  11. * "-load" parameter.
  12. *
  13. * Since the jumplist is write-only: it can only be replaced and the
  14. * current list cannot be read, we must maintain the contents of the
  15. * list persistently in the registry. The file winstore.h contains
  16. * functions to directly manipulate these registry entries. This file
  17. * contains higher level functions to manipulate the jumplist.
  18. */
  19. #include <assert.h>
  20. #include "putty.h"
  21. #include "storage.h"
  22. #define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in
  23. * the jumplist than this, regardless of
  24. * user preferences. */
  25. /*
  26. * COM structures and functions.
  27. */
  28. #ifndef PROPERTYKEY_DEFINED
  29. #define PROPERTYKEY_DEFINED
  30. typedef struct _tagpropertykey {
  31. GUID fmtid;
  32. DWORD pid;
  33. } PROPERTYKEY;
  34. #endif
  35. #ifndef _REFPROPVARIANT_DEFINED
  36. #define _REFPROPVARIANT_DEFINED
  37. typedef PROPVARIANT *REFPROPVARIANT;
  38. #endif
  39. /* MinGW doesn't define this yet: */
  40. #if !defined _PROPVARIANTINIT_DEFINED_ && !defined _PROPVARIANT_INIT_DEFINED_
  41. #define _PROPVARIANTINIT_DEFINED_
  42. #define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))
  43. #endif
  44. #define IID_IShellLink IID_IShellLinkA
  45. typedef struct ICustomDestinationListVtbl {
  46. HRESULT ( __stdcall *QueryInterface ) (
  47. /* [in] ICustomDestinationList*/ void *This,
  48. /* [in] */ const GUID * const riid,
  49. /* [out] */ void **ppvObject);
  50. ULONG ( __stdcall *AddRef )(
  51. /* [in] ICustomDestinationList*/ void *This);
  52. ULONG ( __stdcall *Release )(
  53. /* [in] ICustomDestinationList*/ void *This);
  54. HRESULT ( __stdcall *SetAppID )(
  55. /* [in] ICustomDestinationList*/ void *This,
  56. /* [string][in] */ LPCWSTR pszAppID);
  57. HRESULT ( __stdcall *BeginList )(
  58. /* [in] ICustomDestinationList*/ void *This,
  59. /* [out] */ UINT *pcMinSlots,
  60. /* [in] */ const GUID * const riid,
  61. /* [out] */ void **ppv);
  62. HRESULT ( __stdcall *AppendCategory )(
  63. /* [in] ICustomDestinationList*/ void *This,
  64. /* [string][in] */ LPCWSTR pszCategory,
  65. /* [in] IObjectArray*/ void *poa);
  66. HRESULT ( __stdcall *AppendKnownCategory )(
  67. /* [in] ICustomDestinationList*/ void *This,
  68. /* [in] KNOWNDESTCATEGORY*/ int category);
  69. HRESULT ( __stdcall *AddUserTasks )(
  70. /* [in] ICustomDestinationList*/ void *This,
  71. /* [in] IObjectArray*/ void *poa);
  72. HRESULT ( __stdcall *CommitList )(
  73. /* [in] ICustomDestinationList*/ void *This);
  74. HRESULT ( __stdcall *GetRemovedDestinations )(
  75. /* [in] ICustomDestinationList*/ void *This,
  76. /* [in] */ const IID * const riid,
  77. /* [out] */ void **ppv);
  78. HRESULT ( __stdcall *DeleteList )(
  79. /* [in] ICustomDestinationList*/ void *This,
  80. /* [string][unique][in] */ LPCWSTR pszAppID);
  81. HRESULT ( __stdcall *AbortList )(
  82. /* [in] ICustomDestinationList*/ void *This);
  83. } ICustomDestinationListVtbl;
  84. typedef struct ICustomDestinationList
  85. {
  86. ICustomDestinationListVtbl *lpVtbl;
  87. } ICustomDestinationList;
  88. typedef struct IObjectArrayVtbl
  89. {
  90. HRESULT ( __stdcall *QueryInterface )(
  91. /* [in] IObjectArray*/ void *This,
  92. /* [in] */ const GUID * const riid,
  93. /* [out] */ void **ppvObject);
  94. ULONG ( __stdcall *AddRef )(
  95. /* [in] IObjectArray*/ void *This);
  96. ULONG ( __stdcall *Release )(
  97. /* [in] IObjectArray*/ void *This);
  98. HRESULT ( __stdcall *GetCount )(
  99. /* [in] IObjectArray*/ void *This,
  100. /* [out] */ UINT *pcObjects);
  101. HRESULT ( __stdcall *GetAt )(
  102. /* [in] IObjectArray*/ void *This,
  103. /* [in] */ UINT uiIndex,
  104. /* [in] */ const GUID * const riid,
  105. /* [out] */ void **ppv);
  106. } IObjectArrayVtbl;
  107. typedef struct IObjectArray
  108. {
  109. IObjectArrayVtbl *lpVtbl;
  110. } IObjectArray;
  111. typedef struct IShellLinkVtbl
  112. {
  113. HRESULT ( __stdcall *QueryInterface )(
  114. /* [in] IShellLink*/ void *This,
  115. /* [in] */ const GUID * const riid,
  116. /* [out] */ void **ppvObject);
  117. ULONG ( __stdcall *AddRef )(
  118. /* [in] IShellLink*/ void *This);
  119. ULONG ( __stdcall *Release )(
  120. /* [in] IShellLink*/ void *This);
  121. HRESULT ( __stdcall *GetPath )(
  122. /* [in] IShellLink*/ void *This,
  123. /* [string][out] */ LPSTR pszFile,
  124. /* [in] */ int cch,
  125. /* [unique][out][in] */ WIN32_FIND_DATAA *pfd,
  126. /* [in] */ DWORD fFlags);
  127. HRESULT ( __stdcall *GetIDList )(
  128. /* [in] IShellLink*/ void *This,
  129. /* [out] LPITEMIDLIST*/ void **ppidl);
  130. HRESULT ( __stdcall *SetIDList )(
  131. /* [in] IShellLink*/ void *This,
  132. /* [in] LPITEMIDLIST*/ void *pidl);
  133. HRESULT ( __stdcall *GetDescription )(
  134. /* [in] IShellLink*/ void *This,
  135. /* [string][out] */ LPSTR pszName,
  136. /* [in] */ int cch);
  137. HRESULT ( __stdcall *SetDescription )(
  138. /* [in] IShellLink*/ void *This,
  139. /* [string][in] */ LPCSTR pszName);
  140. HRESULT ( __stdcall *GetWorkingDirectory )(
  141. /* [in] IShellLink*/ void *This,
  142. /* [string][out] */ LPSTR pszDir,
  143. /* [in] */ int cch);
  144. HRESULT ( __stdcall *SetWorkingDirectory )(
  145. /* [in] IShellLink*/ void *This,
  146. /* [string][in] */ LPCSTR pszDir);
  147. HRESULT ( __stdcall *GetArguments )(
  148. /* [in] IShellLink*/ void *This,
  149. /* [string][out] */ LPSTR pszArgs,
  150. /* [in] */ int cch);
  151. HRESULT ( __stdcall *SetArguments )(
  152. /* [in] IShellLink*/ void *This,
  153. /* [string][in] */ LPCSTR pszArgs);
  154. HRESULT ( __stdcall *GetHotkey )(
  155. /* [in] IShellLink*/ void *This,
  156. /* [out] */ WORD *pwHotkey);
  157. HRESULT ( __stdcall *SetHotkey )(
  158. /* [in] IShellLink*/ void *This,
  159. /* [in] */ WORD wHotkey);
  160. HRESULT ( __stdcall *GetShowCmd )(
  161. /* [in] IShellLink*/ void *This,
  162. /* [out] */ int *piShowCmd);
  163. HRESULT ( __stdcall *SetShowCmd )(
  164. /* [in] IShellLink*/ void *This,
  165. /* [in] */ int iShowCmd);
  166. HRESULT ( __stdcall *GetIconLocation )(
  167. /* [in] IShellLink*/ void *This,
  168. /* [string][out] */ LPSTR pszIconPath,
  169. /* [in] */ int cch,
  170. /* [out] */ int *piIcon);
  171. HRESULT ( __stdcall *SetIconLocation )(
  172. /* [in] IShellLink*/ void *This,
  173. /* [string][in] */ LPCSTR pszIconPath,
  174. /* [in] */ int iIcon);
  175. HRESULT ( __stdcall *SetRelativePath )(
  176. /* [in] IShellLink*/ void *This,
  177. /* [string][in] */ LPCSTR pszPathRel,
  178. /* [in] */ DWORD dwReserved);
  179. HRESULT ( __stdcall *Resolve )(
  180. /* [in] IShellLink*/ void *This,
  181. /* [unique][in] */ HWND hwnd,
  182. /* [in] */ DWORD fFlags);
  183. HRESULT ( __stdcall *SetPath )(
  184. /* [in] IShellLink*/ void *This,
  185. /* [string][in] */ LPCSTR pszFile);
  186. } IShellLinkVtbl;
  187. typedef struct IShellLink
  188. {
  189. IShellLinkVtbl *lpVtbl;
  190. } IShellLink;
  191. typedef struct IObjectCollectionVtbl
  192. {
  193. HRESULT ( __stdcall *QueryInterface )(
  194. /* [in] IShellLink*/ void *This,
  195. /* [in] */ const GUID * const riid,
  196. /* [out] */ void **ppvObject);
  197. ULONG ( __stdcall *AddRef )(
  198. /* [in] IShellLink*/ void *This);
  199. ULONG ( __stdcall *Release )(
  200. /* [in] IShellLink*/ void *This);
  201. HRESULT ( __stdcall *GetCount )(
  202. /* [in] IShellLink*/ void *This,
  203. /* [out] */ UINT *pcObjects);
  204. HRESULT ( __stdcall *GetAt )(
  205. /* [in] IShellLink*/ void *This,
  206. /* [in] */ UINT uiIndex,
  207. /* [in] */ const GUID * const riid,
  208. /* [iid_is][out] */ void **ppv);
  209. HRESULT ( __stdcall *AddObject )(
  210. /* [in] IShellLink*/ void *This,
  211. /* [in] */ void *punk);
  212. HRESULT ( __stdcall *AddFromArray )(
  213. /* [in] IShellLink*/ void *This,
  214. /* [in] */ IObjectArray *poaSource);
  215. HRESULT ( __stdcall *RemoveObjectAt )(
  216. /* [in] IShellLink*/ void *This,
  217. /* [in] */ UINT uiIndex);
  218. HRESULT ( __stdcall *Clear )(
  219. /* [in] IShellLink*/ void *This);
  220. } IObjectCollectionVtbl;
  221. typedef struct IObjectCollection
  222. {
  223. IObjectCollectionVtbl *lpVtbl;
  224. } IObjectCollection;
  225. typedef struct IPropertyStoreVtbl
  226. {
  227. HRESULT ( __stdcall *QueryInterface )(
  228. /* [in] IPropertyStore*/ void *This,
  229. /* [in] */ const GUID * const riid,
  230. /* [iid_is][out] */ void **ppvObject);
  231. ULONG ( __stdcall *AddRef )(
  232. /* [in] IPropertyStore*/ void *This);
  233. ULONG ( __stdcall *Release )(
  234. /* [in] IPropertyStore*/ void *This);
  235. HRESULT ( __stdcall *GetCount )(
  236. /* [in] IPropertyStore*/ void *This,
  237. /* [out] */ DWORD *cProps);
  238. HRESULT ( __stdcall *GetAt )(
  239. /* [in] IPropertyStore*/ void *This,
  240. /* [in] */ DWORD iProp,
  241. /* [out] */ PROPERTYKEY *pkey);
  242. HRESULT ( __stdcall *GetValue )(
  243. /* [in] IPropertyStore*/ void *This,
  244. /* [in] */ const PROPERTYKEY * const key,
  245. /* [out] */ PROPVARIANT *pv);
  246. HRESULT ( __stdcall *SetValue )(
  247. /* [in] IPropertyStore*/ void *This,
  248. /* [in] */ const PROPERTYKEY * const key,
  249. /* [in] */ REFPROPVARIANT propvar);
  250. HRESULT ( __stdcall *Commit )(
  251. /* [in] IPropertyStore*/ void *This);
  252. } IPropertyStoreVtbl;
  253. typedef struct IPropertyStore
  254. {
  255. IPropertyStoreVtbl *lpVtbl;
  256. } IPropertyStore;
  257. static const CLSID CLSID_DestinationList = {
  258. 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}
  259. };
  260. static const CLSID CLSID_ShellLink = {
  261. 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
  262. };
  263. static const CLSID CLSID_EnumerableObjectCollection = {
  264. 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}
  265. };
  266. static const IID IID_IObjectCollection = {
  267. 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}
  268. };
  269. static const IID IID_IShellLink = {
  270. 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
  271. };
  272. static const IID IID_ICustomDestinationList = {
  273. 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}
  274. };
  275. static const IID IID_IObjectArray = {
  276. 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}
  277. };
  278. static const IID IID_IPropertyStore = {
  279. 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}
  280. };
  281. static const PROPERTYKEY PKEY_Title = {
  282. {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},
  283. 0x00000002
  284. };
  285. /* Type-checking macro to provide arguments for CoCreateInstance()
  286. * etc, ensuring that 'obj' really is a 'type **'. */
  287. #define typecheck(checkexpr, result) \
  288. (sizeof(checkexpr) ? (result) : (result))
  289. #define COMPTR(type, obj) &IID_##type, \
  290. typecheck((obj)-(type **)(obj), (void **)(void *)(obj))
  291. static char putty_path[2048];
  292. /*
  293. * Function to make an IShellLink describing a particular PuTTY
  294. * command. If 'appname' is null, the command run will be the one
  295. * returned by GetModuleFileName, i.e. our own executable; if it's
  296. * non-null then it will be assumed to be a filename in the same
  297. * directory as our own executable, and the return value will be NULL
  298. * if that file doesn't exist.
  299. *
  300. * If 'sessionname' is null then no command line will be passed to the
  301. * program. If it's non-null, the command line will be that text
  302. * prefixed with an @ (to load a PuTTY saved session).
  303. *
  304. * Hence, you can launch a saved session using make_shell_link(NULL,
  305. * sessionname), and launch another app using e.g.
  306. * make_shell_link("puttygen.exe", NULL).
  307. */
  308. static IShellLink *make_shell_link(const char *appname,
  309. const char *sessionname)
  310. {
  311. IShellLink *ret;
  312. char *app_path, *param_string, *desc_string;
  313. IPropertyStore *pPS;
  314. PROPVARIANT pv;
  315. /* Retrieve path to executable. */
  316. if (!putty_path[0])
  317. GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1);
  318. if (appname) {
  319. char *p, *q = putty_path;
  320. FILE *fp;
  321. if ((p = strrchr(q, '\\')) != NULL) q = p+1;
  322. if ((p = strrchr(q, ':')) != NULL) q = p+1;
  323. app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path,
  324. appname);
  325. if ((fp = fopen(app_path, "r")) == NULL) {
  326. sfree(app_path);
  327. return NULL;
  328. }
  329. fclose(fp);
  330. } else {
  331. app_path = dupstr(putty_path);
  332. }
  333. /* Check if this is a valid session, otherwise don't add. */
  334. if (sessionname) {
  335. settings_r *psettings_tmp = open_settings_r(sessionname);
  336. if (!psettings_tmp) {
  337. sfree(app_path);
  338. return NULL;
  339. }
  340. close_settings_r(psettings_tmp);
  341. }
  342. /* Create the new item. */
  343. if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,
  344. CLSCTX_INPROC_SERVER,
  345. COMPTR(IShellLink, &ret)))) {
  346. sfree(app_path);
  347. return NULL;
  348. }
  349. /* Set path, parameters, icon and description. */
  350. ret->lpVtbl->SetPath(ret, app_path);
  351. if (sessionname) {
  352. /* The leading space is reported to work around a Windows 10
  353. * behaviour change in which an argument string starting with
  354. * '@' causes the SetArguments method to silently do the wrong
  355. * thing. */
  356. param_string = dupcat(" @", sessionname);
  357. } else {
  358. param_string = dupstr("");
  359. }
  360. ret->lpVtbl->SetArguments(ret, param_string);
  361. sfree(param_string);
  362. if (sessionname) {
  363. desc_string = dupcat("Connect to PuTTY session '", sessionname, "'");
  364. } else {
  365. assert(appname);
  366. desc_string = dupprintf("Run %.*s",
  367. (int)strcspn(appname, "."), appname);
  368. }
  369. ret->lpVtbl->SetDescription(ret, desc_string);
  370. sfree(desc_string);
  371. ret->lpVtbl->SetIconLocation(ret, app_path, 0);
  372. /* To set the link title, we require the property store of the link. */
  373. if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret,
  374. COMPTR(IPropertyStore, &pPS)))) {
  375. PropVariantInit(&pv);
  376. pv.vt = VT_LPSTR;
  377. if (sessionname) {
  378. pv.pszVal = dupstr(sessionname);
  379. } else {
  380. assert(appname);
  381. pv.pszVal = dupprintf("Run %.*s",
  382. (int)strcspn(appname, "."), appname);
  383. }
  384. pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);
  385. sfree(pv.pszVal);
  386. pPS->lpVtbl->Commit(pPS);
  387. pPS->lpVtbl->Release(pPS);
  388. }
  389. sfree(app_path);
  390. return ret;
  391. }
  392. /* Updates jumplist from registry. */
  393. static void update_jumplist_from_registry(void)
  394. {
  395. const char *piterator;
  396. UINT num_items;
  397. int jumplist_counter;
  398. UINT nremoved;
  399. /* Variables used by the cleanup code must be initialised to NULL,
  400. * so that we don't try to free or release them if they were never
  401. * set up. */
  402. ICustomDestinationList *pCDL = NULL;
  403. char *pjumplist_reg_entries = NULL;
  404. IObjectCollection *collection = NULL;
  405. IObjectArray *array = NULL;
  406. IShellLink *link = NULL;
  407. IObjectArray *pRemoved = NULL;
  408. bool need_abort = false;
  409. /*
  410. * Create an ICustomDestinationList: the top-level object which
  411. * deals with jump list management.
  412. */
  413. if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,
  414. CLSCTX_INPROC_SERVER,
  415. COMPTR(ICustomDestinationList, &pCDL))))
  416. goto cleanup;
  417. /*
  418. * Call its BeginList method to start compiling a list. This gives
  419. * us back 'num_items' (a hint derived from systemwide
  420. * configuration about how many things to put on the list) and
  421. * 'pRemoved' (user configuration about things to leave off the
  422. * list).
  423. */
  424. if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,
  425. COMPTR(IObjectArray, &pRemoved))))
  426. goto cleanup;
  427. need_abort = true;
  428. if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))
  429. nremoved = 0;
  430. /*
  431. * Create an object collection to form the 'Recent Sessions'
  432. * category on the jump list.
  433. */
  434. if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
  435. NULL, CLSCTX_INPROC_SERVER,
  436. COMPTR(IObjectCollection, &collection))))
  437. goto cleanup;
  438. /*
  439. * Go through the jump list entries from the registry and add each
  440. * one to the collection.
  441. */
  442. pjumplist_reg_entries = get_jumplist_registry_entries();
  443. piterator = pjumplist_reg_entries;
  444. jumplist_counter = 0;
  445. while (*piterator != '\0' &&
  446. (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) {
  447. link = make_shell_link(NULL, piterator);
  448. if (link) {
  449. UINT i;
  450. bool found;
  451. /*
  452. * Check that the link isn't in the user-removed list.
  453. */
  454. for (i = 0, found = false; i < nremoved && !found; i++) {
  455. IShellLink *rlink;
  456. if (SUCCEEDED(pRemoved->lpVtbl->GetAt(
  457. pRemoved, i, COMPTR(IShellLink, &rlink)))) {
  458. char desc1[2048], desc2[2048];
  459. if (SUCCEEDED(link->lpVtbl->GetDescription(
  460. link, desc1, sizeof(desc1)-1)) &&
  461. SUCCEEDED(rlink->lpVtbl->GetDescription(
  462. rlink, desc2, sizeof(desc2)-1)) &&
  463. !strcmp(desc1, desc2)) {
  464. found = true;
  465. }
  466. rlink->lpVtbl->Release(rlink);
  467. }
  468. }
  469. if (!found) {
  470. collection->lpVtbl->AddObject(collection, link);
  471. jumplist_counter++;
  472. }
  473. link->lpVtbl->Release(link);
  474. link = NULL;
  475. }
  476. piterator += strlen(piterator) + 1;
  477. }
  478. sfree(pjumplist_reg_entries);
  479. pjumplist_reg_entries = NULL;
  480. /*
  481. * Get the array form of the collection we've just constructed,
  482. * and put it in the jump list.
  483. */
  484. if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
  485. collection, COMPTR(IObjectArray, &array))))
  486. goto cleanup;
  487. pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);
  488. /*
  489. * Create an object collection to form the 'Tasks' category on the
  490. * jump list.
  491. */
  492. if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
  493. NULL, CLSCTX_INPROC_SERVER,
  494. COMPTR(IObjectCollection, &collection))))
  495. goto cleanup;
  496. /*
  497. * Add task entries for PuTTYgen and Pageant.
  498. */
  499. piterator = "Pageant.exe\0PuTTYgen.exe\0\0";
  500. while (*piterator != '\0') {
  501. link = make_shell_link(piterator, NULL);
  502. if (link) {
  503. collection->lpVtbl->AddObject(collection, link);
  504. link->lpVtbl->Release(link);
  505. link = NULL;
  506. }
  507. piterator += strlen(piterator) + 1;
  508. }
  509. /*
  510. * Get the array form of the collection we've just constructed,
  511. * and put it in the jump list.
  512. */
  513. if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
  514. collection, COMPTR(IObjectArray, &array))))
  515. goto cleanup;
  516. pCDL->lpVtbl->AddUserTasks(pCDL, array);
  517. /*
  518. * Now we can clean up the array and collection variables, so as
  519. * to be able to reuse them.
  520. */
  521. array->lpVtbl->Release(array);
  522. array = NULL;
  523. collection->lpVtbl->Release(collection);
  524. collection = NULL;
  525. /*
  526. * Create another object collection to form the user tasks
  527. * category.
  528. */
  529. if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
  530. NULL, CLSCTX_INPROC_SERVER,
  531. COMPTR(IObjectCollection, &collection))))
  532. goto cleanup;
  533. /*
  534. * Get the array form of the collection we've just constructed,
  535. * and put it in the jump list.
  536. */
  537. if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
  538. collection, COMPTR(IObjectArray, &array))))
  539. goto cleanup;
  540. pCDL->lpVtbl->AddUserTasks(pCDL, array);
  541. /*
  542. * Now we can clean up the array and collection variables, so as
  543. * to be able to reuse them.
  544. */
  545. array->lpVtbl->Release(array);
  546. array = NULL;
  547. collection->lpVtbl->Release(collection);
  548. collection = NULL;
  549. /*
  550. * Commit the jump list.
  551. */
  552. pCDL->lpVtbl->CommitList(pCDL);
  553. need_abort = false;
  554. /*
  555. * Clean up.
  556. */
  557. cleanup:
  558. if (pRemoved) pRemoved->lpVtbl->Release(pRemoved);
  559. if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL);
  560. if (pCDL) pCDL->lpVtbl->Release(pCDL);
  561. if (collection) collection->lpVtbl->Release(collection);
  562. if (array) array->lpVtbl->Release(array);
  563. if (link) link->lpVtbl->Release(link);
  564. sfree(pjumplist_reg_entries);
  565. }
  566. /* Clears the entire jumplist. */
  567. void clear_jumplist(void)
  568. {
  569. ICustomDestinationList *pCDL;
  570. if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER,
  571. COMPTR(ICustomDestinationList, &pCDL)) == S_OK) {
  572. pCDL->lpVtbl->DeleteList(pCDL, NULL);
  573. pCDL->lpVtbl->Release(pCDL);
  574. }
  575. }
  576. /* Adds a saved session to the Windows 7 jumplist. */
  577. void add_session_to_jumplist(const char * const sessionname)
  578. {
  579. if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
  580. return; /* do nothing on pre-Win7 systems */
  581. if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
  582. update_jumplist_from_registry();
  583. } else {
  584. /* Make sure we don't leave the jumplist dangling. */
  585. clear_jumplist();
  586. }
  587. }
  588. /* Removes a saved session from the Windows jumplist. */
  589. void remove_session_from_jumplist(const char * const sessionname)
  590. {
  591. if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
  592. return; /* do nothing on pre-Win7 systems */
  593. if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
  594. update_jumplist_from_registry();
  595. } else {
  596. /* Make sure we don't leave the jumplist dangling. */
  597. clear_jumplist();
  598. }
  599. }
  600. /* Set Explicit App User Model Id to fix removable media error with
  601. jump lists */
  602. bool set_explicit_app_user_model_id(void)
  603. {
  604. DECL_WINDOWS_FUNCTION(
  605. static, HRESULT, SetCurrentProcessExplicitAppUserModelID, (PCWSTR));
  606. static HMODULE shell32_module = 0;
  607. if (!shell32_module) {
  608. shell32_module = load_system32_dll("Shell32.dll");
  609. /*
  610. * We can't typecheck this function here, because it's defined
  611. * in <shobjidl.h>, which we're not including due to clashes
  612. * with all the manual-COM machinery above.
  613. */
  614. GET_WINDOWS_FUNCTION_NO_TYPECHECK(
  615. shell32_module, SetCurrentProcessExplicitAppUserModelID);
  616. }
  617. if (p_SetCurrentProcessExplicitAppUserModelID) {
  618. const wchar_t *id = get_app_user_model_id();
  619. if (p_SetCurrentProcessExplicitAppUserModelID(id) == S_OK) {
  620. return true;
  621. }
  622. return false;
  623. }
  624. /* Function doesn't exist, which is ok for Pre-7 systems */
  625. return true;
  626. }