2 * winjump.c: support for Windows 7 jump lists.
\r
4 * The Windows 7 jumplist is a customizable list defined by the
\r
5 * application. It is persistent across application restarts: the OS
\r
6 * maintains the list when the app is not running. The list is shown
\r
7 * when the user right-clicks on the taskbar button of a running app
\r
8 * or a pinned non-running application. We use the jumplist to
\r
9 * maintain a list of recently started saved sessions, started either
\r
10 * by doubleclicking on a saved session, or with the command line
\r
11 * "-load" parameter.
\r
13 * Since the jumplist is write-only: it can only be replaced and the
\r
14 * current list cannot be read, we must maintain the contents of the
\r
15 * list persistantly in the registry. The file winstore.h contains
\r
16 * functions to directly manipulate these registry entries. This file
\r
17 * contains higher level functions to manipulate the jumplist.
\r
23 #include "storage.h"
\r
25 #define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in
\r
26 * the jumplist than this, regardless of
\r
27 * user preferences. */
\r
30 * COM structures and functions.
\r
32 #ifndef PROPERTYKEY_DEFINED
\r
33 #define PROPERTYKEY_DEFINED
\r
34 typedef struct _tagpropertykey {
\r
39 #ifndef _REFPROPVARIANT_DEFINED
\r
40 #define _REFPROPVARIANT_DEFINED
\r
41 typedef PROPVARIANT *REFPROPVARIANT;
\r
43 /* MinGW doesn't define this yet: */
\r
44 #ifndef _PROPVARIANTINIT_DEFINED_
\r
45 #define _PROPVARIANTINIT_DEFINED_
\r
46 #define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))
\r
49 #define IID_IShellLink IID_IShellLinkA
\r
51 typedef struct ICustomDestinationListVtbl {
\r
52 HRESULT ( __stdcall *QueryInterface ) (
\r
53 /* [in] ICustomDestinationList*/ void *This,
\r
54 /* [in] */ const GUID * const riid,
\r
55 /* [out] */ void **ppvObject);
\r
57 ULONG ( __stdcall *AddRef )(
\r
58 /* [in] ICustomDestinationList*/ void *This);
\r
60 ULONG ( __stdcall *Release )(
\r
61 /* [in] ICustomDestinationList*/ void *This);
\r
63 HRESULT ( __stdcall *SetAppID )(
\r
64 /* [in] ICustomDestinationList*/ void *This,
\r
65 /* [string][in] */ LPCWSTR pszAppID);
\r
67 HRESULT ( __stdcall *BeginList )(
\r
68 /* [in] ICustomDestinationList*/ void *This,
\r
69 /* [out] */ UINT *pcMinSlots,
\r
70 /* [in] */ const GUID * const riid,
\r
71 /* [out] */ void **ppv);
\r
73 HRESULT ( __stdcall *AppendCategory )(
\r
74 /* [in] ICustomDestinationList*/ void *This,
\r
75 /* [string][in] */ LPCWSTR pszCategory,
\r
76 /* [in] IObjectArray*/ void *poa);
\r
78 HRESULT ( __stdcall *AppendKnownCategory )(
\r
79 /* [in] ICustomDestinationList*/ void *This,
\r
80 /* [in] KNOWNDESTCATEGORY*/ int category);
\r
82 HRESULT ( __stdcall *AddUserTasks )(
\r
83 /* [in] ICustomDestinationList*/ void *This,
\r
84 /* [in] IObjectArray*/ void *poa);
\r
86 HRESULT ( __stdcall *CommitList )(
\r
87 /* [in] ICustomDestinationList*/ void *This);
\r
89 HRESULT ( __stdcall *GetRemovedDestinations )(
\r
90 /* [in] ICustomDestinationList*/ void *This,
\r
91 /* [in] */ const IID * const riid,
\r
92 /* [out] */ void **ppv);
\r
94 HRESULT ( __stdcall *DeleteList )(
\r
95 /* [in] ICustomDestinationList*/ void *This,
\r
96 /* [string][unique][in] */ LPCWSTR pszAppID);
\r
98 HRESULT ( __stdcall *AbortList )(
\r
99 /* [in] ICustomDestinationList*/ void *This);
\r
101 } ICustomDestinationListVtbl;
\r
103 typedef struct ICustomDestinationList
\r
105 ICustomDestinationListVtbl *lpVtbl;
\r
106 } ICustomDestinationList;
\r
108 typedef struct IObjectArrayVtbl
\r
110 HRESULT ( __stdcall *QueryInterface )(
\r
111 /* [in] IObjectArray*/ void *This,
\r
112 /* [in] */ const GUID * const riid,
\r
113 /* [out] */ void **ppvObject);
\r
115 ULONG ( __stdcall *AddRef )(
\r
116 /* [in] IObjectArray*/ void *This);
\r
118 ULONG ( __stdcall *Release )(
\r
119 /* [in] IObjectArray*/ void *This);
\r
121 HRESULT ( __stdcall *GetCount )(
\r
122 /* [in] IObjectArray*/ void *This,
\r
123 /* [out] */ UINT *pcObjects);
\r
125 HRESULT ( __stdcall *GetAt )(
\r
126 /* [in] IObjectArray*/ void *This,
\r
127 /* [in] */ UINT uiIndex,
\r
128 /* [in] */ const GUID * const riid,
\r
129 /* [out] */ void **ppv);
\r
131 } IObjectArrayVtbl;
\r
133 typedef struct IObjectArray
\r
135 IObjectArrayVtbl *lpVtbl;
\r
138 typedef struct IShellLinkVtbl
\r
140 HRESULT ( __stdcall *QueryInterface )(
\r
141 /* [in] IShellLink*/ void *This,
\r
142 /* [in] */ const GUID * const riid,
\r
143 /* [out] */ void **ppvObject);
\r
145 ULONG ( __stdcall *AddRef )(
\r
146 /* [in] IShellLink*/ void *This);
\r
148 ULONG ( __stdcall *Release )(
\r
149 /* [in] IShellLink*/ void *This);
\r
151 HRESULT ( __stdcall *GetPath )(
\r
152 /* [in] IShellLink*/ void *This,
\r
153 /* [string][out] */ LPSTR pszFile,
\r
154 /* [in] */ int cch,
\r
155 /* [unique][out][in] */ WIN32_FIND_DATAA *pfd,
\r
156 /* [in] */ DWORD fFlags);
\r
158 HRESULT ( __stdcall *GetIDList )(
\r
159 /* [in] IShellLink*/ void *This,
\r
160 /* [out] LPITEMIDLIST*/ void **ppidl);
\r
162 HRESULT ( __stdcall *SetIDList )(
\r
163 /* [in] IShellLink*/ void *This,
\r
164 /* [in] LPITEMIDLIST*/ void *pidl);
\r
166 HRESULT ( __stdcall *GetDescription )(
\r
167 /* [in] IShellLink*/ void *This,
\r
168 /* [string][out] */ LPSTR pszName,
\r
169 /* [in] */ int cch);
\r
171 HRESULT ( __stdcall *SetDescription )(
\r
172 /* [in] IShellLink*/ void *This,
\r
173 /* [string][in] */ LPCSTR pszName);
\r
175 HRESULT ( __stdcall *GetWorkingDirectory )(
\r
176 /* [in] IShellLink*/ void *This,
\r
177 /* [string][out] */ LPSTR pszDir,
\r
178 /* [in] */ int cch);
\r
180 HRESULT ( __stdcall *SetWorkingDirectory )(
\r
181 /* [in] IShellLink*/ void *This,
\r
182 /* [string][in] */ LPCSTR pszDir);
\r
184 HRESULT ( __stdcall *GetArguments )(
\r
185 /* [in] IShellLink*/ void *This,
\r
186 /* [string][out] */ LPSTR pszArgs,
\r
187 /* [in] */ int cch);
\r
189 HRESULT ( __stdcall *SetArguments )(
\r
190 /* [in] IShellLink*/ void *This,
\r
191 /* [string][in] */ LPCSTR pszArgs);
\r
193 HRESULT ( __stdcall *GetHotkey )(
\r
194 /* [in] IShellLink*/ void *This,
\r
195 /* [out] */ WORD *pwHotkey);
\r
197 HRESULT ( __stdcall *SetHotkey )(
\r
198 /* [in] IShellLink*/ void *This,
\r
199 /* [in] */ WORD wHotkey);
\r
201 HRESULT ( __stdcall *GetShowCmd )(
\r
202 /* [in] IShellLink*/ void *This,
\r
203 /* [out] */ int *piShowCmd);
\r
205 HRESULT ( __stdcall *SetShowCmd )(
\r
206 /* [in] IShellLink*/ void *This,
\r
207 /* [in] */ int iShowCmd);
\r
209 HRESULT ( __stdcall *GetIconLocation )(
\r
210 /* [in] IShellLink*/ void *This,
\r
211 /* [string][out] */ LPSTR pszIconPath,
\r
212 /* [in] */ int cch,
\r
213 /* [out] */ int *piIcon);
\r
215 HRESULT ( __stdcall *SetIconLocation )(
\r
216 /* [in] IShellLink*/ void *This,
\r
217 /* [string][in] */ LPCSTR pszIconPath,
\r
218 /* [in] */ int iIcon);
\r
220 HRESULT ( __stdcall *SetRelativePath )(
\r
221 /* [in] IShellLink*/ void *This,
\r
222 /* [string][in] */ LPCSTR pszPathRel,
\r
223 /* [in] */ DWORD dwReserved);
\r
225 HRESULT ( __stdcall *Resolve )(
\r
226 /* [in] IShellLink*/ void *This,
\r
227 /* [unique][in] */ HWND hwnd,
\r
228 /* [in] */ DWORD fFlags);
\r
230 HRESULT ( __stdcall *SetPath )(
\r
231 /* [in] IShellLink*/ void *This,
\r
232 /* [string][in] */ LPCSTR pszFile);
\r
236 typedef struct IShellLink
\r
238 IShellLinkVtbl *lpVtbl;
\r
241 typedef struct IObjectCollectionVtbl
\r
243 HRESULT ( __stdcall *QueryInterface )(
\r
244 /* [in] IShellLink*/ void *This,
\r
245 /* [in] */ const GUID * const riid,
\r
246 /* [out] */ void **ppvObject);
\r
248 ULONG ( __stdcall *AddRef )(
\r
249 /* [in] IShellLink*/ void *This);
\r
251 ULONG ( __stdcall *Release )(
\r
252 /* [in] IShellLink*/ void *This);
\r
254 HRESULT ( __stdcall *GetCount )(
\r
255 /* [in] IShellLink*/ void *This,
\r
256 /* [out] */ UINT *pcObjects);
\r
258 HRESULT ( __stdcall *GetAt )(
\r
259 /* [in] IShellLink*/ void *This,
\r
260 /* [in] */ UINT uiIndex,
\r
261 /* [in] */ const GUID * const riid,
\r
262 /* [iid_is][out] */ void **ppv);
\r
264 HRESULT ( __stdcall *AddObject )(
\r
265 /* [in] IShellLink*/ void *This,
\r
266 /* [in] */ void *punk);
\r
268 HRESULT ( __stdcall *AddFromArray )(
\r
269 /* [in] IShellLink*/ void *This,
\r
270 /* [in] */ IObjectArray *poaSource);
\r
272 HRESULT ( __stdcall *RemoveObjectAt )(
\r
273 /* [in] IShellLink*/ void *This,
\r
274 /* [in] */ UINT uiIndex);
\r
276 HRESULT ( __stdcall *Clear )(
\r
277 /* [in] IShellLink*/ void *This);
\r
279 } IObjectCollectionVtbl;
\r
281 typedef struct IObjectCollection
\r
283 IObjectCollectionVtbl *lpVtbl;
\r
284 } IObjectCollection;
\r
286 typedef struct IPropertyStoreVtbl
\r
288 HRESULT ( __stdcall *QueryInterface )(
\r
289 /* [in] IPropertyStore*/ void *This,
\r
290 /* [in] */ const GUID * const riid,
\r
291 /* [iid_is][out] */ void **ppvObject);
\r
293 ULONG ( __stdcall *AddRef )(
\r
294 /* [in] IPropertyStore*/ void *This);
\r
296 ULONG ( __stdcall *Release )(
\r
297 /* [in] IPropertyStore*/ void *This);
\r
299 HRESULT ( __stdcall *GetCount )(
\r
300 /* [in] IPropertyStore*/ void *This,
\r
301 /* [out] */ DWORD *cProps);
\r
303 HRESULT ( __stdcall *GetAt )(
\r
304 /* [in] IPropertyStore*/ void *This,
\r
305 /* [in] */ DWORD iProp,
\r
306 /* [out] */ PROPERTYKEY *pkey);
\r
308 HRESULT ( __stdcall *GetValue )(
\r
309 /* [in] IPropertyStore*/ void *This,
\r
310 /* [in] */ const PROPERTYKEY * const key,
\r
311 /* [out] */ PROPVARIANT *pv);
\r
313 HRESULT ( __stdcall *SetValue )(
\r
314 /* [in] IPropertyStore*/ void *This,
\r
315 /* [in] */ const PROPERTYKEY * const key,
\r
316 /* [in] */ REFPROPVARIANT propvar);
\r
318 HRESULT ( __stdcall *Commit )(
\r
319 /* [in] IPropertyStore*/ void *This);
\r
320 } IPropertyStoreVtbl;
\r
322 typedef struct IPropertyStore
\r
324 IPropertyStoreVtbl *lpVtbl;
\r
327 static const CLSID CLSID_DestinationList = {
\r
328 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}
\r
330 static const CLSID CLSID_ShellLink = {
\r
331 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
\r
333 static const CLSID CLSID_EnumerableObjectCollection = {
\r
334 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}
\r
336 static const IID IID_IObjectCollection = {
\r
337 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}
\r
339 static const IID IID_IShellLink = {
\r
340 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
\r
342 static const IID IID_ICustomDestinationList = {
\r
343 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}
\r
345 static const IID IID_IObjectArray = {
\r
346 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}
\r
348 static const IID IID_IPropertyStore = {
\r
349 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}
\r
351 static const PROPERTYKEY PKEY_Title = {
\r
352 {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},
\r
356 /* Type-checking macro to provide arguments for CoCreateInstance() etc.
\r
357 * The pointer arithmetic is a compile-time pointer type check that 'obj'
\r
358 * really is a 'type **', but is intended to have no effect at runtime. */
\r
359 #define COMPTR(type, obj) &IID_##type, \
\r
360 (void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \
\r
361 - (sizeof((obj)-(type **)(obj))))
\r
363 static char putty_path[2048];
\r
366 * Function to make an IShellLink describing a particular PuTTY
\r
367 * command. If 'appname' is null, the command run will be the one
\r
368 * returned by GetModuleFileName, i.e. our own executable; if it's
\r
369 * non-null then it will be assumed to be a filename in the same
\r
370 * directory as our own executable, and the return value will be NULL
\r
371 * if that file doesn't exist.
\r
373 * If 'sessionname' is null then no command line will be passed to the
\r
374 * program. If it's non-null, the command line will be that text
\r
375 * prefixed with an @ (to load a PuTTY saved session).
\r
377 * Hence, you can launch a saved session using make_shell_link(NULL,
\r
378 * sessionname), and launch another app using e.g.
\r
379 * make_shell_link("puttygen.exe", NULL).
\r
381 static IShellLink *make_shell_link(const char *appname,
\r
382 const char *sessionname)
\r
385 char *app_path, *param_string, *desc_string;
\r
386 void *psettings_tmp;
\r
387 IPropertyStore *pPS;
\r
390 /* Retrieve path to executable. */
\r
391 if (!putty_path[0])
\r
392 GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1);
\r
394 char *p, *q = putty_path;
\r
397 if ((p = strrchr(q, '\\')) != NULL) q = p+1;
\r
398 if ((p = strrchr(q, ':')) != NULL) q = p+1;
\r
399 app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path,
\r
401 if ((fp = fopen(app_path, "r")) == NULL) {
\r
407 app_path = dupstr(putty_path);
\r
410 /* Check if this is a valid session, otherwise don't add. */
\r
412 psettings_tmp = open_settings_r(sessionname);
\r
413 if (!psettings_tmp)
\r
415 close_settings_r(psettings_tmp);
\r
418 /* Create the new item. */
\r
419 if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,
\r
420 CLSCTX_INPROC_SERVER,
\r
421 COMPTR(IShellLink, &ret))))
\r
424 /* Set path, parameters, icon and description. */
\r
425 ret->lpVtbl->SetPath(ret, app_path);
\r
428 param_string = dupcat("@", sessionname, NULL);
\r
430 param_string = dupstr("");
\r
432 ret->lpVtbl->SetArguments(ret, param_string);
\r
433 sfree(param_string);
\r
436 desc_string = dupcat("Connect to PuTTY session '",
\r
437 sessionname, "'", NULL);
\r
440 desc_string = dupprintf("Run %.*s", strcspn(appname, "."), appname);
\r
442 ret->lpVtbl->SetDescription(ret, desc_string);
\r
443 sfree(desc_string);
\r
445 ret->lpVtbl->SetIconLocation(ret, app_path, 0);
\r
447 /* To set the link title, we require the property store of the link. */
\r
448 if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret,
\r
449 COMPTR(IPropertyStore, &pPS)))) {
\r
450 PropVariantInit(&pv);
\r
453 pv.pszVal = dupstr(sessionname);
\r
456 pv.pszVal = dupprintf("Run %.*s", strcspn(appname, "."), appname);
\r
458 pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);
\r
460 pPS->lpVtbl->Commit(pPS);
\r
461 pPS->lpVtbl->Release(pPS);
\r
469 /* Updates jumplist from registry. */
\r
470 static void update_jumplist_from_registry(void)
\r
472 const char *piterator;
\r
474 int jumplist_counter;
\r
477 /* Variables used by the cleanup code must be initialised to NULL,
\r
478 * so that we don't try to free or release them if they were never
\r
480 ICustomDestinationList *pCDL = NULL;
\r
481 char *pjumplist_reg_entries = NULL;
\r
482 IObjectCollection *collection = NULL;
\r
483 IObjectArray *array = NULL;
\r
484 IShellLink *link = NULL;
\r
485 IObjectArray *pRemoved = NULL;
\r
486 int need_abort = FALSE;
\r
489 * Create an ICustomDestinationList: the top-level object which
\r
490 * deals with jump list management.
\r
492 if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,
\r
493 CLSCTX_INPROC_SERVER,
\r
494 COMPTR(ICustomDestinationList, &pCDL))))
\r
498 * Call its BeginList method to start compiling a list. This gives
\r
499 * us back 'num_items' (a hint derived from systemwide
\r
500 * configuration about how many things to put on the list) and
\r
501 * 'pRemoved' (user configuration about things to leave off the
\r
504 if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,
\r
505 COMPTR(IObjectArray, &pRemoved))))
\r
508 if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))
\r
512 * Create an object collection to form the 'Recent Sessions'
\r
513 * category on the jump list.
\r
515 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
\r
516 NULL, CLSCTX_INPROC_SERVER,
\r
517 COMPTR(IObjectCollection, &collection))))
\r
521 * Go through the jump list entries from the registry and add each
\r
522 * one to the collection.
\r
524 pjumplist_reg_entries = get_jumplist_registry_entries();
\r
525 piterator = pjumplist_reg_entries;
\r
526 jumplist_counter = 0;
\r
527 while (*piterator != '\0' &&
\r
528 (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) {
\r
529 link = make_shell_link(NULL, piterator);
\r
535 * Check that the link isn't in the user-removed list.
\r
537 for (i = 0, found = FALSE; i < nremoved && !found; i++) {
\r
539 if (SUCCEEDED(pRemoved->lpVtbl->GetAt
\r
540 (pRemoved, i, COMPTR(IShellLink, &rlink)))) {
\r
541 char desc1[2048], desc2[2048];
\r
542 if (SUCCEEDED(link->lpVtbl->GetDescription
\r
543 (link, desc1, sizeof(desc1)-1)) &&
\r
544 SUCCEEDED(rlink->lpVtbl->GetDescription
\r
545 (rlink, desc2, sizeof(desc2)-1)) &&
\r
546 !strcmp(desc1, desc2)) {
\r
549 rlink->lpVtbl->Release(rlink);
\r
554 collection->lpVtbl->AddObject(collection, link);
\r
555 jumplist_counter++;
\r
558 link->lpVtbl->Release(link);
\r
561 piterator += strlen(piterator) + 1;
\r
563 sfree(pjumplist_reg_entries);
\r
564 pjumplist_reg_entries = NULL;
\r
567 * Get the array form of the collection we've just constructed,
\r
568 * and put it in the jump list.
\r
570 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
\r
571 (collection, COMPTR(IObjectArray, &array))))
\r
574 pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);
\r
577 * Create an object collection to form the 'Tasks' category on the
\r
580 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
\r
581 NULL, CLSCTX_INPROC_SERVER,
\r
582 COMPTR(IObjectCollection, &collection))))
\r
586 * Add task entries for PuTTYgen and Pageant.
\r
588 piterator = "Pageant.exe\0PuTTYgen.exe\0\0";
\r
589 while (*piterator != '\0') {
\r
590 link = make_shell_link(piterator, NULL);
\r
592 collection->lpVtbl->AddObject(collection, link);
\r
593 link->lpVtbl->Release(link);
\r
596 piterator += strlen(piterator) + 1;
\r
600 * Get the array form of the collection we've just constructed,
\r
601 * and put it in the jump list.
\r
603 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
\r
604 (collection, COMPTR(IObjectArray, &array))))
\r
607 pCDL->lpVtbl->AddUserTasks(pCDL, array);
\r
610 * Now we can clean up the array and collection variables, so as
\r
611 * to be able to reuse them.
\r
613 array->lpVtbl->Release(array);
\r
615 collection->lpVtbl->Release(collection);
\r
619 * Create another object collection to form the user tasks
\r
622 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
\r
623 NULL, CLSCTX_INPROC_SERVER,
\r
624 COMPTR(IObjectCollection, &collection))))
\r
628 * Get the array form of the collection we've just constructed,
\r
629 * and put it in the jump list.
\r
631 if (!SUCCEEDED(collection->lpVtbl->QueryInterface
\r
632 (collection, COMPTR(IObjectArray, &array))))
\r
635 pCDL->lpVtbl->AddUserTasks(pCDL, array);
\r
638 * Now we can clean up the array and collection variables, so as
\r
639 * to be able to reuse them.
\r
641 array->lpVtbl->Release(array);
\r
643 collection->lpVtbl->Release(collection);
\r
647 * Commit the jump list.
\r
649 pCDL->lpVtbl->CommitList(pCDL);
\r
650 need_abort = FALSE;
\r
656 if (pRemoved) pRemoved->lpVtbl->Release(pRemoved);
\r
657 if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL);
\r
658 if (pCDL) pCDL->lpVtbl->Release(pCDL);
\r
659 if (collection) collection->lpVtbl->Release(collection);
\r
660 if (array) array->lpVtbl->Release(array);
\r
661 if (link) link->lpVtbl->Release(link);
\r
662 sfree(pjumplist_reg_entries);
\r
665 /* Clears the entire jumplist. */
\r
666 void clear_jumplist(void)
\r
668 ICustomDestinationList *pCDL;
\r
670 if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER,
\r
671 COMPTR(ICustomDestinationList, &pCDL)) == S_OK) {
\r
672 pCDL->lpVtbl->DeleteList(pCDL, NULL);
\r
673 pCDL->lpVtbl->Release(pCDL);
\r
678 /* Adds a saved session to the Windows 7 jumplist. */
\r
679 void add_session_to_jumplist(const char * const sessionname)
\r
681 if ((osVersion.dwMajorVersion < 6) ||
\r
682 (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
\r
683 return; /* do nothing on pre-Win7 systems */
\r
685 if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
\r
686 update_jumplist_from_registry();
\r
688 /* Make sure we don't leave the jumplist dangling. */
\r
693 /* Removes a saved session from the Windows jumplist. */
\r
694 void remove_session_from_jumplist(const char * const sessionname)
\r
696 if ((osVersion.dwMajorVersion < 6) ||
\r
697 (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
\r
698 return; /* do nothing on pre-Win7 systems */
\r
700 if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
\r
701 update_jumplist_from_registry();
\r
703 /* Make sure we don't leave the jumplist dangling. */
\r