OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / WINDOWS / WINJUMP.C
1 /*\r
2  * winjump.c: support for Windows 7 jump lists.\r
3  *\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
12  *\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
18  */\r
19 \r
20 #include <assert.h>\r
21 \r
22 #include "putty.h"\r
23 #include "storage.h"\r
24 \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
28 \r
29 /*\r
30  * COM structures and functions.\r
31  */\r
32 #ifndef PROPERTYKEY_DEFINED\r
33 #define PROPERTYKEY_DEFINED\r
34 typedef struct _tagpropertykey {\r
35     GUID fmtid;\r
36     DWORD pid;\r
37 } PROPERTYKEY;\r
38 #endif\r
39 #ifndef _REFPROPVARIANT_DEFINED\r
40 #define _REFPROPVARIANT_DEFINED\r
41 typedef PROPVARIANT *REFPROPVARIANT;\r
42 #endif\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
47 #endif\r
48 \r
49 #define IID_IShellLink IID_IShellLinkA\r
50 \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
56 \r
57     ULONG ( __stdcall *AddRef )(\r
58         /* [in] ICustomDestinationList*/ void *This);\r
59 \r
60     ULONG ( __stdcall *Release )(\r
61         /* [in] ICustomDestinationList*/ void *This);\r
62 \r
63     HRESULT ( __stdcall *SetAppID )(\r
64         /* [in] ICustomDestinationList*/ void *This,\r
65         /* [string][in] */ LPCWSTR pszAppID);\r
66 \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
72 \r
73     HRESULT ( __stdcall *AppendCategory )(\r
74         /* [in] ICustomDestinationList*/ void *This,\r
75         /* [string][in] */ LPCWSTR pszCategory,\r
76         /* [in] IObjectArray*/ void *poa);\r
77 \r
78     HRESULT ( __stdcall *AppendKnownCategory )(\r
79         /* [in] ICustomDestinationList*/ void *This,\r
80         /* [in] KNOWNDESTCATEGORY*/ int category);\r
81 \r
82     HRESULT ( __stdcall *AddUserTasks )(\r
83         /* [in] ICustomDestinationList*/ void *This,\r
84         /* [in] IObjectArray*/ void *poa);\r
85 \r
86     HRESULT ( __stdcall *CommitList )(\r
87         /* [in] ICustomDestinationList*/ void *This);\r
88 \r
89     HRESULT ( __stdcall *GetRemovedDestinations )(\r
90         /* [in] ICustomDestinationList*/ void *This,\r
91         /* [in] */ const IID * const riid,\r
92         /* [out] */ void **ppv);\r
93 \r
94     HRESULT ( __stdcall *DeleteList )(\r
95         /* [in] ICustomDestinationList*/ void *This,\r
96         /* [string][unique][in] */ LPCWSTR pszAppID);\r
97 \r
98     HRESULT ( __stdcall *AbortList )(\r
99         /* [in] ICustomDestinationList*/ void *This);\r
100 \r
101 } ICustomDestinationListVtbl;\r
102 \r
103 typedef struct ICustomDestinationList\r
104 {\r
105     ICustomDestinationListVtbl *lpVtbl;\r
106 } ICustomDestinationList;\r
107 \r
108 typedef struct IObjectArrayVtbl\r
109 {\r
110     HRESULT ( __stdcall *QueryInterface )(\r
111         /* [in] IObjectArray*/ void *This,\r
112         /* [in] */ const GUID * const riid,\r
113         /* [out] */ void **ppvObject);\r
114 \r
115     ULONG ( __stdcall *AddRef )(\r
116         /* [in] IObjectArray*/ void *This);\r
117 \r
118     ULONG ( __stdcall *Release )(\r
119         /* [in] IObjectArray*/ void *This);\r
120 \r
121     HRESULT ( __stdcall *GetCount )(\r
122         /* [in] IObjectArray*/ void *This,\r
123         /* [out] */ UINT *pcObjects);\r
124 \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
130 \r
131 } IObjectArrayVtbl;\r
132 \r
133 typedef struct IObjectArray\r
134 {\r
135     IObjectArrayVtbl *lpVtbl;\r
136 } IObjectArray;\r
137 \r
138 typedef struct IShellLinkVtbl\r
139 {\r
140     HRESULT ( __stdcall *QueryInterface )(\r
141         /* [in] IShellLink*/ void *This,\r
142         /* [in] */ const GUID * const riid,\r
143         /* [out] */ void **ppvObject);\r
144 \r
145     ULONG ( __stdcall *AddRef )(\r
146         /* [in] IShellLink*/ void *This);\r
147 \r
148     ULONG ( __stdcall *Release )(\r
149         /* [in] IShellLink*/ void *This);\r
150 \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
157 \r
158     HRESULT ( __stdcall *GetIDList )(\r
159         /* [in] IShellLink*/ void *This,\r
160         /* [out] LPITEMIDLIST*/ void **ppidl);\r
161 \r
162     HRESULT ( __stdcall *SetIDList )(\r
163         /* [in] IShellLink*/ void *This,\r
164         /* [in] LPITEMIDLIST*/ void *pidl);\r
165 \r
166     HRESULT ( __stdcall *GetDescription )(\r
167         /* [in] IShellLink*/ void *This,\r
168         /* [string][out] */ LPSTR pszName,\r
169         /* [in] */ int cch);\r
170 \r
171     HRESULT ( __stdcall *SetDescription )(\r
172         /* [in] IShellLink*/ void *This,\r
173         /* [string][in] */ LPCSTR pszName);\r
174 \r
175     HRESULT ( __stdcall *GetWorkingDirectory )(\r
176         /* [in] IShellLink*/ void *This,\r
177         /* [string][out] */ LPSTR pszDir,\r
178         /* [in] */ int cch);\r
179 \r
180     HRESULT ( __stdcall *SetWorkingDirectory )(\r
181         /* [in] IShellLink*/ void *This,\r
182         /* [string][in] */ LPCSTR pszDir);\r
183 \r
184     HRESULT ( __stdcall *GetArguments )(\r
185         /* [in] IShellLink*/ void *This,\r
186         /* [string][out] */ LPSTR pszArgs,\r
187         /* [in] */ int cch);\r
188 \r
189     HRESULT ( __stdcall *SetArguments )(\r
190         /* [in] IShellLink*/ void *This,\r
191         /* [string][in] */ LPCSTR pszArgs);\r
192 \r
193     HRESULT ( __stdcall *GetHotkey )(\r
194         /* [in] IShellLink*/ void *This,\r
195         /* [out] */ WORD *pwHotkey);\r
196 \r
197     HRESULT ( __stdcall *SetHotkey )(\r
198         /* [in] IShellLink*/ void *This,\r
199         /* [in] */ WORD wHotkey);\r
200 \r
201     HRESULT ( __stdcall *GetShowCmd )(\r
202         /* [in] IShellLink*/ void *This,\r
203         /* [out] */ int *piShowCmd);\r
204 \r
205     HRESULT ( __stdcall *SetShowCmd )(\r
206         /* [in] IShellLink*/ void *This,\r
207         /* [in] */ int iShowCmd);\r
208 \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
214 \r
215     HRESULT ( __stdcall *SetIconLocation )(\r
216         /* [in] IShellLink*/ void *This,\r
217         /* [string][in] */ LPCSTR pszIconPath,\r
218         /* [in] */ int iIcon);\r
219 \r
220     HRESULT ( __stdcall *SetRelativePath )(\r
221         /* [in] IShellLink*/ void *This,\r
222         /* [string][in] */ LPCSTR pszPathRel,\r
223         /* [in] */ DWORD dwReserved);\r
224 \r
225     HRESULT ( __stdcall *Resolve )(\r
226         /* [in] IShellLink*/ void *This,\r
227         /* [unique][in] */ HWND hwnd,\r
228         /* [in] */ DWORD fFlags);\r
229 \r
230     HRESULT ( __stdcall *SetPath )(\r
231         /* [in] IShellLink*/ void *This,\r
232         /* [string][in] */ LPCSTR pszFile);\r
233 \r
234 } IShellLinkVtbl;\r
235 \r
236 typedef struct IShellLink\r
237 {\r
238     IShellLinkVtbl *lpVtbl;\r
239 } IShellLink;\r
240 \r
241 typedef struct IObjectCollectionVtbl\r
242 {\r
243     HRESULT ( __stdcall *QueryInterface )(\r
244         /* [in] IShellLink*/ void *This,\r
245         /* [in] */ const GUID * const riid,\r
246         /* [out] */ void **ppvObject);\r
247 \r
248     ULONG ( __stdcall *AddRef )(\r
249         /* [in] IShellLink*/ void *This);\r
250 \r
251     ULONG ( __stdcall *Release )(\r
252         /* [in] IShellLink*/ void *This);\r
253 \r
254     HRESULT ( __stdcall *GetCount )(\r
255         /* [in] IShellLink*/ void *This,\r
256         /* [out] */ UINT *pcObjects);\r
257 \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
263 \r
264     HRESULT ( __stdcall *AddObject )(\r
265         /* [in] IShellLink*/ void *This,\r
266         /* [in] */ void *punk);\r
267 \r
268     HRESULT ( __stdcall *AddFromArray )(\r
269         /* [in] IShellLink*/ void *This,\r
270         /* [in] */ IObjectArray *poaSource);\r
271 \r
272     HRESULT ( __stdcall *RemoveObjectAt )(\r
273         /* [in] IShellLink*/ void *This,\r
274         /* [in] */ UINT uiIndex);\r
275 \r
276     HRESULT ( __stdcall *Clear )(\r
277         /* [in] IShellLink*/ void *This);\r
278 \r
279 } IObjectCollectionVtbl;\r
280 \r
281 typedef struct IObjectCollection\r
282 {\r
283     IObjectCollectionVtbl *lpVtbl;\r
284 } IObjectCollection;\r
285 \r
286 typedef struct IPropertyStoreVtbl\r
287 {\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
292 \r
293     ULONG ( __stdcall *AddRef )(\r
294         /* [in] IPropertyStore*/ void *This);\r
295 \r
296     ULONG ( __stdcall *Release )(\r
297         /* [in] IPropertyStore*/ void *This);\r
298 \r
299     HRESULT ( __stdcall *GetCount )(\r
300         /* [in] IPropertyStore*/ void *This,\r
301         /* [out] */ DWORD *cProps);\r
302 \r
303     HRESULT ( __stdcall *GetAt )(\r
304         /* [in] IPropertyStore*/ void *This,\r
305         /* [in] */ DWORD iProp,\r
306         /* [out] */ PROPERTYKEY *pkey);\r
307 \r
308     HRESULT ( __stdcall *GetValue )(\r
309         /* [in] IPropertyStore*/ void *This,\r
310         /* [in] */ const PROPERTYKEY * const key,\r
311         /* [out] */ PROPVARIANT *pv);\r
312 \r
313     HRESULT ( __stdcall *SetValue )(\r
314         /* [in] IPropertyStore*/ void *This,\r
315         /* [in] */ const PROPERTYKEY * const key,\r
316         /* [in] */ REFPROPVARIANT propvar);\r
317 \r
318     HRESULT ( __stdcall *Commit )(\r
319         /* [in] IPropertyStore*/ void *This);\r
320 } IPropertyStoreVtbl;\r
321 \r
322 typedef struct IPropertyStore\r
323 {\r
324     IPropertyStoreVtbl *lpVtbl;\r
325 } IPropertyStore;\r
326 \r
327 static const CLSID CLSID_DestinationList = {\r
328     0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}\r
329 };\r
330 static const CLSID CLSID_ShellLink = {\r
331     0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}\r
332 };\r
333 static const CLSID CLSID_EnumerableObjectCollection = {\r
334     0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}\r
335 };\r
336 static const IID IID_IObjectCollection = {\r
337     0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}\r
338 };\r
339 static const IID IID_IShellLink = {\r
340     0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}\r
341 };\r
342 static const IID IID_ICustomDestinationList = {\r
343     0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}\r
344 };\r
345 static const IID IID_IObjectArray = {\r
346     0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}\r
347 };\r
348 static const IID IID_IPropertyStore = {\r
349     0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}\r
350 };\r
351 static const PROPERTYKEY PKEY_Title = {\r
352     {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},\r
353     0x00000002\r
354 };\r
355 \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
362 \r
363 static char putty_path[2048];\r
364 \r
365 /*\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
372  *\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
376  *\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
380  */\r
381 static IShellLink *make_shell_link(const char *appname,\r
382                                    const char *sessionname)\r
383 {\r
384     IShellLink *ret;\r
385     char *app_path, *param_string, *desc_string;\r
386     void *psettings_tmp;\r
387     IPropertyStore *pPS;\r
388     PROPVARIANT pv;\r
389 \r
390     /* Retrieve path to executable. */\r
391     if (!putty_path[0])\r
392         GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1);\r
393     if (appname) {\r
394         char *p, *q = putty_path;\r
395         FILE *fp;\r
396 \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
400                              appname);\r
401         if ((fp = fopen(app_path, "r")) == NULL) {\r
402             sfree(app_path);\r
403             return NULL;\r
404         }\r
405         fclose(fp);\r
406     } else {\r
407         app_path = dupstr(putty_path);\r
408     }\r
409 \r
410     /* Check if this is a valid session, otherwise don't add. */\r
411     if (sessionname) {\r
412         psettings_tmp = open_settings_r(sessionname);\r
413         if (!psettings_tmp)\r
414             return NULL;\r
415         close_settings_r(psettings_tmp);\r
416     }\r
417 \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
422         return NULL;\r
423 \r
424     /* Set path, parameters, icon and description. */\r
425     ret->lpVtbl->SetPath(ret, app_path);\r
426 \r
427     if (sessionname) {\r
428         param_string = dupcat("@", sessionname, NULL);\r
429     } else {\r
430         param_string = dupstr("");\r
431     }\r
432     ret->lpVtbl->SetArguments(ret, param_string);\r
433     sfree(param_string);\r
434 \r
435     if (sessionname) {\r
436         desc_string = dupcat("Connect to PuTTY session '",\r
437                              sessionname, "'", NULL);\r
438     } else {\r
439         assert(appname);\r
440         desc_string = dupprintf("Run %.*s", strcspn(appname, "."), appname);\r
441     }\r
442     ret->lpVtbl->SetDescription(ret, desc_string);\r
443     sfree(desc_string);\r
444 \r
445     ret->lpVtbl->SetIconLocation(ret, app_path, 0);\r
446 \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
451         pv.vt = VT_LPSTR;\r
452         if (sessionname) {\r
453             pv.pszVal = dupstr(sessionname);\r
454         } else {\r
455             assert(appname);\r
456             pv.pszVal = dupprintf("Run %.*s", strcspn(appname, "."), appname);\r
457         }\r
458         pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);\r
459         sfree(pv.pszVal);\r
460         pPS->lpVtbl->Commit(pPS);\r
461         pPS->lpVtbl->Release(pPS);\r
462     }\r
463 \r
464     sfree(app_path);\r
465 \r
466     return ret;\r
467 }\r
468 \r
469 /* Updates jumplist from registry. */\r
470 static void update_jumplist_from_registry(void)\r
471 {\r
472     const char *piterator;\r
473     UINT num_items;\r
474     int jumplist_counter;\r
475     UINT nremoved;\r
476 \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
479      * set up. */\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
487 \r
488     /*\r
489      * Create an ICustomDestinationList: the top-level object which\r
490      * deals with jump list management.\r
491      */\r
492     if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,\r
493                                     CLSCTX_INPROC_SERVER,\r
494                                     COMPTR(ICustomDestinationList, &pCDL))))\r
495         goto cleanup;\r
496 \r
497     /*\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
502      * list).\r
503      */\r
504     if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,\r
505                                            COMPTR(IObjectArray, &pRemoved))))\r
506         goto cleanup;\r
507     need_abort = TRUE;\r
508     if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))\r
509         nremoved = 0;\r
510 \r
511     /*\r
512      * Create an object collection to form the 'Recent Sessions'\r
513      * category on the jump list.\r
514      */\r
515     if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
516                                     NULL, CLSCTX_INPROC_SERVER,\r
517                                     COMPTR(IObjectCollection, &collection))))\r
518         goto cleanup;\r
519 \r
520     /*\r
521      * Go through the jump list entries from the registry and add each\r
522      * one to the collection.\r
523      */\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
530         if (link) {\r
531             UINT i;\r
532             int found;\r
533 \r
534             /*\r
535              * Check that the link isn't in the user-removed list.\r
536              */\r
537             for (i = 0, found = FALSE; i < nremoved && !found; i++) {\r
538                 IShellLink *rlink;\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
547                         found = TRUE;\r
548                     }\r
549                     rlink->lpVtbl->Release(rlink);\r
550                 }\r
551             }\r
552 \r
553             if (!found) {\r
554                 collection->lpVtbl->AddObject(collection, link);\r
555                 jumplist_counter++;\r
556             }\r
557 \r
558             link->lpVtbl->Release(link);\r
559             link = NULL;\r
560         }\r
561         piterator += strlen(piterator) + 1;\r
562     }\r
563     sfree(pjumplist_reg_entries);\r
564     pjumplist_reg_entries = NULL;\r
565 \r
566     /*\r
567      * Get the array form of the collection we've just constructed,\r
568      * and put it in the jump list.\r
569      */\r
570     if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
571                    (collection, COMPTR(IObjectArray, &array))))\r
572         goto cleanup;\r
573 \r
574     pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);\r
575 \r
576     /*\r
577      * Create an object collection to form the 'Tasks' category on the\r
578      * jump list.\r
579      */\r
580     if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
581                                     NULL, CLSCTX_INPROC_SERVER,\r
582                                     COMPTR(IObjectCollection, &collection))))\r
583         goto cleanup;\r
584 \r
585     /*\r
586      * Add task entries for PuTTYgen and Pageant.\r
587      */\r
588     piterator = "Pageant.exe\0PuTTYgen.exe\0\0";\r
589     while (*piterator != '\0') {\r
590         link = make_shell_link(piterator, NULL);\r
591         if (link) {\r
592             collection->lpVtbl->AddObject(collection, link);\r
593             link->lpVtbl->Release(link);\r
594             link = NULL;\r
595         }\r
596         piterator += strlen(piterator) + 1;\r
597     }\r
598 \r
599     /*\r
600      * Get the array form of the collection we've just constructed,\r
601      * and put it in the jump list.\r
602      */\r
603     if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
604                    (collection, COMPTR(IObjectArray, &array))))\r
605         goto cleanup;\r
606 \r
607     pCDL->lpVtbl->AddUserTasks(pCDL, array);\r
608 \r
609     /*\r
610      * Now we can clean up the array and collection variables, so as\r
611      * to be able to reuse them.\r
612      */\r
613     array->lpVtbl->Release(array);\r
614     array = NULL;\r
615     collection->lpVtbl->Release(collection);\r
616     collection = NULL;\r
617 \r
618     /*\r
619      * Create another object collection to form the user tasks\r
620      * category.\r
621      */\r
622     if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
623                                     NULL, CLSCTX_INPROC_SERVER,\r
624                                     COMPTR(IObjectCollection, &collection))))\r
625         goto cleanup;\r
626 \r
627     /*\r
628      * Get the array form of the collection we've just constructed,\r
629      * and put it in the jump list.\r
630      */\r
631     if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
632                    (collection, COMPTR(IObjectArray, &array))))\r
633         goto cleanup;\r
634 \r
635     pCDL->lpVtbl->AddUserTasks(pCDL, array);\r
636 \r
637     /*\r
638      * Now we can clean up the array and collection variables, so as\r
639      * to be able to reuse them.\r
640      */\r
641     array->lpVtbl->Release(array);\r
642     array = NULL;\r
643     collection->lpVtbl->Release(collection);\r
644     collection = NULL;\r
645 \r
646     /*\r
647      * Commit the jump list.\r
648      */\r
649     pCDL->lpVtbl->CommitList(pCDL);\r
650     need_abort = FALSE;\r
651 \r
652     /*\r
653      * Clean up.\r
654      */\r
655   cleanup:\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
663 }\r
664 \r
665 /* Clears the entire jumplist. */\r
666 void clear_jumplist(void)\r
667 {\r
668     ICustomDestinationList *pCDL;\r
669 \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
674     }\r
675 \r
676 }\r
677 \r
678 /* Adds a saved session to the Windows 7 jumplist. */\r
679 void add_session_to_jumplist(const char * const sessionname)\r
680 {\r
681     if ((osVersion.dwMajorVersion < 6) ||\r
682         (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))\r
683         return;                        /* do nothing on pre-Win7 systems */\r
684 \r
685     if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {\r
686         update_jumplist_from_registry();\r
687     } else {\r
688         /* Make sure we don't leave the jumplist dangling. */\r
689         clear_jumplist();\r
690     }\r
691 }\r
692 \r
693 /* Removes a saved session from the Windows jumplist. */\r
694 void remove_session_from_jumplist(const char * const sessionname)\r
695 {\r
696     if ((osVersion.dwMajorVersion < 6) ||\r
697         (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))\r
698         return;                        /* do nothing on pre-Win7 systems */\r
699 \r
700     if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {\r
701         update_jumplist_from_registry();\r
702     } else {\r
703         /* Make sure we don't leave the jumplist dangling. */\r
704         clear_jumplist();\r
705     }\r
706 }\r