ATL的dll与Qt程序通信和传递消息
开发时遇到的需要完成的需求是:Windows右键菜单拓展,增加菜单项,但具体增加的菜单项的名称需要从Qt应用程序中获取状态进而显示不同名称,因此需要ATL的dll与Qt应用程序之前进程消息传递和通信。
Windows下发送消息的方法有几种:
SendMessage:阻塞的,等窗口程序处理完消息再返回。
PostMessage:不阻塞,将消息放到消息队列中立即返回。通过指定目标窗口句柄来确定目标线程,通常情况下由窗口过程来处理消息。
SendMessageTimeout:相当于加了超时时间的SendMessage。
PostThreadMessage:直接指定目标线程ID来确定目标线程,没有目标窗口,只能在消息循环中直接根据消息类型做相应的处理。
发送消息时可以广播HWND_BROADCAST,但是广播只能发给所有的顶层窗口,非顶层是收不到的。并且如果用SendMessage发送广播,有程序没有返回时就会阻塞,因此应使用PostMessage发送广播消息。
如果想传递消息,则必须使用WM_COPYDATA消息,而这种消息只能用SendMessage发送,不能用PostMessage发送。但有时又担心超时,因此我们使用SendMessageTimeout来发送WM_COPYDATA消息。避免程序阻塞。
发送WM_COPYDATA消息,实际上会传递COPYDATASTRUCT结构体,
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
其中dwData是自定义数据,我们可以通过RegisterWindowMessage注册一个窗口消息,保证该消息在系统范围内是唯一的。这样接收数据时就可以判断是否是我们自己发出去的消息。cbData是发送的字符串的长度,lpData是发送的字符串指针。
首先要明确的是,我们此次做的消息通知都是发送到窗口的,因此无论是对于dll和Qt的客户端,都应该是以窗口为基础接收消息。客户端的窗口很容易找到,因为本来就是桌面客户端。dll是没有窗口的,如何接收消息和发送消息呢?
此处的方案是给dll创建一个隐藏窗口。指定类名,窗口不能接受广播消息,但可以通过FindWindow或FindWindowEx查找到该类名的窗口句柄,向指定窗口发送消息。
具体的代码如下:
dll中涉及的代码:
创建自定义消息类型
const UINT WM_WSW_Message = ::RegisterWindowMessage(_T("dllCustomMessage"));
发送消息:
//参数分别是要发送的字符串和RegisterWindowMessage返回值
void sendMsg(std::string strJson, const UINT strMessage)
{
//查找指定标题名的窗口句柄
HWND hwnd = FindWindowEx(nullptr, nullptr, nullptr, _T("Client"));
if (hwnd)
{
LRESULT copyDataResult;
COPYDATASTRUCT cpd;
cpd.dwData = strMessage;
cpd.cbData = strJson.length();
cpd.lpData = (void*)strJson.c_str();
//发送消息
copyDataResult = SendMessageTimeout(hwnd, WM_COPYDATA, NULL, (LPARAM)&cpd, SMTO_ABORTIFHUNG, 15000, 0);
}
}
创建隐藏窗口,接收消息
//创建隐藏窗口
void createMsgWnd()
{
if (m_hWnd != NULL)
return;
HINSTANCE hInst = GetModuleHandle(0);
LPCTSTR lpWndClsName = _T("customHideWindow");
WNDCLASS wc = { 0 };
wc.lpszClassName = lpWndClsName;
wc.style = CS_NOCLOSE;
wc.hInstance = hInst;
wc.lpfnWndProc = __WindowProc;
//注册这个类
if (RegisterClass(&wc))
{
//不能接受广播消息 创建窗口
m_hWnd = CreateWindow(lpWndClsName, lpWndClsName, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInst, this);
}
}
//回调函数,有消息来通过回调得到
LRESULT CALLBACK __WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CAISearch* pThis = NULL;
if (uMsg == WM_NCCREATE) {
LPCREATESTRUCT lpCS = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<CATLTestMenu*>(lpCS->lpCreateParams);
ATLASSERT(NULL != pThis);
::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG>(pThis));
}
else
{
pThis = reinterpret_cast<CATLTestMenu*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (NULL == pThis || pThis->m_hWnd != hwnd) {
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//使用类自己的方法处理消息
return pThis->handleMessage(uMsg, wParam, lParam);
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CAISearch::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//如果是传递字符串的消息
if (uMsg == WM_COPYDATA) {
const UINT WM_WSW_Message = ::RegisterWindowMessage(_T("clientCustomMessage"));
COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
if (pCopyData->dwData == WM_WSW_Message)
{
//获取到客户端发送过来的数据
string strJson = string((char*)pCopyData->lpData, pCopyData->cbData);
return S_OK;
}
}
//关闭窗口的消息
else if (uMsg == WM_CLOSE)
{
HINSTANCE hInst = GetModuleHandle(0);
LPCTSTR lpWndClsName = _T("customHideWindow");
//销毁窗口
DestroyWindow(m_hWnd);
//反注册这个类
UnregisterClass(lpWndClsName, hInst);
}
return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
手动关闭隐藏窗口
//向隐藏窗口句柄发送关闭消息
SendMessage(m_hWnd, WM_CLOSE, 0, NULL);
客户端中涉及代码:
主窗口接收Windows消息,主窗口集成QWidget,重载nativeEvent方法,消息类型为"windows_generic_MSG"代表windows平台
bool nativeEvent(const QByteArray & eventType, void * message, long * result)
{
//windows平台
if (eventType == "windows_generic_MSG")
{
MSG* msg = reinterpret_cast<MSG*>(message); //
//消息类型
if (msg->message == WM_COPYDATA)
{
COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)msg->lParam;
//消息是注册的消息,对上了,证明是dll发送的
const UINT WM_WSW_Message = ::RegisterWindowMessage(_T("dllCustomMessage"));
if(pCopyData->dwData == WM_WSW_Message)
{
//成功拿到dll发送的数据,进行下一步处理
string strJson = string((char*)pCopyData->lpData, pCopyData->cbData);
return true;
}
}
}
return QWidget::nativeEvent(eventType, message, result);
}
向dll发送消息
void sendMsg(std::string strJson)
{
const UINT strMessage = RegisterWindowMessage(_T("clientCustomMessage"));
LRESULT copyDataResult;
COPYDATASTRUCT cpd;
cpd.dwData = strMessage;
cpd.cbData = strJson.length();
cpd.lpData = (void*)strJson.c_str();
HWND hParentWnd = NULL;
HWND hChild = NULL;
string str = "customHideWindow";
WCHAR wsz[64];
swprintf(wsz, 100, L"%S", str.c_str());
LPCWSTR szName = wsz;
//查找dll那个隐藏窗口的句柄
hParentWnd = ::FindWindow(NULL, szName);
if (hParentWnd != NULL)
{
//找到了句柄,发送消息,消息类型是自定义消息类型
copyDataResult = SendMessage(hParentWnd, WM_COPYDATA, NULL, (LPARAM)&cpd);
}
}
有问题欢迎大家一起交流探讨学习。