win32: single-instance support
This commit is contained in:
parent
045da06ace
commit
4360a45fa6
|
@ -2,6 +2,7 @@
|
||||||
(require ffi/unsafe
|
(require ffi/unsafe
|
||||||
racket/class
|
racket/class
|
||||||
racket/draw
|
racket/draw
|
||||||
|
racket/draw/bstr
|
||||||
"../../syntax.rkt"
|
"../../syntax.rkt"
|
||||||
"../common/freeze.rkt"
|
"../common/freeze.rkt"
|
||||||
"../common/queue.rkt"
|
"../common/queue.rkt"
|
||||||
|
@ -102,6 +103,17 @@
|
||||||
(define-user32 BeginPaint (_wfun _HWND _pointer -> _HDC))
|
(define-user32 BeginPaint (_wfun _HWND _pointer -> _HDC))
|
||||||
(define-user32 EndPaint (_wfun _HDC _pointer -> _BOOL))
|
(define-user32 EndPaint (_wfun _HDC _pointer -> _BOOL))
|
||||||
|
|
||||||
|
(define WM_IS_GRACKET (cast (scheme_register_process_global "PLT_WM_IS_GRACKET" #f)
|
||||||
|
_pointer
|
||||||
|
_UINT_PTR))
|
||||||
|
(define GRACKET_GUID (cast (scheme_register_process_global "PLT_GRACKET_GUID" #f)
|
||||||
|
_pointer
|
||||||
|
_bytes))
|
||||||
|
(define-cstruct _COPYDATASTRUCT
|
||||||
|
([dwData _pointer]
|
||||||
|
[cbData _DWORD]
|
||||||
|
[lpData _pointer]))
|
||||||
|
|
||||||
(defclass window% object%
|
(defclass window% object%
|
||||||
(init-field parent hwnd)
|
(init-field parent hwnd)
|
||||||
(init style
|
(init style
|
||||||
|
@ -189,6 +201,15 @@
|
||||||
[(= msg WM_DROPFILES)
|
[(= msg WM_DROPFILES)
|
||||||
(handle-drop-files wParam)
|
(handle-drop-files wParam)
|
||||||
0]
|
0]
|
||||||
|
;; for single-instance applications:
|
||||||
|
[(and (= msg WM_IS_GRACKET)
|
||||||
|
(positive? WM_IS_GRACKET))
|
||||||
|
;; return 79 to indicate that this is a GRacket window
|
||||||
|
79]
|
||||||
|
;; also for single-instance:
|
||||||
|
[(= msg WM_COPYDATA)
|
||||||
|
(handle-copydata lParam)
|
||||||
|
0]
|
||||||
[else
|
[else
|
||||||
(default w msg wParam lParam)])))
|
(default w msg wParam lParam)])))
|
||||||
|
|
||||||
|
@ -621,6 +642,49 @@
|
||||||
|
|
||||||
;; ----------------------------------------
|
;; ----------------------------------------
|
||||||
|
|
||||||
|
(define (handle-copydata lParam)
|
||||||
|
(let* ([cd (cast lParam _LPARAM _COPYDATASTRUCT-pointer)]
|
||||||
|
[data (COPYDATASTRUCT-lpData cd)]
|
||||||
|
[guid-len (bytes-length GRACKET_GUID)]
|
||||||
|
[data-len (COPYDATASTRUCT-cbData cd)])
|
||||||
|
(when (and (data-len
|
||||||
|
. > .
|
||||||
|
(+ guid-len (ctype-sizeof _DWORD)))
|
||||||
|
(bytes=? GRACKET_GUID
|
||||||
|
(scheme_make_sized_byte_string data
|
||||||
|
guid-len
|
||||||
|
0))
|
||||||
|
(bytes=? #"OPEN"
|
||||||
|
(scheme_make_sized_byte_string (ptr-add data guid-len)
|
||||||
|
4
|
||||||
|
0)))
|
||||||
|
;; The command line's argv (sans argv[0]) is
|
||||||
|
;; expressed as a DWORD for the number of args,
|
||||||
|
;; followed by each arg. Each arg is a DWORD
|
||||||
|
;; for the number of chars and then the chars
|
||||||
|
(let ([args
|
||||||
|
(let ([count (ptr-ref data _DWORD 'abs (+ guid-len 4))])
|
||||||
|
(let loop ([i 0] [delta (+ guid-len 4 (ctype-sizeof _DWORD))])
|
||||||
|
(if (or (= i count)
|
||||||
|
((+ delta (ctype-sizeof _DWORD)) . > . data-len))
|
||||||
|
null
|
||||||
|
(let ([len (ptr-ref (ptr-add data delta) _DWORD)]
|
||||||
|
[delta (+ delta (ctype-sizeof _DWORD))])
|
||||||
|
(if ((+ delta len) . > . data-len)
|
||||||
|
null
|
||||||
|
(let ([s (scheme_make_sized_byte_string
|
||||||
|
(ptr-add data delta)
|
||||||
|
len
|
||||||
|
1)])
|
||||||
|
(if (or (bytes=? s #"")
|
||||||
|
(regexp-match? #rx"\0" s))
|
||||||
|
null
|
||||||
|
(cons (bytes->path s)
|
||||||
|
(loop (add1 i) (+ delta len))))))))))])
|
||||||
|
(map queue-file-event args)))))
|
||||||
|
|
||||||
|
;; ----------------------------------------
|
||||||
|
|
||||||
(define (queue-window-event win thunk)
|
(define (queue-window-event win thunk)
|
||||||
(queue-event (send win get-eventspace) thunk))
|
(queue-event (send win get-eventspace) thunk))
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
char *check_for_another = "yes, please check for another";
|
char *check_for_another = "yes, please check for another";
|
||||||
static int wx_in_terminal = 0;
|
static int wx_in_terminal = 0;
|
||||||
# define MZ_DEFINE_UTF8_MAIN
|
# define MZ_DEFINE_UTF8_MAIN
|
||||||
|
# define PRE_FILTER_CMDLINE_ARGUMENTS
|
||||||
|
static void pre_filter_cmdline_arguments(int *argc, char ***argv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Scheme_Env;
|
struct Scheme_Env;
|
||||||
|
@ -508,6 +510,182 @@ static int parse_command_line(char ***_command, char *buf)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------- */
|
||||||
|
/* single-instance detection */
|
||||||
|
/* ---------------------------------------- */
|
||||||
|
|
||||||
|
static char *CreateUniqueName()
|
||||||
|
{
|
||||||
|
char desktop[MAX_PATH], session[32], *together;
|
||||||
|
int dlen, slen;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Name should be desktop unique, so add current desktop name
|
||||||
|
HDESK hDesk;
|
||||||
|
ULONG cchDesk = MAX_PATH - 1;
|
||||||
|
|
||||||
|
hDesk = GetThreadDesktop(GetCurrentThreadId());
|
||||||
|
|
||||||
|
if (!GetUserObjectInformation( hDesk, UOI_NAME, desktop, cchDesk, &cchDesk))
|
||||||
|
desktop[0] = 0;
|
||||||
|
else
|
||||||
|
desktop[MAX_PATH - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Name should be session unique, so add current session id
|
||||||
|
HANDLE hToken = NULL;
|
||||||
|
// Try to open the token (fails on Win9x) and check necessary buffer size
|
||||||
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||||
|
DWORD cbBytes = 0;
|
||||||
|
|
||||||
|
if(!GetTokenInformation( hToken, TokenStatistics, NULL, cbBytes, &cbBytes )
|
||||||
|
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
PTOKEN_STATISTICS pTS;
|
||||||
|
|
||||||
|
pTS = (PTOKEN_STATISTICS)malloc(cbBytes);
|
||||||
|
|
||||||
|
if(GetTokenInformation(hToken, TokenStatistics, (LPVOID)pTS, cbBytes, &cbBytes)) {
|
||||||
|
sprintf(session, "-%08x%08x-",
|
||||||
|
pTS->AuthenticationId.HighPart,
|
||||||
|
pTS->AuthenticationId.LowPart);
|
||||||
|
} else
|
||||||
|
session[0] = 0;
|
||||||
|
free(pTS);
|
||||||
|
} else {
|
||||||
|
session[0] = 0;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
session[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dlen = strlen(desktop);
|
||||||
|
slen = strlen(session);
|
||||||
|
together = (char *)malloc(slen + dlen + 1);
|
||||||
|
memcpy(together, desktop, dlen);
|
||||||
|
memcpy(together + dlen, session, slen);
|
||||||
|
together[dlen + slen] = 0;
|
||||||
|
|
||||||
|
return together;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GRACKET_GUID "B2261834-D535-44dd-8511-A26FC8F97DD0"
|
||||||
|
|
||||||
|
static int wm_is_gracket;
|
||||||
|
|
||||||
|
static BOOL CALLBACK CheckWindow(HWND wnd, LPARAM param)
|
||||||
|
{
|
||||||
|
int i, len, gl;
|
||||||
|
DWORD w;
|
||||||
|
char **argv, *v;
|
||||||
|
COPYDATASTRUCT cd;
|
||||||
|
DWORD result;
|
||||||
|
LRESULT ok;
|
||||||
|
|
||||||
|
ok = SendMessageTimeout(wnd, wm_is_gracket,
|
||||||
|
0, 0,
|
||||||
|
SMTO_BLOCK |
|
||||||
|
SMTO_ABORTIFHUNG,
|
||||||
|
200,
|
||||||
|
&result);
|
||||||
|
|
||||||
|
printf("try %p result %d\n", wnd, result);
|
||||||
|
|
||||||
|
if (ok == 0)
|
||||||
|
return TRUE; /* ignore and continue */
|
||||||
|
if (result == 79) {
|
||||||
|
/* found it */
|
||||||
|
} else
|
||||||
|
return TRUE; /* continue search */
|
||||||
|
|
||||||
|
/* wnd is owned by another instance of this application */
|
||||||
|
|
||||||
|
SetForegroundWindow(wnd);
|
||||||
|
if (IsIconic(wnd))
|
||||||
|
ShowWindow(wnd, SW_RESTORE);
|
||||||
|
|
||||||
|
argv = (char **)param;
|
||||||
|
|
||||||
|
len = gl = strlen(GRACKET_GUID);
|
||||||
|
len += 4 + sizeof(DWORD);
|
||||||
|
for (i = 1; argv[i]; i++) {
|
||||||
|
len += sizeof(DWORD) + strlen(argv[i]);
|
||||||
|
}
|
||||||
|
w = i - 1;
|
||||||
|
|
||||||
|
v = (char *)malloc(len);
|
||||||
|
memcpy(v, GRACKET_GUID, gl);
|
||||||
|
memcpy(v + gl, "OPEN", 4);
|
||||||
|
memcpy(v + gl + 4, &w, sizeof(DWORD));
|
||||||
|
len = gl + 4 + sizeof(DWORD);
|
||||||
|
for (i = 1; argv[i]; i++) {
|
||||||
|
w = strlen(argv[i]);
|
||||||
|
memcpy(v + len, &w, sizeof(DWORD));
|
||||||
|
len += sizeof(DWORD);
|
||||||
|
memcpy(v + len, argv[i], w);
|
||||||
|
len += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
cd.dwData = 79;
|
||||||
|
cd.cbData = len;
|
||||||
|
cd.lpData = v;
|
||||||
|
|
||||||
|
SendMessage(wnd, WM_COPYDATA, (WPARAM)wnd, (LPARAM)&cd);
|
||||||
|
|
||||||
|
free(v);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CheckSingleInstance(char *normalized_path, char **argv)
|
||||||
|
{
|
||||||
|
/* Check for an existing instance: */
|
||||||
|
if (check_for_another[0] != 'n') {
|
||||||
|
int alreadyrunning;
|
||||||
|
HANDLE mutex;
|
||||||
|
int j, l, i;
|
||||||
|
char *a, *b;
|
||||||
|
|
||||||
|
/* This mutex creation synchronizes multiple instances of
|
||||||
|
the application that may have been started. */
|
||||||
|
j = strlen(normalized_path);
|
||||||
|
|
||||||
|
b = CreateUniqueName();
|
||||||
|
l = strlen(b);
|
||||||
|
a = (char *)malloc(j + l + 50);
|
||||||
|
memcpy(a, normalized_path, j);
|
||||||
|
for (i = 0; i < j; i++) {
|
||||||
|
/* backslashes are not allowed in mutex names */
|
||||||
|
if (a[i] == '\\') a[i] = '/';
|
||||||
|
}
|
||||||
|
memcpy(a + j, b, l);
|
||||||
|
memcpy(a + j + l, "GRacket-" GRACKET_GUID, strlen(GRACKET_GUID) + 9);
|
||||||
|
mutex = CreateMutex(NULL, FALSE, a);
|
||||||
|
alreadyrunning = (GetLastError() == ERROR_ALREADY_EXISTS ||
|
||||||
|
GetLastError() == ERROR_ACCESS_DENIED);
|
||||||
|
/* The call fails with ERROR_ACCESS_DENIED if the Mutex was
|
||||||
|
created in a different users session because of passing
|
||||||
|
NULL for the SECURITY_ATTRIBUTES on Mutex creation. */
|
||||||
|
wm_is_gracket = RegisterWindowMessage(a);
|
||||||
|
free(a);
|
||||||
|
|
||||||
|
if (alreadyrunning) {
|
||||||
|
/* If another instance has been started, try to find it. */
|
||||||
|
if (!EnumWindows((WNDENUMPROC)CheckWindow, (LPARAM)argv)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pre_filter_cmdline_arguments(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
scheme_register_process_global("PLT_WM_IS_GRACKET", (void *)(long)wm_is_gracket);
|
||||||
|
scheme_register_process_global("PLT_GRACKET_GUID", GRACKET_GUID);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------- */
|
/* ---------------------------------------- */
|
||||||
/* command-line parsing */
|
/* command-line parsing */
|
||||||
/* ---------------------------------------- */
|
/* ---------------------------------------- */
|
||||||
|
@ -577,6 +755,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CheckSingleInstance(normalized_path, argv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!wx_in_terminal) {
|
if (!wx_in_terminal) {
|
||||||
scheme_set_stdio_makers(MrEdMakeStdIn,
|
scheme_set_stdio_makers(MrEdMakeStdIn,
|
||||||
MrEdMakeStdOut,
|
MrEdMakeStdOut,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user