зеркало из https://github.com/github/putty.git
717 строки
22 KiB
C
717 строки
22 KiB
C
/*
|
|
* winjump.c: support for Windows 7 jump lists.
|
|
*
|
|
* The Windows 7 jumplist is a customizable list defined by the
|
|
* application. It is persistent across application restarts: the OS
|
|
* maintains the list when the app is not running. The list is shown
|
|
* when the user right-clicks on the taskbar button of a running app
|
|
* or a pinned non-running application. We use the jumplist to
|
|
* maintain a list of recently started saved sessions, started either
|
|
* by doubleclicking on a saved session, or with the command line
|
|
* "-load" parameter.
|
|
*
|
|
* Since the jumplist is write-only: it can only be replaced and the
|
|
* current list cannot be read, we must maintain the contents of the
|
|
* list persistantly in the registry. The file winstore.h contains
|
|
* functions to directly manipulate these registry entries. This file
|
|
* contains higher level functions to manipulate the jumplist.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "putty.h"
|
|
#include "storage.h"
|
|
|
|
#define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in
|
|
* the jumplist than this, regardless of
|
|
* user preferences. */
|
|
|
|
/*
|
|
* COM structures and functions.
|
|
*/
|
|
#ifndef PROPERTYKEY_DEFINED
|
|
#define PROPERTYKEY_DEFINED
|
|
typedef struct _tagpropertykey {
|
|
GUID fmtid;
|
|
DWORD pid;
|
|
} PROPERTYKEY;
|
|
#endif
|
|
#ifndef _REFPROPVARIANT_DEFINED
|
|
#define _REFPROPVARIANT_DEFINED
|
|
typedef PROPVARIANT *REFPROPVARIANT;
|
|
#endif
|
|
/* MinGW doesn't define this yet: */
|
|
#ifndef _PROPVARIANTINIT_DEFINED_
|
|
#define _PROPVARIANTINIT_DEFINED_
|
|
#define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))
|
|
#endif
|
|
|
|
#define IID_IShellLink IID_IShellLinkA
|
|
|
|
typedef struct ICustomDestinationListVtbl {
|
|
HRESULT ( __stdcall *QueryInterface ) (
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
HRESULT ( __stdcall *SetAppID )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][in] */ LPCWSTR pszAppID);
|
|
|
|
HRESULT ( __stdcall *BeginList )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [out] */ UINT *pcMinSlots,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *AppendCategory )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][in] */ LPCWSTR pszCategory,
|
|
/* [in] IObjectArray*/ void *poa);
|
|
|
|
HRESULT ( __stdcall *AppendKnownCategory )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] KNOWNDESTCATEGORY*/ int category);
|
|
|
|
HRESULT ( __stdcall *AddUserTasks )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] IObjectArray*/ void *poa);
|
|
|
|
HRESULT ( __stdcall *CommitList )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetRemovedDestinations )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] */ const IID * const riid,
|
|
/* [out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *DeleteList )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][unique][in] */ LPCWSTR pszAppID);
|
|
|
|
HRESULT ( __stdcall *AbortList )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
} ICustomDestinationListVtbl;
|
|
|
|
typedef struct ICustomDestinationList
|
|
{
|
|
ICustomDestinationListVtbl *lpVtbl;
|
|
} ICustomDestinationList;
|
|
|
|
typedef struct IObjectArrayVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface )(
|
|
/* [in] IObjectArray*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] IObjectArray*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] IObjectArray*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetCount )(
|
|
/* [in] IObjectArray*/ void *This,
|
|
/* [out] */ UINT *pcObjects);
|
|
|
|
HRESULT ( __stdcall *GetAt )(
|
|
/* [in] IObjectArray*/ void *This,
|
|
/* [in] */ UINT uiIndex,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppv);
|
|
|
|
} IObjectArrayVtbl;
|
|
|
|
typedef struct IObjectArray
|
|
{
|
|
IObjectArrayVtbl *lpVtbl;
|
|
} IObjectArray;
|
|
|
|
typedef struct IShellLinkVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetPath )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][out] */ LPSTR pszFile,
|
|
/* [in] */ int cch,
|
|
/* [unique][out][in] */ WIN32_FIND_DATAA *pfd,
|
|
/* [in] */ DWORD fFlags);
|
|
|
|
HRESULT ( __stdcall *GetIDList )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [out] LPITEMIDLIST*/ void **ppidl);
|
|
|
|
HRESULT ( __stdcall *SetIDList )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] LPITEMIDLIST*/ void *pidl);
|
|
|
|
HRESULT ( __stdcall *GetDescription )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][out] */ LPSTR pszName,
|
|
/* [in] */ int cch);
|
|
|
|
HRESULT ( __stdcall *SetDescription )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszName);
|
|
|
|
HRESULT ( __stdcall *GetWorkingDirectory )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][out] */ LPSTR pszDir,
|
|
/* [in] */ int cch);
|
|
|
|
HRESULT ( __stdcall *SetWorkingDirectory )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszDir);
|
|
|
|
HRESULT ( __stdcall *GetArguments )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][out] */ LPSTR pszArgs,
|
|
/* [in] */ int cch);
|
|
|
|
HRESULT ( __stdcall *SetArguments )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszArgs);
|
|
|
|
HRESULT ( __stdcall *GetHotkey )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [out] */ WORD *pwHotkey);
|
|
|
|
HRESULT ( __stdcall *SetHotkey )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ WORD wHotkey);
|
|
|
|
HRESULT ( __stdcall *GetShowCmd )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [out] */ int *piShowCmd);
|
|
|
|
HRESULT ( __stdcall *SetShowCmd )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ int iShowCmd);
|
|
|
|
HRESULT ( __stdcall *GetIconLocation )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][out] */ LPSTR pszIconPath,
|
|
/* [in] */ int cch,
|
|
/* [out] */ int *piIcon);
|
|
|
|
HRESULT ( __stdcall *SetIconLocation )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszIconPath,
|
|
/* [in] */ int iIcon);
|
|
|
|
HRESULT ( __stdcall *SetRelativePath )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszPathRel,
|
|
/* [in] */ DWORD dwReserved);
|
|
|
|
HRESULT ( __stdcall *Resolve )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [unique][in] */ HWND hwnd,
|
|
/* [in] */ DWORD fFlags);
|
|
|
|
HRESULT ( __stdcall *SetPath )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [string][in] */ LPCSTR pszFile);
|
|
|
|
} IShellLinkVtbl;
|
|
|
|
typedef struct IShellLink
|
|
{
|
|
IShellLinkVtbl *lpVtbl;
|
|
} IShellLink;
|
|
|
|
typedef struct IObjectCollectionVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetCount )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [out] */ UINT *pcObjects);
|
|
|
|
HRESULT ( __stdcall *GetAt )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ UINT uiIndex,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [iid_is][out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *AddObject )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ void *punk);
|
|
|
|
HRESULT ( __stdcall *AddFromArray )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ IObjectArray *poaSource);
|
|
|
|
HRESULT ( __stdcall *RemoveObjectAt )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ UINT uiIndex);
|
|
|
|
HRESULT ( __stdcall *Clear )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
} IObjectCollectionVtbl;
|
|
|
|
typedef struct IObjectCollection
|
|
{
|
|
IObjectCollectionVtbl *lpVtbl;
|
|
} IObjectCollection;
|
|
|
|
typedef struct IPropertyStoreVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface )(
|
|
/* [in] IPropertyStore*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [iid_is][out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] IPropertyStore*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] IPropertyStore*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetCount )(
|
|
/* [in] IPropertyStore*/ void *This,
|
|
/* [out] */ DWORD *cProps);
|
|
|
|
HRESULT ( __stdcall *GetAt )(
|
|
/* [in] IPropertyStore*/ void *This,
|
|
/* [in] */ DWORD iProp,
|
|
/* [out] */ PROPERTYKEY *pkey);
|
|
|
|
HRESULT ( __stdcall *GetValue )(
|
|
/* [in] IPropertyStore*/ void *This,
|
|
/* [in] */ const PROPERTYKEY * const key,
|
|
/* [out] */ PROPVARIANT *pv);
|
|
|
|
HRESULT ( __stdcall *SetValue )(
|
|
/* [in] IPropertyStore*/ void *This,
|
|
/* [in] */ const PROPERTYKEY * const key,
|
|
/* [in] */ REFPROPVARIANT propvar);
|
|
|
|
HRESULT ( __stdcall *Commit )(
|
|
/* [in] IPropertyStore*/ void *This);
|
|
} IPropertyStoreVtbl;
|
|
|
|
typedef struct IPropertyStore
|
|
{
|
|
IPropertyStoreVtbl *lpVtbl;
|
|
} IPropertyStore;
|
|
|
|
static const CLSID CLSID_DestinationList = {
|
|
0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}
|
|
};
|
|
static const CLSID CLSID_ShellLink = {
|
|
0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
|
|
};
|
|
static const CLSID CLSID_EnumerableObjectCollection = {
|
|
0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}
|
|
};
|
|
static const IID IID_IObjectCollection = {
|
|
0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}
|
|
};
|
|
static const IID IID_IShellLink = {
|
|
0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
|
|
};
|
|
static const IID IID_ICustomDestinationList = {
|
|
0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}
|
|
};
|
|
static const IID IID_IObjectArray = {
|
|
0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}
|
|
};
|
|
static const IID IID_IPropertyStore = {
|
|
0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}
|
|
};
|
|
static const PROPERTYKEY PKEY_Title = {
|
|
{0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},
|
|
0x00000002
|
|
};
|
|
|
|
/* Type-checking macro to provide arguments for CoCreateInstance()
|
|
* etc, ensuring that 'obj' really is a 'type **'. */
|
|
#define typecheck(checkexpr, result) \
|
|
(sizeof(checkexpr) ? (result) : (result))
|
|
#define COMPTR(type, obj) &IID_##type, \
|
|
typecheck((obj)-(type **)(obj), (void **)(void *)(obj))
|
|
|
|
static char putty_path[2048];
|
|
|
|
/*
|
|
* Function to make an IShellLink describing a particular PuTTY
|
|
* command. If 'appname' is null, the command run will be the one
|
|
* returned by GetModuleFileName, i.e. our own executable; if it's
|
|
* non-null then it will be assumed to be a filename in the same
|
|
* directory as our own executable, and the return value will be NULL
|
|
* if that file doesn't exist.
|
|
*
|
|
* If 'sessionname' is null then no command line will be passed to the
|
|
* program. If it's non-null, the command line will be that text
|
|
* prefixed with an @ (to load a PuTTY saved session).
|
|
*
|
|
* Hence, you can launch a saved session using make_shell_link(NULL,
|
|
* sessionname), and launch another app using e.g.
|
|
* make_shell_link("puttygen.exe", NULL).
|
|
*/
|
|
static IShellLink *make_shell_link(const char *appname,
|
|
const char *sessionname)
|
|
{
|
|
IShellLink *ret;
|
|
char *app_path, *param_string, *desc_string;
|
|
void *psettings_tmp;
|
|
IPropertyStore *pPS;
|
|
PROPVARIANT pv;
|
|
|
|
/* Retrieve path to executable. */
|
|
if (!putty_path[0])
|
|
GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1);
|
|
if (appname) {
|
|
char *p, *q = putty_path;
|
|
FILE *fp;
|
|
|
|
if ((p = strrchr(q, '\\')) != NULL) q = p+1;
|
|
if ((p = strrchr(q, ':')) != NULL) q = p+1;
|
|
app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path,
|
|
appname);
|
|
if ((fp = fopen(app_path, "r")) == NULL) {
|
|
sfree(app_path);
|
|
return NULL;
|
|
}
|
|
fclose(fp);
|
|
} else {
|
|
app_path = dupstr(putty_path);
|
|
}
|
|
|
|
/* Check if this is a valid session, otherwise don't add. */
|
|
if (sessionname) {
|
|
psettings_tmp = open_settings_r(sessionname);
|
|
if (!psettings_tmp) {
|
|
sfree(app_path);
|
|
return NULL;
|
|
}
|
|
close_settings_r(psettings_tmp);
|
|
}
|
|
|
|
/* Create the new item. */
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
COMPTR(IShellLink, &ret)))) {
|
|
sfree(app_path);
|
|
return NULL;
|
|
}
|
|
|
|
/* Set path, parameters, icon and description. */
|
|
ret->lpVtbl->SetPath(ret, app_path);
|
|
|
|
if (sessionname) {
|
|
/* The leading space is reported to work around a Windows 10
|
|
* behaviour change in which an argument string starting with
|
|
* '@' causes the SetArguments method to silently do the wrong
|
|
* thing. */
|
|
param_string = dupcat(" @", sessionname, NULL);
|
|
} else {
|
|
param_string = dupstr("");
|
|
}
|
|
ret->lpVtbl->SetArguments(ret, param_string);
|
|
sfree(param_string);
|
|
|
|
if (sessionname) {
|
|
desc_string = dupcat("Connect to PuTTY session '",
|
|
sessionname, "'", NULL);
|
|
} else {
|
|
assert(appname);
|
|
desc_string = dupprintf("Run %.*s",
|
|
(int)strcspn(appname, "."), appname);
|
|
}
|
|
ret->lpVtbl->SetDescription(ret, desc_string);
|
|
sfree(desc_string);
|
|
|
|
ret->lpVtbl->SetIconLocation(ret, app_path, 0);
|
|
|
|
/* To set the link title, we require the property store of the link. */
|
|
if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret,
|
|
COMPTR(IPropertyStore, &pPS)))) {
|
|
PropVariantInit(&pv);
|
|
pv.vt = VT_LPSTR;
|
|
if (sessionname) {
|
|
pv.pszVal = dupstr(sessionname);
|
|
} else {
|
|
assert(appname);
|
|
pv.pszVal = dupprintf("Run %.*s",
|
|
(int)strcspn(appname, "."), appname);
|
|
}
|
|
pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);
|
|
sfree(pv.pszVal);
|
|
pPS->lpVtbl->Commit(pPS);
|
|
pPS->lpVtbl->Release(pPS);
|
|
}
|
|
|
|
sfree(app_path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Updates jumplist from registry. */
|
|
static void update_jumplist_from_registry(void)
|
|
{
|
|
const char *piterator;
|
|
UINT num_items;
|
|
int jumplist_counter;
|
|
UINT nremoved;
|
|
|
|
/* Variables used by the cleanup code must be initialised to NULL,
|
|
* so that we don't try to free or release them if they were never
|
|
* set up. */
|
|
ICustomDestinationList *pCDL = NULL;
|
|
char *pjumplist_reg_entries = NULL;
|
|
IObjectCollection *collection = NULL;
|
|
IObjectArray *array = NULL;
|
|
IShellLink *link = NULL;
|
|
IObjectArray *pRemoved = NULL;
|
|
int need_abort = FALSE;
|
|
|
|
/*
|
|
* Create an ICustomDestinationList: the top-level object which
|
|
* deals with jump list management.
|
|
*/
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
COMPTR(ICustomDestinationList, &pCDL))))
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Call its BeginList method to start compiling a list. This gives
|
|
* us back 'num_items' (a hint derived from systemwide
|
|
* configuration about how many things to put on the list) and
|
|
* 'pRemoved' (user configuration about things to leave off the
|
|
* list).
|
|
*/
|
|
if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,
|
|
COMPTR(IObjectArray, &pRemoved))))
|
|
goto cleanup;
|
|
need_abort = TRUE;
|
|
if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))
|
|
nremoved = 0;
|
|
|
|
/*
|
|
* Create an object collection to form the 'Recent Sessions'
|
|
* category on the jump list.
|
|
*/
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
|
|
NULL, CLSCTX_INPROC_SERVER,
|
|
COMPTR(IObjectCollection, &collection))))
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Go through the jump list entries from the registry and add each
|
|
* one to the collection.
|
|
*/
|
|
pjumplist_reg_entries = get_jumplist_registry_entries();
|
|
piterator = pjumplist_reg_entries;
|
|
jumplist_counter = 0;
|
|
while (*piterator != '\0' &&
|
|
(jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) {
|
|
link = make_shell_link(NULL, piterator);
|
|
if (link) {
|
|
UINT i;
|
|
int found;
|
|
|
|
/*
|
|
* Check that the link isn't in the user-removed list.
|
|
*/
|
|
for (i = 0, found = FALSE; i < nremoved && !found; i++) {
|
|
IShellLink *rlink;
|
|
if (SUCCEEDED(pRemoved->lpVtbl->GetAt
|
|
(pRemoved, i, COMPTR(IShellLink, &rlink)))) {
|
|
char desc1[2048], desc2[2048];
|
|
if (SUCCEEDED(link->lpVtbl->GetDescription
|
|
(link, desc1, sizeof(desc1)-1)) &&
|
|
SUCCEEDED(rlink->lpVtbl->GetDescription
|
|
(rlink, desc2, sizeof(desc2)-1)) &&
|
|
!strcmp(desc1, desc2)) {
|
|
found = TRUE;
|
|
}
|
|
rlink->lpVtbl->Release(rlink);
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
collection->lpVtbl->AddObject(collection, link);
|
|
jumplist_counter++;
|
|
}
|
|
|
|
link->lpVtbl->Release(link);
|
|
link = NULL;
|
|
}
|
|
piterator += strlen(piterator) + 1;
|
|
}
|
|
sfree(pjumplist_reg_entries);
|
|
pjumplist_reg_entries = NULL;
|
|
|
|
/*
|
|
* Get the array form of the collection we've just constructed,
|
|
* and put it in the jump list.
|
|
*/
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface
|
|
(collection, COMPTR(IObjectArray, &array))))
|
|
goto cleanup;
|
|
|
|
pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);
|
|
|
|
/*
|
|
* Create an object collection to form the 'Tasks' category on the
|
|
* jump list.
|
|
*/
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
|
|
NULL, CLSCTX_INPROC_SERVER,
|
|
COMPTR(IObjectCollection, &collection))))
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Add task entries for PuTTYgen and Pageant.
|
|
*/
|
|
piterator = "Pageant.exe\0PuTTYgen.exe\0\0";
|
|
while (*piterator != '\0') {
|
|
link = make_shell_link(piterator, NULL);
|
|
if (link) {
|
|
collection->lpVtbl->AddObject(collection, link);
|
|
link->lpVtbl->Release(link);
|
|
link = NULL;
|
|
}
|
|
piterator += strlen(piterator) + 1;
|
|
}
|
|
|
|
/*
|
|
* Get the array form of the collection we've just constructed,
|
|
* and put it in the jump list.
|
|
*/
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface
|
|
(collection, COMPTR(IObjectArray, &array))))
|
|
goto cleanup;
|
|
|
|
pCDL->lpVtbl->AddUserTasks(pCDL, array);
|
|
|
|
/*
|
|
* Now we can clean up the array and collection variables, so as
|
|
* to be able to reuse them.
|
|
*/
|
|
array->lpVtbl->Release(array);
|
|
array = NULL;
|
|
collection->lpVtbl->Release(collection);
|
|
collection = NULL;
|
|
|
|
/*
|
|
* Create another object collection to form the user tasks
|
|
* category.
|
|
*/
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,
|
|
NULL, CLSCTX_INPROC_SERVER,
|
|
COMPTR(IObjectCollection, &collection))))
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Get the array form of the collection we've just constructed,
|
|
* and put it in the jump list.
|
|
*/
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface
|
|
(collection, COMPTR(IObjectArray, &array))))
|
|
goto cleanup;
|
|
|
|
pCDL->lpVtbl->AddUserTasks(pCDL, array);
|
|
|
|
/*
|
|
* Now we can clean up the array and collection variables, so as
|
|
* to be able to reuse them.
|
|
*/
|
|
array->lpVtbl->Release(array);
|
|
array = NULL;
|
|
collection->lpVtbl->Release(collection);
|
|
collection = NULL;
|
|
|
|
/*
|
|
* Commit the jump list.
|
|
*/
|
|
pCDL->lpVtbl->CommitList(pCDL);
|
|
need_abort = FALSE;
|
|
|
|
/*
|
|
* Clean up.
|
|
*/
|
|
cleanup:
|
|
if (pRemoved) pRemoved->lpVtbl->Release(pRemoved);
|
|
if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL);
|
|
if (pCDL) pCDL->lpVtbl->Release(pCDL);
|
|
if (collection) collection->lpVtbl->Release(collection);
|
|
if (array) array->lpVtbl->Release(array);
|
|
if (link) link->lpVtbl->Release(link);
|
|
sfree(pjumplist_reg_entries);
|
|
}
|
|
|
|
/* Clears the entire jumplist. */
|
|
void clear_jumplist(void)
|
|
{
|
|
ICustomDestinationList *pCDL;
|
|
|
|
if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER,
|
|
COMPTR(ICustomDestinationList, &pCDL)) == S_OK) {
|
|
pCDL->lpVtbl->DeleteList(pCDL, NULL);
|
|
pCDL->lpVtbl->Release(pCDL);
|
|
}
|
|
|
|
}
|
|
|
|
/* Adds a saved session to the Windows 7 jumplist. */
|
|
void add_session_to_jumplist(const char * const sessionname)
|
|
{
|
|
if ((osVersion.dwMajorVersion < 6) ||
|
|
(osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
|
|
return; /* do nothing on pre-Win7 systems */
|
|
|
|
if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
|
|
update_jumplist_from_registry();
|
|
} else {
|
|
/* Make sure we don't leave the jumplist dangling. */
|
|
clear_jumplist();
|
|
}
|
|
}
|
|
|
|
/* Removes a saved session from the Windows jumplist. */
|
|
void remove_session_from_jumplist(const char * const sessionname)
|
|
{
|
|
if ((osVersion.dwMajorVersion < 6) ||
|
|
(osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
|
|
return; /* do nothing on pre-Win7 systems */
|
|
|
|
if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
|
|
update_jumplist_from_registry();
|
|
} else {
|
|
/* Make sure we don't leave the jumplist dangling. */
|
|
clear_jumplist();
|
|
}
|
|
}
|