Open files from Windows Explorer using DDE so we re-use the running instance

Fix #477
This commit is contained in:
David Capello 2016-02-04 13:07:36 -03:00
parent 72338a6f26
commit 210ffc090e
7 changed files with 259 additions and 6 deletions

View File

@ -1,5 +1,5 @@
# SHE
# Copyright (C) 2012-2015 David Capello
# Copyright (C) 2012-2016 David Capello
set(SHE_SOURCES)
@ -143,7 +143,8 @@ if(USE_SKIA_BACKEND)
if(WIN32)
list(APPEND SHE_SOURCES
skia/skia_window_win.cpp
win/vk.cpp)
win/vk.cpp
win/window_dde.cpp)
endif()
if(APPLE)

View File

@ -1,4 +1,4 @@
Copyright (c) 2012-2013 David Capello
Copyright (c) 2012-2016 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,5 +1,5 @@
// SHE library
// Copyright (C) 2012-2015 David Capello
// Copyright (C) 2012-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -82,6 +82,21 @@ bool capture_mouse = false;
static LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
// Don't process DDE messages here because we cannot get the first
// DDE message correctly. The problem is that the message pump
// starts before we can subclass the Allegro HWND, so we don't have
// enough time to inject the code to process this kind of message.
//
// For more info see "Once you go input-idle, your application is
// deemed ready to receive DDE messages":
// https://blogs.msdn.microsoft.com/oldnewthing/20140620-00/?p=693
//
// Anyway a possible solution would be to avoid processing the
// message loop until we subclass the HWND. I've tested this and it
// doesn't work, maybe because the message pump on Allegro 4 isn't
// in the main thread, I really don't know. But it just crash the
// whole system (Windows 10).
switch (msg) {
case WM_DROPFILES: {

View File

@ -1,5 +1,5 @@
// SHE library
// Copyright (C) 2012-2015 David Capello
// Copyright (C) 2012-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -22,6 +22,14 @@
#endif
#ifdef _WIN32
#include <windows.h>
#include "she/win/window_dde.h" // Include this one time to
#endif
namespace she {
SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display,

View File

@ -1,5 +1,5 @@
// SHE library
// Copyright (C) 2012-2015 David Capello
// Copyright (C) 2012-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -18,6 +18,7 @@
#include "she/event.h"
#include "she/keys.h"
#include "she/native_cursor.h"
#include "she/win/window_dde.h"
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
@ -554,6 +555,10 @@ namespace she {
}
LRESULT result = FALSE;
if (handle_dde_messages(m_hwnd, msg, wparam, lparam, result))
return result;
return DefWindowProc(m_hwnd, msg, wparam, lparam);
}

205
src/she/win/window_dde.cpp Normal file
View File

@ -0,0 +1,205 @@
// SHE library
// Copyright (C) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "she/win/window_dde.h"
#include "base/string.h"
#include "she/event.h"
#include "she/event_queue.h"
#include <string>
#include <vector>
namespace she {
namespace {
std::string get_atom_string(ATOM atom, bool isUnicode)
{
std::string result;
if (!atom)
return result;
if (isUnicode) {
std::vector<WCHAR> buf(256, 0);
GlobalGetAtomNameW(atom, &buf[0], buf.size());
result = base::to_utf8(std::wstring(&buf[0]));
}
else {
std::vector<char> buf;
UINT chars = GlobalGetAtomNameA(atom, &buf[0], buf.size());
result = &buf[0];
}
return result;
}
// Converts a DDE command string (e.g. "[open(filename)]") into she events.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648995.aspx
bool parse_dde_command(const std::string& cmd)
{
bool result = false;
for (size_t i=0; i<cmd.size(); ) {
if (cmd[i] == '[') {
size_t j = ++i;
for (; j<cmd.size(); ++j) {
if (cmd[j] == '(')
break;
}
std::string cmdName = cmd.substr(i, j-i);
std::vector<std::string> cmdParams;
for (i=j+1; i<cmd.size(); ) {
if (cmd[i] == ')') {
j = i+1;
break;
}
else if (cmd[i] == '"') {
std::string cmdParam;
for (j=++i; j<cmd.size(); ++j) {
if (cmd[j] == '"')
break;
else
cmdParam.push_back(cmd[j]);
}
cmdParams.push_back(cmdParam);
i = j+1;
}
else {
std::string cmdParam;
for (j=i; j<cmd.size(); ++j) {
if (cmd[j] == ',' || cmd[j] == ')')
break;
else
cmdParam.push_back(cmd[j]);
}
cmdParams.push_back(cmdParam);
i = j;
}
}
if (cmdName == "open" && !cmdParams.empty()) {
// Send a message to open the file as if it were dropped into the window.
Event ev;
ev.setType(Event::DropFiles);
ev.setFiles(cmdParams);
she::queue_event(ev);
result = true;
}
i = j;
}
else
++i;
}
return result;
}
union DdeackContainer {
WORD value;
DDEACK ddeack;
};
} // anonymous namespace
bool handle_dde_messages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result)
{
switch (msg) {
case WM_DDE_INITIATE: {
HWND clienthwnd = (HWND)wparam;
UINT appAtom = 0, topicAtom = 0;
if (!UnpackDDElParam(msg, lparam,
&appAtom,
&topicAtom))
return false;
bool useUnicode = (IsWindowUnicode(clienthwnd) ? true: false);
std::string app = get_atom_string((ATOM)appAtom, useUnicode);
std::string topic = get_atom_string((ATOM)topicAtom, useUnicode);
FreeDDElParam(msg, lparam);
if (app != PACKAGE) {
return false;
}
// We have to create new atoms for this WM_DDE_ACK, we cannot
// reuse them according to MSDN:
//
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648996(v=vs.85).aspx
//
// Anyway it looks like Windows returns the same ATOM number.
ATOM newApp = (useUnicode ? GlobalAddAtomW(base::from_utf8(app).c_str()):
GlobalAddAtomA(app.c_str()));
ATOM newTopic = (useUnicode ? GlobalAddAtomW(base::from_utf8(topic).c_str()):
GlobalAddAtomA(topic.c_str()));
if (newApp && newTopic) {
SendMessage(clienthwnd,
WM_DDE_ACK,
(WPARAM)hwnd,
PackDDElParam(WM_DDE_ACK, newApp, newTopic));
}
if (newApp) GlobalDeleteAtom(newApp);
if (newTopic) GlobalDeleteAtom(newTopic);
result = 0;
return true;
}
case WM_DDE_EXECUTE: {
HWND clienthwnd = (HWND)wparam;
bool useUnicode = (IsWindowUnicode(clienthwnd) ? true: false);
LPCVOID cmdPtr = (LPCVOID)GlobalLock((HGLOBAL)lparam);
std::string cmd;
if (useUnicode)
cmd = base::to_utf8((const WCHAR*)cmdPtr);
else
cmd = (const CHAR*)cmdPtr;
GlobalUnlock((HGLOBAL)lparam);
DdeackContainer ddeack;
ddeack.value = 0;
ddeack.ddeack.fAck = 1;
PostMessage(clienthwnd, WM_DDE_ACK,
(WPARAM)hwnd,
ReuseDDElParam(lparam, msg, WM_DDE_ACK,
(UINT_PTR)ddeack.value,
(UINT_PTR)lparam));
if (parse_dde_command(cmd))
SetForegroundWindow(hwnd);
result = 0;
return true;
}
case WM_DDE_TERMINATE: {
HWND clienthwnd = (HWND)wparam;
PostMessage(clienthwnd, WM_DDE_TERMINATE, (WPARAM)hwnd, lparam);
result = 0;
return true;
}
}
return false;
}
} // namespace she

19
src/she/win/window_dde.h Normal file
View File

@ -0,0 +1,19 @@
// SHE library
// Copyright (C) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef SHE_WIN_WINDOW_DDE_H_INCLUDED
#define SHE_WIN_WINDOW_DDE_H_INCLUDED
#pragma once
#include <windows.h>
namespace she {
bool handle_dde_messages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result);
} // namespace she
#endif