]>
Commit | Line | Data |
---|---|---|
1c1af145 | 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 | |
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 | |
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 | ||
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)))) | |
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. */ | |
666 | void clear_jumplist(void) | |
667 | { | |
668 | ICustomDestinationList *pCDL; | |
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 | } |