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