Document the behaviour of Alt in the "hybrid" resizing mode alluded to in
[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
43
44#define IID_IShellLink IID_IShellLinkA
45
46typedef 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
98typedef struct ICustomDestinationList
99{
100 ICustomDestinationListVtbl *lpVtbl;
101} ICustomDestinationList;
102
103typedef 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
128typedef struct IObjectArray
129{
130 IObjectArrayVtbl *lpVtbl;
131} IObjectArray;
132
133typedef 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
231typedef struct IShellLink
232{
233 IShellLinkVtbl *lpVtbl;
234} IShellLink;
235
236typedef 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
276typedef struct IObjectCollection
277{
278 IObjectCollectionVtbl *lpVtbl;
279} IObjectCollection;
280
281typedef 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
317typedef struct IPropertyStore
318{
319 IPropertyStoreVtbl *lpVtbl;
320} IPropertyStore;
321
322static const CLSID CLSID_DestinationList = {
323 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}
324};
325static const CLSID CLSID_ShellLink = {
326 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
327};
328static const CLSID CLSID_EnumerableObjectCollection = {
329 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}
330};
331static const IID IID_IObjectCollection = {
332 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}
333};
334static const IID IID_IShellLink = {
335 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
336};
337static const IID IID_ICustomDestinationList = {
338 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}
339};
340static const IID IID_IObjectArray = {
341 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}
342};
343static const IID IID_IPropertyStore = {
344 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}
345};
346static 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
353static 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 */
371static 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. */
460static 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. */
656static 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. */
671void 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. */
686void 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}