#include <assert.h>
#include <tchar.h>
+#define PUTTY_DO_GLOBALS
+
#include "putty.h"
#include "ssh.h"
#include "misc.h"
#define IDI_MAINICON 200
#define IDI_TRAYICON 201
-#define WM_XUSER (WM_USER + 0x2000)
-#define WM_SYSTRAY (WM_XUSER + 6)
-#define WM_SYSTRAY2 (WM_XUSER + 7)
+#define WM_SYSTRAY (WM_APP + 6)
+#define WM_SYSTRAY2 (WM_APP + 7)
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
extern char ver[];
-static HINSTANCE instance;
-static HWND main_hwnd;
static HWND keylist;
static HWND aboutbox;
static HMENU systray_menu, session_menu;
static int already_running;
-static int requested_help;
-char *help_path;
static char *putty_path;
+/* CWD for "add key" file requester. */
+static filereq *keypath = NULL;
+
#define IDM_PUTTY 0x0060
#define IDM_SESSIONS_BASE 0x1000
#define IDM_SESSIONS_MAX 0x2000
va_start(ap, fmt);
buf = dupvprintf(fmt, ap);
va_end(ap);
- MessageBox(main_hwnd, buf, "Pageant Fatal Error",
+ MessageBox(hwnd, buf, "Pageant Fatal Error",
MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
sfree(buf);
exit(1);
static int has_security;
#ifndef NO_SECURITY
-typedef DWORD(WINAPI * gsi_fn_t)
- (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
- PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
-static gsi_fn_t getsecurityinfo;
+DECL_WINDOWS_FUNCTION(static, DWORD, GetSecurityInfo,
+ (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
+ PSID *, PSID *, PACL *, PACL *,
+ PSECURITY_DESCRIPTOR *));
#endif
/*
*/
int random_byte(void)
{
- MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
+ MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
exit(0);
/* this line can't be reached but it placates MSVC's warnings :-) */
return 0;
}
/*
- * Blob structure for passing to the asymmetric SSH2 key compare
+ * Blob structure for passing to the asymmetric SSH-2 key compare
* function, prototyped here.
*/
struct blob {
};
static int cmpkeys_ssh2_asymm(void *av, void *bv);
-#define GET_32BIT(cp) \
- (((unsigned long)(unsigned char)(cp)[0] << 24) | \
- ((unsigned long)(unsigned char)(cp)[1] << 16) | \
- ((unsigned long)(unsigned char)(cp)[2] << 8) | \
- ((unsigned long)(unsigned char)(cp)[3]))
-
-#define PUT_32BIT(cp, value) { \
- (cp)[0] = (unsigned char)((value) >> 24); \
- (cp)[1] = (unsigned char)((value) >> 16); \
- (cp)[2] = (unsigned char)((value) >> 8); \
- (cp)[3] = (unsigned char)(value); }
-
#define PASSPHRASE_MAXLEN 512
struct PassphraseProcStruct {
return 0;
case 101:
EnableWindow(hwnd, 0);
- DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
+ DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
EnableWindow(hwnd, 1);
SetActiveWindow(hwnd);
return 0;
{
static const char mbtitle[] = "PuTTY Key File Warning";
static const char message[] =
- "You are loading an SSH 2 private key which has an\n"
+ "You are loading an SSH-2 private key which has an\n"
"old version of the file format. This means your key\n"
"file is not fully tamperproof. Future versions of\n"
"PuTTY may stop supporting this private key format,\n"
int ret;
int attempts;
char *comment;
+ const char *error = NULL;
struct PassphraseProcStruct pps;
int type;
int original_pass;
type = key_type(&filename);
if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
- char msg[256];
- sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
- MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
+ char *msg = dupprintf("Couldn't load this key (%s)",
+ key_type_to_str(type));
+ message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
return;
}
int i, nkeys, bloblen, keylistlen;
if (type == SSH_KEYTYPE_SSH1) {
- if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
- MessageBox(NULL, "Couldn't load private key.", APPNAME,
- MB_OK | MB_ICONERROR);
+ if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL, &error)) {
+ char *msg = dupprintf("Couldn't load private key (%s)", error);
+ message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
return;
}
keylist = get_keylist1(&keylistlen);
} else {
unsigned char *blob2;
- blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
+ blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen,
+ NULL, &error);
if (!blob) {
- MessageBox(NULL, "Couldn't load private key.", APPNAME,
- MB_OK | MB_ICONERROR);
+ char *msg = dupprintf("Couldn't load private key (%s)", error);
+ message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
return;
}
/* For our purposes we want the blob prefixed with its length */
sfree(blob);
}
+ error = NULL;
if (type == SSH_KEYTYPE_SSH1)
needs_pass = rsakey_encrypted(&filename, &comment);
else
} else {
int dlgret;
original_pass = 1;
- dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
- NULL, PassphraseProc, (LPARAM) & pps);
+ dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
+ NULL, PassphraseProc, (LPARAM) &pps);
passphrase_box = NULL;
if (!dlgret) {
if (comment)
} else
*passphrase = '\0';
if (type == SSH_KEYTYPE_SSH1)
- ret = loadrsakey(&filename, rkey, passphrase, NULL);
+ ret = loadrsakey(&filename, rkey, passphrase, &error);
else {
- skey = ssh2_load_userkey(&filename, passphrase, NULL);
+ skey = ssh2_load_userkey(&filename, passphrase, &error);
if (skey == SSH2_WRONG_PASSPHRASE)
ret = -1;
else if (!skey)
if (comment)
sfree(comment);
if (ret == 0) {
- MessageBox(NULL, "Couldn't load private key.", APPNAME,
- MB_OK | MB_ICONERROR);
+ char *msg = dupprintf("Couldn't load private key (%s)", error);
+ message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
if (type == SSH_KEYTYPE_SSH1)
sfree(rkey);
return;
}
/*
- * Create an SSH1 key list in a malloc'ed buffer; return its
+ * Create an SSH-1 key list in a malloc'ed buffer; return its
* length.
*/
static void *make_keylist1(int *length)
}
/*
- * Create an SSH2 key list in a malloc'ed buffer; return its
+ * Create an SSH-2 key list in a malloc'ed buffer; return its
* length.
*/
static void *make_keylist2(int *length)
break;
case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
/*
- * Remove all SSH1 keys. Always returns success.
+ * Remove all SSH-1 keys. Always returns success.
*/
{
struct RSAKey *rkey;
break;
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
/*
- * Remove all SSH2 keys. Always returns success.
+ * Remove all SSH-2 keys. Always returns success.
*/
{
struct ssh2_userkey *skey;
}
/*
- * Key comparison function for the 2-3-4 tree of SSH2 keys.
+ * Key comparison function for the 2-3-4 tree of SSH-2 keys.
*/
static int cmpkeys_ssh2(void *av, void *bv)
{
/*
* Key comparison function for looking up a blob in the 2-3-4 tree
- * of SSH2 keys.
+ * of SSH-2 keys.
*/
static int cmpkeys_ssh2_asymm(void *av, void *bv)
{
static void prompt_add_keyfile(void)
{
OPENFILENAME of;
- char filename[FILENAME_MAX];
char *filelist = snewn(8192, char);
- char *filewalker;
- int n, dirlen;
+ if (!keypath) keypath = filereq_new();
memset(&of, 0, sizeof(of));
-#ifdef OPENFILENAME_SIZE_VERSION_400
- of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
-#else
- of.lStructSize = sizeof(of);
-#endif
- of.hwndOwner = main_hwnd;
- of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
- "All Files (*.*)\0*\0\0\0";
+ of.hwndOwner = hwnd;
+ of.lpstrFilter = FILTER_KEY_FILES;
of.lpstrCustomFilter = NULL;
of.nFilterIndex = 1;
of.lpstrFile = filelist;
*filelist = '\0';
- of.nMaxFile = FILENAME_MAX;
+ of.nMaxFile = 8192;
of.lpstrFileTitle = NULL;
- of.lpstrInitialDir = NULL;
of.lpstrTitle = "Select Private Key File";
of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
- if (GetOpenFileName(&of)) {
+ if (request_file(keypath, &of, TRUE, FALSE)) {
if(strlen(filelist) > of.nFileOffset)
/* Only one filename returned? */
add_keyfile(filename_from_str(filelist));
* rest the filenames. terminated with an
* empty string.
*/
- filewalker = filelist;
- dirlen = strlen(filewalker);
- if(dirlen > FILENAME_MAX - 8) return;
- memcpy(filename, filewalker, dirlen);
-
- filewalker += dirlen + 1;
- filename[dirlen++] = '\\';
-
- /* then go over names one by one */
- for(;;) {
- n = strlen(filewalker) + 1;
- /* end of the list */
- if(n == 1)
- break;
- /* too big, shouldn't happen */
- if(n + dirlen > FILENAME_MAX)
- break;
-
- memcpy(filename + dirlen, filewalker, n);
- filewalker += n;
-
+ char *dir = filelist;
+ char *filewalker = filelist + strlen(dir) + 1;
+ while (*filewalker != '\0') {
+ char *filename = dupcat(dir, "\\", filewalker, NULL);
add_keyfile(filename_from_str(filename));
+ sfree(filename);
+ filewalker += strlen(filewalker) + 1;
}
}
rd.right - rd.left, rd.bottom - rd.top, TRUE);
}
- if (help_path)
- SetWindowLong(hwnd, GWL_EXSTYLE,
- GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
+ if (has_help())
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE,
+ GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
+ WS_EX_CONTEXTHELP);
else {
HWND item = GetDlgItem(hwnd, 103); /* the Help button */
if (item)
DestroyWindow(item);
}
- requested_help = FALSE;
keylist = hwnd;
{
case 103: /* help */
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
- if (help_path) {
- WinHelp(main_hwnd, help_path, HELP_COMMAND,
- (DWORD)"JI(`',`pageant.general')");
- requested_help = TRUE;
- }
+ launch_help(hwnd, WINHELP_CTX_pageant_general);
}
return 0;
}
return 0;
case WM_HELP:
- if (help_path) {
+ {
int id = ((LPHELPINFO)lParam)->iCtrlId;
- char *cmd = NULL;
+ char *topic = NULL;
switch (id) {
- case 100: cmd = "JI(`',`pageant.keylist')"; break;
- case 101: cmd = "JI(`',`pageant.addkey')"; break;
- case 102: cmd = "JI(`',`pageant.remkey')"; break;
+ case 100: topic = WINHELP_CTX_pageant_keylist; break;
+ case 101: topic = WINHELP_CTX_pageant_addkey; break;
+ case 102: topic = WINHELP_CTX_pageant_remkey; break;
}
- if (cmd) {
- WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
- requested_help = TRUE;
+ if (topic) {
+ launch_help(hwnd, topic);
} else {
MessageBeep(0);
}
tnid.uID = 1; /* unique within this systray use */
tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnid.uCallbackMessage = WM_SYSTRAY;
- tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
+ tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
res = Shell_NotifyIcon(NIM_ADD, &tnid);
GetCursorPos(&cursorpos);
PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
} else if (lParam == WM_LBUTTONDBLCLK) {
- /* Equivalent to IDM_VIEWKEYS. */
- PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
+ /* Run the default menu item. */
+ UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
+ if (menuitem != -1)
+ PostMessage(hwnd, WM_COMMAND, menuitem, 0);
}
break;
case WM_SYSTRAY2:
break;
case IDM_VIEWKEYS:
if (!keylist) {
- keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
+ keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
NULL, KeyListProc);
ShowWindow(keylist, SW_SHOWNORMAL);
}
break;
case IDM_ABOUT:
if (!aboutbox) {
- aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
+ aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
NULL, AboutProc);
ShowWindow(aboutbox, SW_SHOWNORMAL);
/*
}
break;
case IDM_HELP:
- if (help_path) {
- WinHelp(main_hwnd, help_path, HELP_COMMAND,
- (DWORD)"JI(`',`pageant.general')");
- requested_help = TRUE;
- }
+ launch_help(hwnd, WINHELP_CTX_pageant_general);
break;
default:
{
}
break;
case WM_DESTROY:
- if (requested_help) {
- WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
- requested_help = FALSE;
- }
+ quit_help(hwnd);
PostQuitMessage(0);
return 0;
case WM_COPYDATA:
#endif
return 0;
}
- if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
- OWNER_SECURITY_INFORMATION,
- &procowner, NULL, NULL, NULL,
- &psd2) != ERROR_SUCCESS) {
+ if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT,
+ OWNER_SECURITY_INFORMATION,
+ &procowner, NULL, NULL, NULL,
+ &psd2) != ERROR_SUCCESS) {
#ifdef DEBUG_IPC
debug(("couldn't get owner info for process\n"));
#endif
return 0; /* unable to get security info */
}
CloseHandle(proc);
- if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
- OWNER_SECURITY_INFORMATION,
- &mapowner, NULL, NULL, NULL,
- &psd1) != ERROR_SUCCESS)) {
+ if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
+ OWNER_SECURITY_INFORMATION,
+ &mapowner, NULL, NULL, NULL,
+ &psd1) != ERROR_SUCCESS)) {
#ifdef DEBUG_IPC
debug(
("couldn't get owner info for filemap: %d\n",
assert(!"We shouldn't get here");
}
-void cleanup_exit(int code) { exit(code); }
+void cleanup_exit(int code)
+{
+ shutdown_help();
+ exit(code);
+}
int flags = FLAG_SYNCAGENT;
int argc, i;
char **argv, **argstart;
+ hinst = inst;
+ hwnd = NULL;
+
/*
* Determine whether we're an NT system (should have security
* APIs) or a non-NT system (don't do security).
* Attempt to get the security API we need.
*/
advapi = LoadLibrary("ADVAPI32.DLL");
- getsecurityinfo =
- (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
- if (!getsecurityinfo) {
+ GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo);
+ if (!p_GetSecurityInfo) {
MessageBox(NULL,
"Unable to access security APIs. Pageant will\n"
"not run, in case it causes a security breach.",
} else
advapi = NULL;
- instance = inst;
-
/*
* See if we can find our Help file.
*/
- {
- char b[2048], *p, *q, *r;
- FILE *fp;
- GetModuleFileName(NULL, b, sizeof(b) - 1);
- r = b;
- p = strrchr(b, '\\');
- if (p && p >= r) r = p+1;
- q = strrchr(b, ':');
- if (q && q >= r) r = q+1;
- strcpy(r, "putty.hlp");
- if ( (fp = fopen(b, "r")) != NULL) {
- help_path = dupstr(b);
- fclose(fp);
- } else
- help_path = NULL;
- }
+ init_help();
/*
* Look for the PuTTY binary (we will enable the saved session
/*
* Find out if Pageant is already running.
*/
- already_running = FALSE;
- if (agent_exists())
- already_running = TRUE;
- else {
-
- if (!prev) {
- wndclass.style = 0;
- wndclass.lpfnWndProc = WndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = inst;
- wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
- wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
- wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = APPNAME;
-
- RegisterClass(&wndclass);
- }
-
- main_hwnd = keylist = NULL;
-
- main_hwnd = CreateWindow(APPNAME, APPNAME,
- WS_OVERLAPPEDWINDOW | WS_VSCROLL,
- CW_USEDEFAULT, CW_USEDEFAULT,
- 100, 100, NULL, NULL, inst, NULL);
-
- /* Set up a system tray icon */
- AddTrayIcon(main_hwnd);
+ already_running = agent_exists();
- /* Accelerators used: nsvkxa */
- systray_menu = CreatePopupMenu();
- if (putty_path) {
- session_menu = CreateMenu();
- AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
- AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
- (UINT) session_menu, "&Saved Sessions");
- AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
- }
- AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
- "&View Keys");
- AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
- AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
- if (help_path)
- AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
- AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
- AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
- AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
- initial_menuitems_count = GetMenuItemCount(session_menu);
-
- ShowWindow(main_hwnd, SW_HIDE);
-
- /*
- * Initialise storage for RSA keys.
- */
+ /*
+ * Initialise storage for RSA keys.
+ */
+ if (!already_running) {
rsakeys = newtree234(cmpkeys_rsa);
ssh2keys = newtree234(cmpkeys_ssh2);
-
}
/*
*/
split_into_argv(cmdline, &argc, &argv, &argstart);
for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "-c")) {
+ if (!strcmp(argv[i], "-pgpfp")) {
+ pgp_fingerprints();
+ if (advapi)
+ FreeLibrary(advapi);
+ return 1;
+ } else if (!strcmp(argv[i], "-c")) {
/*
* If we see `-c', then the rest of the
* command line should be treated as a
return 0;
}
+ if (!prev) {
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = inst;
+ wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
+ wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
+ wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = APPNAME;
+
+ RegisterClass(&wndclass);
+ }
+
+ keylist = NULL;
+
+ hwnd = CreateWindow(APPNAME, APPNAME,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ 100, 100, NULL, NULL, inst, NULL);
+
+ /* Set up a system tray icon */
+ AddTrayIcon(hwnd);
+
+ /* Accelerators used: nsvkxa */
+ systray_menu = CreatePopupMenu();
+ if (putty_path) {
+ session_menu = CreateMenu();
+ AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
+ AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
+ (UINT) session_menu, "&Saved Sessions");
+ AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
+ }
+ AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
+ "&View Keys");
+ AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
+ AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
+ if (has_help())
+ AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
+ AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
+ AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
+ AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
+ initial_menuitems_count = GetMenuItemCount(session_menu);
+
+ /* Set the default menu item. */
+ SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
+
+ ShowWindow(hwnd, SW_HIDE);
+
/*
* Main message loop.
*/
NOTIFYICONDATA tnid;
tnid.cbSize = sizeof(NOTIFYICONDATA);
- tnid.hWnd = main_hwnd;
+ tnid.hWnd = hwnd;
tnid.uID = 1;
Shell_NotifyIcon(NIM_DELETE, &tnid);
DestroyMenu(systray_menu);
}
+ if (keypath) filereq_free(keypath);
+
if (advapi)
FreeLibrary(advapi);
- return msg.wParam;
+
+ cleanup_exit(msg.wParam);
+ return msg.wParam; /* just in case optimiser complains */
}