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