win32: single-instance support
This commit is contained in:
parent
045da06ace
commit
4360a45fa6
|
@ -2,6 +2,7 @@
|
|||
(require ffi/unsafe
|
||||
racket/class
|
||||
racket/draw
|
||||
racket/draw/bstr
|
||||
"../../syntax.rkt"
|
||||
"../common/freeze.rkt"
|
||||
"../common/queue.rkt"
|
||||
|
@ -102,6 +103,17 @@
|
|||
(define-user32 BeginPaint (_wfun _HWND _pointer -> _HDC))
|
||||
(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%
|
||||
(init-field parent hwnd)
|
||||
(init style
|
||||
|
@ -189,6 +201,15 @@
|
|||
[(= msg WM_DROPFILES)
|
||||
(handle-drop-files wParam)
|
||||
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
|
||||
(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)
|
||||
(queue-event (send win get-eventspace) thunk))
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
char *check_for_another = "yes, please check for another";
|
||||
static int wx_in_terminal = 0;
|
||||
# define MZ_DEFINE_UTF8_MAIN
|
||||
# define PRE_FILTER_CMDLINE_ARGUMENTS
|
||||
static void pre_filter_cmdline_arguments(int *argc, char ***argv);
|
||||
#endif
|
||||
|
||||
struct Scheme_Env;
|
||||
|
@ -508,6 +510,182 @@ static int parse_command_line(char ***_command, char *buf)
|
|||
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 */
|
||||
/* ---------------------------------------- */
|
||||
|
@ -577,6 +755,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR ignored
|
|||
}
|
||||
}
|
||||
|
||||
if (CheckSingleInstance(normalized_path, argv))
|
||||
return 0;
|
||||
|
||||
if (!wx_in_terminal) {
|
||||
scheme_set_stdio_makers(MrEdMakeStdIn,
|
||||
MrEdMakeStdOut,
|
||||
|
|
Loading…
Reference in New Issue
Block a user