【问题标题】:DdeConnect Fails with DMLERR_NO_CONV_ESTABLISHEDDdeConnect 失败并显示 DMLERR_NO_CONV_ESTABLISHED
【发布时间】:2021-12-10 14:04:17
【问题描述】:

我正在尝试创建一个最小的 DDE 客户端和服务器,以验证我对如何使用 Dynamic Data Exchange Management Library (DDEML) 有正确的理解。

我有两个简单的 Visual C++ 2019 项目,设置如下:

  • 配置属性 -> 常规 -> 平台工具集 Visual Studio 2017 -> Windows XP (v141_xp)
  • C/C++ -> 语言 -> 一致性模式 ->
  • 链接器 -> 子系统 -> Windows

DdeServer.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(); 
    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);
}

DdeClient.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 documentation,这通常是由以下问题之一引起的:

  • 应用程序使用了一个数据句柄,其初始化的项目名称句柄与事务所需的句柄不同。
  • 应用程序使用的数据句柄使用不同于事务所需的剪贴板数据格式进行初始化。
  • 应用程序使用客户端会话句柄和服务器端函数,反之亦然。
  • 应用程序使用了释放的数据句柄或字符串句柄。
  • 应用程序的多个实例使用同一个对象。

根据 cmets 中的反馈,此错误是由 pidInst 未初始化为 0 引起的。修复此错误后,错误变为 DMLERR_NO_CONV_ESTABLISHED,这意味着“客户端建立对话的尝试失败。”

但是,我无法准确指出问题出在哪里。我正在尝试找出我需要更改的内容以使DdeConnect() 工作。我没有看到服务器获取 XTYP_CONNECT 事件。

【问题讨论】:

  • 您在玩 DDEML 是因为您需要与另一个使用 DDE 的组件进行交互吗?这真的是 DDE 唯一有效的用例了。你不应该尝试将它用于任何新事物。
  • 我不想使用 DDE,但我正在调试我的 16 位 Windows 仿真层,以尝试在 64 位 Windows 10 上运行具有丢失源代码的关键业务 Visual Basic 3 应用程序。它使用 DDE 通过 VB3 运行时在两个组件之间进行通信。我正在模拟 16 位 CPU 指令并拦截 Win16 API 调用并试图让它们与 Win32 API 一起工作,除了 DDE 之外一切都正常。
  • 这可能是不相关的,尽管你的 DdeServer.cpp 设置 _WIN32_WINNT after #include &lt;Windows.h&gt; 很奇怪。
  • 也和这个问题无关,但是你考虑过反编译VB3可执行文件吗?据我了解,应该可以得到非常接近原始源代码的东西。
  • 必须在调用 DdeInitialize 之前将客户端的 pidInst 初始化为 0,而不是让它未初始化。此外 DdeCallback 应该使用 UINT_PTR dwData args 而不是 DWORD (不是一个大问题,与您的实际问题无关)。然后您显然必须在服务器中处理传入的 XTYP_CONNECT。

标签: c++ winapi dde


【解决方案1】:

DdeConnect 工作需要解决三个问题:

  1. 正如 cmets 中指出的,确保 pidInst 在调用 DdeInitialize 之前初始化为 0

    DWORD pidInst = 0; UINT uResult = 0; uResult = DdeInitializeA( &pidInst, DdeCallback, 等等

  2. 修改服务器 DdeInitializeA 只为 afCmd 指定 APPCMD_FILTERINITS

  3. 在服务器回调中为 XTYPE_CONNECT 返回 DDE_FACK

案例 XTYP_CONNECT: std::cout

【讨论】:

    【解决方案2】:

    可以通过阅读documentation获得答案:

    APPCLASS_MONITOR

    使应用程序可以监视系统中的 DDE 活动。 此标志供 DDE 监控应用程序使用。 应用程序通过将一个或多个监控标志与 APPCLASS_MONITOR 标志组合来指定要监控的 DDE 活动类型。详情请参阅以下备注部分。

    备注

    DDE 监控应用程序不应尝试在同一应用程序实例的上下文中执行 DDE 操作(建立对话、发出事务等)。

    换句话说,APPCLASS_MONITOR 仅适用于 DDE 监视/调试应用程序。提供 DDE 服务的 DDE 应用程序不应指定该标志。

    【讨论】:

      猜你喜欢
      • 2015-12-31
      • 1970-01-01
      • 2020-02-08
      • 2011-05-21
      • 2012-05-04
      • 2019-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多