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