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 | |
735a4294 |
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)))) |
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); |
413 | if (!psettings_tmp) |
414 | return NULL; |
415 | close_settings_r(psettings_tmp); |
416 | } |
417 | |
418 | /* Create the new item. */ |
419 | if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, |
420 | CLSCTX_INPROC_SERVER, |
421 | COMPTR(IShellLink, &ret)))) |
422 | return NULL; |
423 | |
424 | /* Set path, parameters, icon and description. */ |
425 | ret->lpVtbl->SetPath(ret, app_path); |
426 | |
427 | if (sessionname) { |
428 | param_string = dupcat("@", sessionname, NULL); |
429 | } else { |
430 | param_string = dupstr(""); |
431 | } |
432 | ret->lpVtbl->SetArguments(ret, param_string); |
433 | sfree(param_string); |
434 | |
435 | if (sessionname) { |
436 | desc_string = dupcat("Connect to PuTTY session '", |
437 | sessionname, "'", NULL); |
438 | } else { |
439 | assert(appname); |
440 | desc_string = dupprintf("Run %.*s", strcspn(appname, "."), appname); |
441 | } |
442 | ret->lpVtbl->SetDescription(ret, desc_string); |
443 | sfree(desc_string); |
444 | |
445 | ret->lpVtbl->SetIconLocation(ret, app_path, 0); |
446 | |
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); |
451 | pv.vt = VT_LPSTR; |
452 | if (sessionname) { |
453 | pv.pszVal = dupstr(sessionname); |
454 | } else { |
455 | assert(appname); |
456 | pv.pszVal = dupprintf("Run %.*s", strcspn(appname, "."), appname); |
457 | } |
458 | pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv); |
459 | sfree(pv.pszVal); |
460 | pPS->lpVtbl->Commit(pPS); |
461 | pPS->lpVtbl->Release(pPS); |
462 | } |
463 | |
464 | sfree(app_path); |
465 | |
466 | return ret; |
467 | } |
468 | |
469 | /* Updates jumplist from registry. */ |
470 | static void update_jumplist_from_registry(void) |
471 | { |
472 | const char *piterator; |
473 | UINT num_items; |
474 | int jumplist_counter; |
475 | UINT nremoved; |
476 | |
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 |
479 | * set up. */ |
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; |
487 | |
488 | /* |
489 | * Create an ICustomDestinationList: the top-level object which |
490 | * deals with jump list management. |
491 | */ |
492 | if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL, |
493 | CLSCTX_INPROC_SERVER, |
494 | COMPTR(ICustomDestinationList, &pCDL)))) |
495 | goto cleanup; |
496 | |
497 | /* |
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 |
502 | * list). |
503 | */ |
504 | if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items, |
505 | COMPTR(IObjectArray, &pRemoved)))) |
506 | goto cleanup; |
507 | need_abort = TRUE; |
508 | if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved))) |
509 | nremoved = 0; |
510 | |
511 | /* |
512 | * Create an object collection to form the 'Recent Sessions' |
513 | * category on the jump list. |
514 | */ |
515 | if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, |
516 | NULL, CLSCTX_INPROC_SERVER, |
517 | COMPTR(IObjectCollection, &collection)))) |
518 | goto cleanup; |
519 | |
520 | /* |
521 | * Go through the jump list entries from the registry and add each |
522 | * one to the collection. |
523 | */ |
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); |
530 | if (link) { |
531 | UINT i; |
532 | int found; |
533 | |
534 | /* |
535 | * Check that the link isn't in the user-removed list. |
536 | */ |
537 | for (i = 0, found = FALSE; i < nremoved && !found; i++) { |
538 | IShellLink *rlink; |
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)) { |
547 | found = TRUE; |
548 | } |
549 | rlink->lpVtbl->Release(rlink); |
550 | } |
551 | } |
552 | |
553 | if (!found) { |
554 | collection->lpVtbl->AddObject(collection, link); |
555 | jumplist_counter++; |
556 | } |
557 | |
558 | link->lpVtbl->Release(link); |
559 | link = NULL; |
560 | } |
561 | piterator += strlen(piterator) + 1; |
562 | } |
563 | sfree(pjumplist_reg_entries); |
564 | pjumplist_reg_entries = NULL; |
565 | |
566 | /* |
567 | * Get the array form of the collection we've just constructed, |
568 | * and put it in the jump list. |
569 | */ |
570 | if (!SUCCEEDED(collection->lpVtbl->QueryInterface |
571 | (collection, COMPTR(IObjectArray, &array)))) |
572 | goto cleanup; |
573 | |
574 | pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array); |
575 | |
576 | /* |
577 | * Create an object collection to form the 'Tasks' category on the |
578 | * jump list. |
579 | */ |
580 | if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, |
581 | NULL, CLSCTX_INPROC_SERVER, |
582 | COMPTR(IObjectCollection, &collection)))) |
583 | goto cleanup; |
584 | |
585 | /* |
586 | * Add task entries for PuTTYgen and Pageant. |
587 | */ |
588 | piterator = "Pageant.exe\0PuTTYgen.exe\0\0"; |
589 | while (*piterator != '\0') { |
590 | link = make_shell_link(piterator, NULL); |
591 | if (link) { |
592 | collection->lpVtbl->AddObject(collection, link); |
593 | link->lpVtbl->Release(link); |
594 | link = NULL; |
595 | } |
596 | piterator += strlen(piterator) + 1; |
597 | } |
598 | |
599 | /* |
600 | * Get the array form of the collection we've just constructed, |
601 | * and put it in the jump list. |
602 | */ |
603 | if (!SUCCEEDED(collection->lpVtbl->QueryInterface |
604 | (collection, COMPTR(IObjectArray, &array)))) |
605 | goto cleanup; |
606 | |
607 | pCDL->lpVtbl->AddUserTasks(pCDL, array); |
608 | |
609 | /* |
610 | * Now we can clean up the array and collection variables, so as |
611 | * to be able to reuse them. |
612 | */ |
613 | array->lpVtbl->Release(array); |
614 | array = NULL; |
615 | collection->lpVtbl->Release(collection); |
616 | collection = NULL; |
617 | |
618 | /* |
619 | * Create another object collection to form the user tasks |
620 | * category. |
621 | */ |
622 | if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, |
623 | NULL, CLSCTX_INPROC_SERVER, |
624 | COMPTR(IObjectCollection, &collection)))) |
625 | goto cleanup; |
626 | |
627 | /* |
628 | * Get the array form of the collection we've just constructed, |
629 | * and put it in the jump list. |
630 | */ |
631 | if (!SUCCEEDED(collection->lpVtbl->QueryInterface |
632 | (collection, COMPTR(IObjectArray, &array)))) |
633 | goto cleanup; |
634 | |
635 | pCDL->lpVtbl->AddUserTasks(pCDL, array); |
636 | |
637 | /* |
638 | * Now we can clean up the array and collection variables, so as |
639 | * to be able to reuse them. |
640 | */ |
641 | array->lpVtbl->Release(array); |
642 | array = NULL; |
643 | collection->lpVtbl->Release(collection); |
644 | collection = NULL; |
645 | |
646 | /* |
647 | * Commit the jump list. |
648 | */ |
649 | pCDL->lpVtbl->CommitList(pCDL); |
650 | need_abort = FALSE; |
651 | |
652 | /* |
653 | * Clean up. |
654 | */ |
655 | cleanup: |
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); |
663 | } |
664 | |
665 | /* Clears the entire jumplist. */ |
21cdf8c2 |
666 | void clear_jumplist(void) |
073e9f42 |
667 | { |
668 | ICustomDestinationList *pCDL; |
073e9f42 |
669 | |
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); |
674 | } |
675 | |
676 | } |
677 | |
678 | /* Adds a saved session to the Windows 7 jumplist. */ |
679 | void add_session_to_jumplist(const char * const sessionname) |
680 | { |
681 | if ((osVersion.dwMajorVersion < 6) || |
682 | (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1)) |
683 | return; /* do nothing on pre-Win7 systems */ |
684 | |
685 | if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) { |
686 | update_jumplist_from_registry(); |
687 | } else { |
688 | /* Make sure we don't leave the jumplist dangling. */ |
689 | clear_jumplist(); |
690 | } |
691 | } |
692 | |
693 | /* Removes a saved session from the Windows jumplist. */ |
694 | void remove_session_from_jumplist(const char * const sessionname) |
695 | { |
696 | if ((osVersion.dwMajorVersion < 6) || |
697 | (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1)) |
698 | return; /* do nothing on pre-Win7 systems */ |
699 | |
700 | if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) { |
701 | update_jumplist_from_registry(); |
702 | } else { |
703 | /* Make sure we don't leave the jumplist dangling. */ |
704 | clear_jumplist(); |
705 | } |
706 | } |