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