2010-12-23 17:32:28 +00:00
|
|
|
/*
|
2022-01-22 15:38:53 +00:00
|
|
|
* jump-list.c: support for Windows 7 jump lists.
|
2010-12-23 17:32:28 +00:00
|
|
|
*
|
|
|
|
* 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
|
2022-01-03 06:38:07 +00:00
|
|
|
* list persistently in the registry. The file winstore.h contains
|
2010-12-23 17:32:28 +00:00
|
|
|
* 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
|
2010-12-26 18:29:53 +00:00
|
|
|
/* MinGW doesn't define this yet: */
|
2022-05-08 10:06:14 +00:00
|
|
|
#if !defined _PROPVARIANTINIT_DEFINED_ && !defined _PROPVARIANT_INIT_DEFINED_
|
2010-12-26 18:29:53 +00:00
|
|
|
#define _PROPVARIANTINIT_DEFINED_
|
|
|
|
#define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))
|
|
|
|
#endif
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
#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
|
|
|
|
};
|
|
|
|
|
2013-07-22 07:12:15 +00:00
|
|
|
/* Type-checking macro to provide arguments for CoCreateInstance()
|
|
|
|
* etc, ensuring that 'obj' really is a 'type **'. */
|
|
|
|
#define typecheck(checkexpr, result) \
|
|
|
|
(sizeof(checkexpr) ? (result) : (result))
|
2010-12-26 18:29:53 +00:00
|
|
|
#define COMPTR(type, obj) &IID_##type, \
|
2013-07-22 07:12:15 +00:00
|
|
|
typecheck((obj)-(type **)(obj), (void **)(void *)(obj))
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
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) {
|
2018-09-14 07:45:42 +00:00
|
|
|
settings_r *psettings_tmp = open_settings_r(sessionname);
|
2013-07-22 07:11:54 +00:00
|
|
|
if (!psettings_tmp) {
|
|
|
|
sfree(app_path);
|
2010-12-23 17:32:28 +00:00
|
|
|
return NULL;
|
2013-07-22 07:11:54 +00:00
|
|
|
}
|
2010-12-23 17:32:28 +00:00
|
|
|
close_settings_r(psettings_tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the new item. */
|
|
|
|
if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,
|
|
|
|
CLSCTX_INPROC_SERVER,
|
2013-07-22 07:11:54 +00:00
|
|
|
COMPTR(IShellLink, &ret)))) {
|
|
|
|
sfree(app_path);
|
2010-12-23 17:32:28 +00:00
|
|
|
return NULL;
|
2013-07-22 07:11:54 +00:00
|
|
|
}
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
/* Set path, parameters, icon and description. */
|
|
|
|
ret->lpVtbl->SetPath(ret, app_path);
|
|
|
|
|
|
|
|
if (sessionname) {
|
2015-08-06 18:25:56 +00:00
|
|
|
/* 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. */
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
param_string = dupcat(" @", sessionname);
|
2010-12-23 17:32:28 +00:00
|
|
|
} else {
|
|
|
|
param_string = dupstr("");
|
|
|
|
}
|
|
|
|
ret->lpVtbl->SetArguments(ret, param_string);
|
|
|
|
sfree(param_string);
|
|
|
|
|
|
|
|
if (sessionname) {
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
desc_string = dupcat("Connect to PuTTY session '", sessionname, "'");
|
2010-12-23 17:32:28 +00:00
|
|
|
} else {
|
|
|
|
assert(appname);
|
2016-04-02 13:12:12 +00:00
|
|
|
desc_string = dupprintf("Run %.*s",
|
|
|
|
(int)strcspn(appname, "."), appname);
|
2010-12-23 17:32:28 +00:00
|
|
|
}
|
|
|
|
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);
|
2016-04-02 13:12:12 +00:00
|
|
|
pv.pszVal = dupprintf("Run %.*s",
|
|
|
|
(int)strcspn(appname, "."), appname);
|
2010-12-23 17:32:28 +00:00
|
|
|
}
|
|
|
|
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;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool need_abort = false;
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2018-10-29 19:50:29 +00:00
|
|
|
need_abort = true;
|
2010-12-23 17:32:28 +00:00
|
|
|
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;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool found;
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the link isn't in the user-removed list.
|
|
|
|
*/
|
2018-10-29 19:50:29 +00:00
|
|
|
for (i = 0, found = false; i < nremoved && !found; i++) {
|
2010-12-23 17:32:28 +00:00
|
|
|
IShellLink *rlink;
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
if (SUCCEEDED(pRemoved->lpVtbl->GetAt(
|
|
|
|
pRemoved, i, COMPTR(IShellLink, &rlink)))) {
|
2010-12-23 17:32:28 +00:00
|
|
|
char desc1[2048], desc2[2048];
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
if (SUCCEEDED(link->lpVtbl->GetDescription(
|
|
|
|
link, desc1, sizeof(desc1)-1)) &&
|
|
|
|
SUCCEEDED(rlink->lpVtbl->GetDescription(
|
|
|
|
rlink, desc2, sizeof(desc2)-1)) &&
|
2010-12-23 17:32:28 +00:00
|
|
|
!strcmp(desc1, desc2)) {
|
2018-10-29 19:50:29 +00:00
|
|
|
found = true;
|
2010-12-23 17:32:28 +00:00
|
|
|
}
|
|
|
|
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.
|
|
|
|
*/
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
|
|
|
|
collection, COMPTR(IObjectArray, &array))))
|
2010-12-23 17:32:28 +00:00
|
|
|
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.
|
|
|
|
*/
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
|
|
|
|
collection, COMPTR(IObjectArray, &array))))
|
2010-12-23 17:32:28 +00:00
|
|
|
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.
|
|
|
|
*/
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
|
|
|
|
collection, COMPTR(IObjectArray, &array))))
|
2010-12-23 17:32:28 +00:00
|
|
|
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);
|
2018-10-29 19:50:29 +00:00
|
|
|
need_abort = false;
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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. */
|
2010-12-26 20:00:45 +00:00
|
|
|
void clear_jumplist(void)
|
2010-12-23 17:32:28 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2018-06-03 14:05:44 +00:00
|
|
|
if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
|
2010-12-23 17:32:28 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-06-03 14:05:44 +00:00
|
|
|
if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
|
2010-12-23 17:32:28 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
2016-08-29 15:55:42 +00:00
|
|
|
|
|
|
|
/* Set Explicit App User Model Id to fix removable media error with
|
|
|
|
jump lists */
|
|
|
|
|
2018-11-03 08:28:35 +00:00
|
|
|
bool set_explicit_app_user_model_id(void)
|
2016-08-29 15:55:42 +00:00
|
|
|
{
|
2022-08-03 19:48:46 +00:00
|
|
|
DECL_WINDOWS_FUNCTION(
|
|
|
|
static, HRESULT, SetCurrentProcessExplicitAppUserModelID, (PCWSTR));
|
2016-08-29 15:55:42 +00:00
|
|
|
|
2022-08-03 19:48:46 +00:00
|
|
|
static HMODULE shell32_module = 0;
|
2016-08-29 15:55:42 +00:00
|
|
|
|
2022-08-03 19:48:46 +00:00
|
|
|
if (!shell32_module) {
|
2016-08-29 15:55:42 +00:00
|
|
|
shell32_module = load_system32_dll("Shell32.dll");
|
Add automatic type-checking to GET_WINDOWS_FUNCTION.
This gives me an extra safety-check against having mistyped one of the
function prototypes that we load at run time from DLLs: we verify that
the typedef we defined based on the prototype in our source code
matches the type of the real function as declared in the Windows
headers.
This was an idea I had while adding a pile of further functions using
this mechanism. It didn't catch any errors (either in the new
functions or in the existing collection), but that's no reason not to
keep it anyway now that I've thought of it!
In VS2015, this automated type-check works for most functions, but a
couple manage to break it. SetCurrentProcessExplicitAppUserModelID in
winjump.c can't be type-checked, because including <shobjidl.h> where
that function is declared would also bring in a load of other stuff
that conflicts with the painful manual COM declarations in winjump.c.
(That stuff could probably be removed now we're on an up-to-date
Visual Studio, on the other hand, but that's a separate chore.) And
gai_strerror, used in winnet.c, does _have_ an implementation in a
DLL, but the header files like to provide an inline version with a
different calling convention, which defeats this error-checking trick.
And in the older VS2003 that we still precautionarily build with,
several more type-checks have to be #ifdeffed out because the
functions they check against just aren't there at all.
2017-04-11 17:56:55 +00:00
|
|
|
/*
|
|
|
|
* We can't typecheck this function here, because it's defined
|
|
|
|
* in <shobjidl.h>, which we're not including due to clashes
|
|
|
|
* with all the manual-COM machinery above.
|
|
|
|
*/
|
|
|
|
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
|
|
|
|
shell32_module, SetCurrentProcessExplicitAppUserModelID);
|
2016-08-29 15:55:42 +00:00
|
|
|
}
|
|
|
|
|
2022-08-03 19:48:46 +00:00
|
|
|
if (p_SetCurrentProcessExplicitAppUserModelID) {
|
2021-05-08 16:20:50 +00:00
|
|
|
const wchar_t *id = get_app_user_model_id();
|
2022-08-03 19:48:46 +00:00
|
|
|
if (p_SetCurrentProcessExplicitAppUserModelID(id) == S_OK) {
|
2022-08-03 19:48:46 +00:00
|
|
|
return true;
|
2016-08-29 15:55:42 +00:00
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2016-08-29 15:55:42 +00:00
|
|
|
}
|
|
|
|
/* Function doesn't exist, which is ok for Pre-7 systems */
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2016-08-29 15:55:42 +00:00
|
|
|
|
|
|
|
}
|