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()
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))
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
);
413 if (!psettings_tmp
) {
417 close_settings_r(psettings_tmp
);
420 /* Create the new item. */
421 if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink
, NULL
,
422 CLSCTX_INPROC_SERVER
,
423 COMPTR(IShellLink
, &ret
)))) {
428 /* Set path, parameters, icon and description. */
429 ret
->lpVtbl
->SetPath(ret
, app_path
);
432 param_string
= dupcat("@", sessionname
, NULL
);
434 param_string
= dupstr("");
436 ret
->lpVtbl
->SetArguments(ret
, param_string
);
440 desc_string
= dupcat("Connect to PuTTY session '",
441 sessionname
, "'", NULL
);
444 desc_string
= dupprintf("Run %.*s", strcspn(appname
, "."), appname
);
446 ret
->lpVtbl
->SetDescription(ret
, desc_string
);
449 ret
->lpVtbl
->SetIconLocation(ret
, app_path
, 0);
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
);
457 pv
.pszVal
= dupstr(sessionname
);
460 pv
.pszVal
= dupprintf("Run %.*s", strcspn(appname
, "."), appname
);
462 pPS
->lpVtbl
->SetValue(pPS
, &PKEY_Title
, &pv
);
464 pPS
->lpVtbl
->Commit(pPS
);
465 pPS
->lpVtbl
->Release(pPS
);
473 /* Updates jumplist from registry. */
474 static void update_jumplist_from_registry(void)
476 const char *piterator
;
478 int jumplist_counter
;
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
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
;
493 * Create an ICustomDestinationList: the top-level object which
494 * deals with jump list management.
496 if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList
, NULL
,
497 CLSCTX_INPROC_SERVER
,
498 COMPTR(ICustomDestinationList
, &pCDL
))))
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
508 if (!SUCCEEDED(pCDL
->lpVtbl
->BeginList(pCDL
, &num_items
,
509 COMPTR(IObjectArray
, &pRemoved
))))
512 if (!SUCCEEDED(pRemoved
->lpVtbl
->GetCount(pRemoved
, &nremoved
)))
516 * Create an object collection to form the 'Recent Sessions'
517 * category on the jump list.
519 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection
,
520 NULL
, CLSCTX_INPROC_SERVER
,
521 COMPTR(IObjectCollection
, &collection
))))
525 * Go through the jump list entries from the registry and add each
526 * one to the collection.
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
);
539 * Check that the link isn't in the user-removed list.
541 for (i
= 0, found
= FALSE
; i
< nremoved
&& !found
; i
++) {
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
)) {
553 rlink
->lpVtbl
->Release(rlink
);
558 collection
->lpVtbl
->AddObject(collection
, link
);
562 link
->lpVtbl
->Release(link
);
565 piterator
+= strlen(piterator
) + 1;
567 sfree(pjumplist_reg_entries
);
568 pjumplist_reg_entries
= NULL
;
571 * Get the array form of the collection we've just constructed,
572 * and put it in the jump list.
574 if (!SUCCEEDED(collection
->lpVtbl
->QueryInterface
575 (collection
, COMPTR(IObjectArray
, &array
))))
578 pCDL
->lpVtbl
->AppendCategory(pCDL
, L
"Recent Sessions", array
);
581 * Create an object collection to form the 'Tasks' category on the
584 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection
,
585 NULL
, CLSCTX_INPROC_SERVER
,
586 COMPTR(IObjectCollection
, &collection
))))
590 * Add task entries for PuTTYgen and Pageant.
592 piterator
= "Pageant.exe\0PuTTYgen.exe\0\0";
593 while (*piterator
!= '\0') {
594 link
= make_shell_link(piterator
, NULL
);
596 collection
->lpVtbl
->AddObject(collection
, link
);
597 link
->lpVtbl
->Release(link
);
600 piterator
+= strlen(piterator
) + 1;
604 * Get the array form of the collection we've just constructed,
605 * and put it in the jump list.
607 if (!SUCCEEDED(collection
->lpVtbl
->QueryInterface
608 (collection
, COMPTR(IObjectArray
, &array
))))
611 pCDL
->lpVtbl
->AddUserTasks(pCDL
, array
);
614 * Now we can clean up the array and collection variables, so as
615 * to be able to reuse them.
617 array
->lpVtbl
->Release(array
);
619 collection
->lpVtbl
->Release(collection
);
623 * Create another object collection to form the user tasks
626 if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection
,
627 NULL
, CLSCTX_INPROC_SERVER
,
628 COMPTR(IObjectCollection
, &collection
))))
632 * Get the array form of the collection we've just constructed,
633 * and put it in the jump list.
635 if (!SUCCEEDED(collection
->lpVtbl
->QueryInterface
636 (collection
, COMPTR(IObjectArray
, &array
))))
639 pCDL
->lpVtbl
->AddUserTasks(pCDL
, array
);
642 * Now we can clean up the array and collection variables, so as
643 * to be able to reuse them.
645 array
->lpVtbl
->Release(array
);
647 collection
->lpVtbl
->Release(collection
);
651 * Commit the jump list.
653 pCDL
->lpVtbl
->CommitList(pCDL
);
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
);
669 /* Clears the entire jumplist. */
670 void clear_jumplist(void)
672 ICustomDestinationList
*pCDL
;
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
);
682 /* Adds a saved session to the Windows 7 jumplist. */
683 void add_session_to_jumplist(const char * const sessionname
)
685 if ((osVersion
.dwMajorVersion
< 6) ||
686 (osVersion
.dwMajorVersion
== 6 && osVersion
.dwMinorVersion
< 1))
687 return; /* do nothing on pre-Win7 systems */
689 if (add_to_jumplist_registry(sessionname
) == JUMPLISTREG_OK
) {
690 update_jumplist_from_registry();
692 /* Make sure we don't leave the jumplist dangling. */
697 /* Removes a saved session from the Windows jumplist. */
698 void remove_session_from_jumplist(const char * const sessionname
)
700 if ((osVersion
.dwMajorVersion
< 6) ||
701 (osVersion
.dwMajorVersion
== 6 && osVersion
.dwMinorVersion
< 1))
702 return; /* do nothing on pre-Win7 systems */
704 if (remove_from_jumplist_registry(sessionname
) == JUMPLISTREG_OK
) {
705 update_jumplist_from_registry();
707 /* Make sure we don't leave the jumplist dangling. */