我正在嘗試創建一個最小的 DDE 客戶端和服務器來驗證我對如何使用動態資料交換管理庫 (DDEML)有正確的理解。
我有兩個簡單的 Visual C 2019 專案,它們具有以下設定:
- 配置屬性 -> 常規 -> 平臺工具集 Visual Studio 2017 -> Windows XP (v141_xp)
- C/C -> 語言 -> 一致性模式 ->否
- 聯結器 -> 子系統 -> Windows
檔案服務器
#define _WIN32_WINNT 0x0501
#include <iostream>
#include <Windows.h>
HDDEDATA CALLBACK DdeCallback(
UINT uType, // transaction type
UINT uFmt, // clipboard data format
HCONV hconv, // handle to conversation
HSZ hsz1, // handle to string
HSZ hsz2, // handle to string
HDDEDATA hdata, // handle to global memory object
DWORD dwData1, // transaction-specific data
DWORD dwData2) // transaction-specific data
{
switch (uType)
{
case XTYP_REGISTER:
std::cout << "DDE Callback XTYP_REGISTER" << std::endl;
return (HDDEDATA)NULL;
case XTYP_UNREGISTER:
std::cout << "DDE Callback XTYP_UNREGISTER" << std::endl;
return (HDDEDATA)NULL;
case XTYP_ADVDATA:
std::cout << "DDE Callback XTYP_ADVDATA" << std::endl;
return (HDDEDATA)DDE_FACK;
case XTYP_XACT_COMPLETE:
std::cout << "DDE Callback XTYP_XACT_COMPLETE" << std::endl;
return (HDDEDATA)NULL;
case XTYP_CONNECT:
std::cout << "DDE Callback XTYP_CONNECT" << std::endl;
return (HDDEDATA)NULL;
case XTYP_DISCONNECT:
std::cout << "DDE Callback XTYP_DISCONNECT" << std::endl;
return (HDDEDATA)NULL;
default:
return (HDDEDATA)NULL;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
case WM_DESTROY:
case WM_QUERYENDSESSION:
default:
{
return(DefWindowProc(hWnd, uMsg, wParam, lParam));
}
}
return(0L);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
AllocConsole();
FILE* new_stdout;
freopen_s(&new_stdout, "CONOUT$", "w", stdout);
DWORD pidInst = 0;
UINT uResult = 0;
uResult = DdeInitializeA(
&pidInst,
DdeCallback,
APPCLASS_MONITOR | // this is a monitoring application
MF_CALLBACKS | // monitor callback functions
MF_CONV | // monitor conversation data
MF_ERRORS | // monitor DDEML errors
MF_HSZ_INFO | // monitor data handle activity
MF_LINKS | // monitor advise loops
MF_POSTMSGS | // monitor posted DDE messages
MF_SENDMSGS, // monitor sent DDE messages
0); // reserved
HSZ ddeService = DdeCreateStringHandleA(pidInst, "MyDdeServer", CP_WINANSI);
DdeNameService(pidInst, ddeService, 0, DNS_REGISTER);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
客戶端.cpp
#define _WIN32_WINNT 0x0501
#include <iostream>
#include <Windows.h>
HDDEDATA CALLBACK DdeCallback(
UINT uType, // transaction type
UINT uFmt, // clipboard data format
HCONV hconv, // handle to conversation
HSZ hsz1, // handle to string
HSZ hsz2, // handle to string
HDDEDATA hdata, // handle to global memory object
DWORD dwData1, // transaction-specific data
DWORD dwData2) // transaction-specific data
{
switch (uType)
{
case XTYP_REGISTER:
std::cout << "DDE Callback XTYP_REGISTER" << std::endl;
return (HDDEDATA)NULL;
case XTYP_UNREGISTER:
std::cout << "DDE Callback XTYP_UNREGISTER" << std::endl;
return (HDDEDATA)NULL;
case XTYP_ADVDATA:
std::cout << "DDE Callback XTYP_ADVDATA" << std::endl;
return (HDDEDATA)DDE_FACK;
case XTYP_XACT_COMPLETE:
std::cout << "DDE Callback XTYP_XACT_COMPLETE" << std::endl;
return (HDDEDATA)NULL;
case XTYP_CONNECT:
std::cout << "DDE Callback XTYP_CONNECT" << std::endl;
return (HDDEDATA)NULL;
case XTYP_DISCONNECT:
std::cout << "DDE Callback XTYP_DISCONNECT" << std::endl;
return (HDDEDATA)NULL;
default:
return (HDDEDATA)NULL;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
case WM_DESTROY:
case WM_QUERYENDSESSION:
default:
{
return(DefWindowProc(hWnd, uMsg, wParam, lParam));
}
}
return(0L);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
AllocConsole(); // Allocs a console if you do not have one.
FILE* new_stdout;
freopen_s(&new_stdout, "CONOUT$", "w", stdout);
DWORD pidInst = 0;
UINT uResult = 0;
uResult = DdeInitializeA(
&pidInst,
DdeCallback,
APPCLASS_MONITOR | // this is a monitoring application
MF_CALLBACKS | // monitor callback functions
MF_CONV | // monitor conversation data
MF_ERRORS | // monitor DDEML errors
MF_HSZ_INFO | // monitor data handle activity
MF_LINKS | // monitor advise loops
MF_POSTMSGS | // monitor posted DDE messages
MF_SENDMSGS, // monitor sent DDE messages
0);
HSZ stringHandle = DdeCreateStringHandleA(pidInst, "MyDdeClient", CP_WINANSI);
DdeNameService(pidInst, stringHandle, 0, DNS_REGISTER);
HSZ ddeService = DdeCreateStringHandleA(pidInst, "MyDdeServer", CP_WINANSI);
HSZ ddeTopic = DdeCreateStringHandleA(pidInst, "MyDdeTopic", CP_WINANSI);
HCONV hConv = DdeConnect(pidInst, ddeService, ddeTopic, NULL);
if (hConv == 0L)
{
UINT ddeLastError = DdeGetLastError(pidInst);
std::cout << "DDE connect failed error#" << ddeLastError << std::endl;
}
std::cout << "Wating..." << std::endl;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
但是,DdeConnect()失敗并出現錯誤 16390,即DMLERR_INVALIDPARAMETER.
根據DdeGetLastError檔案,這通常是由以下問題之一引起的:
- The application used a data handle initialized with a different item name handle than was required by the transaction.
- The application used a data handle that was initialized with a different clipboard data format than was required by the transaction.
- The application used a client-side conversation handle with a server-side function or vice versa.
- The application used a freed data handle or string handle.
- More than one instance of the application used the same object.
Based on feedback in comments this error was caused by pidInst not being initialized to 0. Once this was fixed the error became DMLERR_NO_CONV_ESTABLISHED which means "A client's attempt to establish a conversation has failed."
However, I've been unable to pinpoint exactly where the issue is. I'm trying to work out what I need to change in order for DdeConnect() to work. I'm not seeing the server get XTYP_CONNECT event.
uj5u.com熱心網友回復:
可以從閱讀檔案中得到答案:
APPCLASS_MONITOR
使應用程式可以監視系統中的 DDE 活動。此標志供 DDE 監視應用程式使用。應用程式通過將一個或多個監視器標志與 APPCLASS_MONITOR 標志相結合來指定要監視的 DDE 活動的型別。有關詳細資訊,請參閱以下備注部分。
評論
DDE 監視應用程式不應嘗試在同一應用程式實體的背景關系中執行 DDE 操作(建立對話、發出事務等)。
換句話說,APPCLASS_MONITOR 僅用于 DDE 監視/除錯應用程式。提供 DDE 服務的 DDE 應用程式不應指定該標志。
uj5u.com熱心網友回復:
DdeConnect 需要解決三個問題:
正如評論中所指出的,在呼叫 DdeInitialize 之前確保 pidInst 被初始化為 0
DWORD pidInst = 0; UINT uResult = 0; uResult = DdeInitializeA( &pidInst, DdeCallback, etc
修改服務器 DdeInitializeA 只為 afCmd 指定 APPCMD_FILTERINITS
在服務器回呼中為 XTYPE_CONNECT 回傳 DDE_FACK
case XTYP_CONNECT: std::cout << "DDE 回呼 XTYP_CONNECT" << std::endl; 回傳 (HDDEDATA)DDE_FACK;
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/345299.html
