Another big batch of memory leak fixes, again mostly on error paths.
[u/mdw/putty] / windows / winjump.c
CommitLineData
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
34typedef struct _tagpropertykey {
35 GUID fmtid;
36 DWORD pid;
37} PROPERTYKEY;
38#endif
39#ifndef _REFPROPVARIANT_DEFINED
40#define _REFPROPVARIANT_DEFINED
41typedef PROPVARIANT *REFPROPVARIANT;
42#endif
735a4294 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
073e9f42 48
49#define IID_IShellLink IID_IShellLinkA
50
51typedef 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
103typedef struct ICustomDestinationList
104{
105 ICustomDestinationListVtbl *lpVtbl;
106} ICustomDestinationList;
107
108typedef 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
133typedef struct IObjectArray
134{
135 IObjectArrayVtbl *lpVtbl;
136} IObjectArray;
137
138typedef 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
236typedef struct IShellLink
237{
238 IShellLinkVtbl *lpVtbl;
239} IShellLink;
240
241typedef 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
281typedef struct IObjectCollection
282{
283 IObjectCollectionVtbl *lpVtbl;
284} IObjectCollection;
285
286typedef 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
322typedef struct IPropertyStore
323{
324 IPropertyStoreVtbl *lpVtbl;
325} IPropertyStore;
326
327static const CLSID CLSID_DestinationList = {
328 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}
329};
330static const CLSID CLSID_ShellLink = {
331 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
332};
333static const CLSID CLSID_EnumerableObjectCollection = {
334 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}
335};
336static const IID IID_IObjectCollection = {
337 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}
338};
339static const IID IID_IShellLink = {
340 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
341};
342static const IID IID_ICustomDestinationList = {
343 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}
344};
345static const IID IID_IObjectArray = {
346 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}
347};
348static const IID IID_IPropertyStore = {
349 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}
350};
351static const PROPERTYKEY PKEY_Title = {
352 {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},
353 0x00000002
354};
355
735a4294 356/* Type-checking macro to provide arguments for CoCreateInstance() etc.
357 * The pointer arithmetic is a compile-time pointer type check that 'obj'
358 * really is a 'type **', but is intended to have no effect at runtime. */
359#define COMPTR(type, obj) &IID_##type, \
360 (void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \
361 - (sizeof((obj)-(type **)(obj))))
073e9f42 362
363static 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 */
381static 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);
0c33d3a6 413 if (!psettings_tmp) {
414 sfree(app_path);
073e9f42 415 return NULL;
0c33d3a6 416 }
073e9f42 417 close_settings_r(psettings_tmp);
418 }
419
420 /* Create the new item. */
421 if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,
422 CLSCTX_INPROC_SERVER,
0c33d3a6 423 COMPTR(IShellLink, &ret)))) {
424 sfree(app_path);
073e9f42 425 return NULL;
0c33d3a6 426 }
073e9f42 427
428 /* Set path, parameters, icon and description. */
429 ret->lpVtbl->SetPath(ret, app_path);
430
431 if (sessionname) {
432 param_string = dupcat("@", sessionname, NULL);
433 } else {
434 param_string = dupstr("");
435 }
436 ret->lpVtbl->SetArguments(ret, param_string);
437 sfree(param_string);
438
439 if (sessionname) {
440 desc_string = dupcat("Connect to PuTTY session '",
441 sessionname, "'", NULL);
442 } else {
443 assert(appname);
444 desc_string = dupprintf("Run %.*s", strcspn(appname, "."), appname);
445 }
446 ret->lpVtbl->SetDescription(ret, desc_string);
447 sfree(desc_string);
448
449 ret->lpVtbl->SetIconLocation(ret, app_path, 0);
450
451 /* To set the link title, we require the property store of the link. */
452 if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret,
453 COMPTR(IPropertyStore, &pPS)))) {
454 PropVariantInit(&pv);
455 pv.vt = VT_LPSTR;
456 if (sessionname) {
457 pv.pszVal = dupstr(sessionname);
458 } else {
459 assert(appname);
460 pv.pszVal = dupprintf("Run %.*s", strcspn(appname, "."), appname);
461 }
462 pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);
463 sfree(pv.pszVal);
464 pPS->lpVtbl->Commit(pPS);
465 pPS->lpVtbl->Release(pPS);
466 }
467
468 sfree(app_path);
469
470 return ret;
471}
472
473/* Updates jumplist from registry. */
474static void update_jumplist_from_registry(void)
475{
476 const char *piterator;
477 UINT num_items;
478 int jumplist_counter;
479 UINT nremoved;
480
481 /* Variables used by the cleanup code must be initialised to NULL,
482 * so that we don't try to free or release them if they were never
483 * set up. */
484 ICustomDestinationList *pCDL = NULL;
485 char *pjumplist_reg_entries = NULL;
486 IObjectCollection *collection = NULL;
487 IObjectArray *array = NULL;
488 IShellLink *link = NULL;
489 IObjectArray *pRemoved = NULL;
490 int need_abort = FALSE;
491
492 /*
493 * Create an ICustomDestinationList: the top-level object which
494 * deals with jump list management.
495 */
496 if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,
497 CLSCTX_INPROC_SERVER,
498 COMPTR(ICustomDestinationList, &pCDL))))
499 goto cleanup;
500
501 /*
502 * Call its BeginList method to start compiling a list. This gives
503 * us back 'num_items' (a hint derived from systemwide
504 * configuration about how many things to put on the list) and
505 * 'pRemoved' (user configuration about things to leave off the
506 * list).
507 */
508 if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,
509 COMPTR(IObjectArray, &pRemoved))))
510 goto cleanup;
511 need_abort = TRUE;
512 if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))
513 nremoved = 0;
514
515 /*
516 * Create an object collection to form the 'Recent Sessions'
517 * category on the jump list.
518 */
519 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
520 NULL, CLSCTX_INPROC_SERVER,
521 COMPTR(IObjectCollection, &collection))))
522 goto cleanup;
523
524 /*
525 * Go through the jump list entries from the registry and add each
526 * one to the collection.
527 */
528 pjumplist_reg_entries = get_jumplist_registry_entries();
529 piterator = pjumplist_reg_entries;
530 jumplist_counter = 0;
531 while (*piterator != '\0' &&
532 (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) {
533 link = make_shell_link(NULL, piterator);
534 if (link) {
535 UINT i;
536 int found;
537
538 /*
539 * Check that the link isn't in the user-removed list.
540 */
541 for (i = 0, found = FALSE; i < nremoved && !found; i++) {
542 IShellLink *rlink;
543 if (SUCCEEDED(pRemoved->lpVtbl->GetAt
544 (pRemoved, i, COMPTR(IShellLink, &rlink)))) {
545 char desc1[2048], desc2[2048];
546 if (SUCCEEDED(link->lpVtbl->GetDescription
547 (link, desc1, sizeof(desc1)-1)) &&
548 SUCCEEDED(rlink->lpVtbl->GetDescription
549 (rlink, desc2, sizeof(desc2)-1)) &&
550 !strcmp(desc1, desc2)) {
551 found = TRUE;
552 }
553 rlink->lpVtbl->Release(rlink);
554 }
555 }
556
557 if (!found) {
558 collection->lpVtbl->AddObject(collection, link);
559 jumplist_counter++;
560 }
561
562 link->lpVtbl->Release(link);
563 link = NULL;
564 }
565 piterator += strlen(piterator) + 1;
566 }
567 sfree(pjumplist_reg_entries);
568 pjumplist_reg_entries = NULL;
569
570 /*
571 * Get the array form of the collection we've just constructed,
572 * and put it in the jump list.
573 */
574 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
575 (collection, COMPTR(IObjectArray, &array))))
576 goto cleanup;
577
578 pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);
579
580 /*
581 * Create an object collection to form the 'Tasks' category on the
582 * jump list.
583 */
584 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
585 NULL, CLSCTX_INPROC_SERVER,
586 COMPTR(IObjectCollection, &collection))))
587 goto cleanup;
588
589 /*
590 * Add task entries for PuTTYgen and Pageant.
591 */
592 piterator = "Pageant.exe\0PuTTYgen.exe\0\0";
593 while (*piterator != '\0') {
594 link = make_shell_link(piterator, NULL);
595 if (link) {
596 collection->lpVtbl->AddObject(collection, link);
597 link->lpVtbl->Release(link);
598 link = NULL;
599 }
600 piterator += strlen(piterator) + 1;
601 }
602
603 /*
604 * Get the array form of the collection we've just constructed,
605 * and put it in the jump list.
606 */
607 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
608 (collection, COMPTR(IObjectArray, &array))))
609 goto cleanup;
610
611 pCDL->lpVtbl->AddUserTasks(pCDL, array);
612
613 /*
614 * Now we can clean up the array and collection variables, so as
615 * to be able to reuse them.
616 */
617 array->lpVtbl->Release(array);
618 array = NULL;
619 collection->lpVtbl->Release(collection);
620 collection = NULL;
621
622 /*
623 * Create another object collection to form the user tasks
624 * category.
625 */
626 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
627 NULL, CLSCTX_INPROC_SERVER,
628 COMPTR(IObjectCollection, &collection))))
629 goto cleanup;
630
631 /*
632 * Get the array form of the collection we've just constructed,
633 * and put it in the jump list.
634 */
635 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
636 (collection, COMPTR(IObjectArray, &array))))
637 goto cleanup;
638
639 pCDL->lpVtbl->AddUserTasks(pCDL, array);
640
641 /*
642 * Now we can clean up the array and collection variables, so as
643 * to be able to reuse them.
644 */
645 array->lpVtbl->Release(array);
646 array = NULL;
647 collection->lpVtbl->Release(collection);
648 collection = NULL;
649
650 /*
651 * Commit the jump list.
652 */
653 pCDL->lpVtbl->CommitList(pCDL);
654 need_abort = FALSE;
655
656 /*
657 * Clean up.
658 */
659 cleanup:
660 if (pRemoved) pRemoved->lpVtbl->Release(pRemoved);
661 if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL);
662 if (pCDL) pCDL->lpVtbl->Release(pCDL);
663 if (collection) collection->lpVtbl->Release(collection);
664 if (array) array->lpVtbl->Release(array);
665 if (link) link->lpVtbl->Release(link);
666 sfree(pjumplist_reg_entries);
667}
668
669/* Clears the entire jumplist. */
21cdf8c2 670void clear_jumplist(void)
073e9f42 671{
672 ICustomDestinationList *pCDL;
073e9f42 673
674 if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER,
675 COMPTR(ICustomDestinationList, &pCDL)) == S_OK) {
676 pCDL->lpVtbl->DeleteList(pCDL, NULL);
677 pCDL->lpVtbl->Release(pCDL);
678 }
679
680}
681
682/* Adds a saved session to the Windows 7 jumplist. */
683void add_session_to_jumplist(const char * const sessionname)
684{
685 if ((osVersion.dwMajorVersion < 6) ||
686 (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
687 return; /* do nothing on pre-Win7 systems */
688
689 if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
690 update_jumplist_from_registry();
691 } else {
692 /* Make sure we don't leave the jumplist dangling. */
693 clear_jumplist();
694 }
695}
696
697/* Removes a saved session from the Windows jumplist. */
698void remove_session_from_jumplist(const char * const sessionname)
699{
700 if ((osVersion.dwMajorVersion < 6) ||
701 (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
702 return; /* do nothing on pre-Win7 systems */
703
704 if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
705 update_jumplist_from_registry();
706 } else {
707 /* Make sure we don't leave the jumplist dangling. */
708 clear_jumplist();
709 }
710}