【问题标题】:Passing data from Delphi DLL into C++ application将数据从 Delphi DLL 传递到 C++ 应用程序
【发布时间】:2012-09-17 11:00:58
【问题描述】:

我有一个用 Delphi 编写的 DLL,它应该确定一些值,然后将结果传递回称为 DLL 的 C++ 应用程序。

要传回 C++ 应用程序的数据是一组四个整数值(但将来可能还有字符串值)。

为此,我需要将这些整数值声明为在调用应用程序和 DLL 之间共享。

在 C++ 应用程序端,我可以这样做(根据http://msdn.microsoft.com/en-us/library/h90dkhs0%28v=vs.80%29.aspx):

#pragma data_seg(".SHARED")

int value1;
int value2;
// ...
int valueN;
#pragma data_seg()

如何在 Delphi 2009 中做同样的事情(声明 value1-N 将存储在共享内存中)?

更新,18.09.2012:我决定使用命名管道来实现注入的 DLL 和外部世界的通信。

但是在我可以使用命名管道之前,我需要解决以下问题。

目前,流程如下:

1) DLL 被注入到旧版应用程序中。

2) DLL 从遗留应用程序中提取一些数据。

3) DLL 被卸载。

我需要修改它,使它像这样工作:

1) DLL 被注入到旧版应用程序中。 2) DLL 启动一个循环,如

bool running = true;
while (running)
{
    command = receiveCommandFromInvokingApp();
    if ("GET_COORDINATES".equals(command))
    {
        // Read coordinates of some cell in the grid
        // Then communicate it via some channel to the outside world
    }
    else if ("READ_CELL_VALUE")
    {
        // Read value of some cell in the grid
        // Then communicate it via some channel to the outside world
    }
    else if ("EXIT")
    {
        // Close the communication channel
        // Perform cleanup work
        running = false;
    }

    // Sleep for, say, 500 milliseconds in order to avoid 100 % CPU usage
}

receiveCommandFromInvokingApp 读取从调用应用程序(从命名管道或任何其他合适的通道)发送的下一个命令。

3) 当调用应用程序发送 EXIT 命令时,DLL 停止循环。

假设我有以下 DLL 代码:

procedure DllMain(reason: integer) ;
begin
  if reason = DLL_PROCESS_DETACH then
    OutputDebugString('DLL PROCESS DETACH')
  else if reason = DLL_THREAD_ATTACH then
    OutputDebugString('DLL THREAD ATTACH')
  else if reason = DLL_THREAD_DETACH then
    OutputDebugString('DLL THREAD DETACH')
  else if reason = DLL_PROCESS_ATTACH then
    OutputDebugString('DLL_PROCESS_ATTACH')
  end;
end; (*DllMain*)

循环应该放在哪里(在哪个分支)?

用它代替OutputDebugString('DLL THREAD ATTACH') 是否明智?

19.09.2012 更新:

我的系统设计发生了变化,现在我通过命名管道将数据从 Delphi DLL 发送到 C# 应用程序。

德尔福代码:

打开命名管道:

function OpenNamedPipe() : THandle;
var
  hPipe : THandle;
  name : string;
  connectResult : LongBool;
begin
  name := '\\.\pipe\delphi-to-cpp';
  hPipe := CreateNamedPipe(PChar(name),
    PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE  or PIPE_NOWAIT,
    PIPE_UNLIMITED_INSTANCES,
    4096 ,
    4096 ,
    4096 ,
    NIL);
  if (hPipe = INVALID_HANDLE_VALUE) then
  begin
    OutputDebugString(PChar('Invalid pipe handle: ' + IntToStr(GetLastError)));
    OutputDebugString(PChar(SysErrorMessage(GetLastError)));
  end;


  OutputDebugString(PChar('OpenNamedPipe, 1'));
  connectResult := ConnectNamedPipe(hPipe, NIL);
  OutputDebugString(PChar('connectResult: ' + BoolToStr(connectResult)));
  OutputDebugString(PChar(SysErrorMessage(GetLastError)));

  OpenNamedPipe := hPipe;
end;

发送消息:

procedure SendMessageToNamedPipe(hPipe:THandle; msg:string);
var
  dwWrite : DWORD;
  lpNumberOfBytesWritten : LongBool;
  MsgLength: DWORD;
  MsgW : PWideChar;
begin
  MsgW := PWideChar(msg);
  MsgLength := lstrlenW(MsgW) * SizeOf(WideChar);
  lpNumberOfBytesWritten := WriteFile(hPipe, MsgW, MsgLength, dwWrite, NIL);

  if not lpNumberOfBytesWritten then
  begin
    OutputDebugString(PChar('Sending error: ' + SysErrorMessage(GetLastError)));
  end
  else
  begin
    OutputDebugString(PChar('Message sent, dwWrite: ' + IntToStr(dwWrite)));
  end;
end;

C# 代码,应该读取 Delphi 应用程序发送的数据:

  NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "delphi-to-cpp",
      PipeDirection.InOut);

  new NamedPipeClientStream(".", "delphi-to-cpp",
      PipeDirection.InOut);

  Debug.WriteLine("Before pipeClient.Connect");

  this.IsRunning = true;


  pipeClient.Connect(5000);
  StreamReader reader = new StreamReader(pipeClient, Encoding.Unicode);

  while (this.IsRunning && pipeClient.IsConnected)
  {
      string message = reader.ReadLine();

      Thread.Sleep(250);
  }
  reader.Close();

此代码不起作用 - reader.ReadLine(); 不返回任何内容。

如果我尝试将数据按字节读取到 char[] 缓冲区中,则该缓冲区在读取结束时包含垃圾。

请注意,C# 应用程序实际上正在接收 SOMETHING,我只是不知道如何从流中提取文本。

我应该如何修改我的代码(Delphi、C# 或两者兼有)以使 Delphi 应用程序发送的文本正确地到达 C# 端?

【问题讨论】:

  • 您能否澄清一下您的问题是否涉及同一个进程(即该 exe 启动 DLL)或作为单独进程运行的 exe 和 DLL 之间的通信?
  • 就其价值而言,我认为您在这里没有充分利用 Stack Overflow。你已经为你的问题提出了 4 或 5 个建议的解决方案,然后每一个都被击倒。如果您回到最开始并仅提出您的问题,那会更好。那么问题是,“你能推荐一个解决这个问题的方法吗”?我的建议是 AutoHotKey,但您可能会得到其他更好的建议。
  • 其实我打算用类似AutoHotKey的东西。为了将文本写入网格单元格(X,Y),我想做的是a)获取单元格的坐标(这已经有效,我试过了),然后b)将这些坐标从Delphi DLL传递到C++应用程序(使用命名管道)和 c) 双击该位置并输入文本。 a) 部分已实施。 c)部分很容易(我过去做过)。所以目前,最难的部分是 b) - 将值从 Delphi DLL 传回 C++。
  • 窗口消息比命名管道更容易。
  • 是的。不过,您需要运行消息泵。在不同的线程中可能会更容易。

标签: c++ delphi dll delphi-2009


【解决方案1】:

Delphi 工具链没有内置该功能。

为了在 Delphi 中做类似的事情,您需要使用内存映射文件 API,或其他一些进程间通信 (IPC) 机制,例如管道、套接字、Windows 消息等

或者,您可以简单地从您的 Delphi DLL 加载 C++ DLL。 C++ DLL 可以访问共享数据,并且您的 Delphi DLL 可以调用 C++ DLL 中读取和写入共享数据的函数。但是,共享数据确实非常不适合这项任务。使用IPC更合适。

您在问题中没有说清楚的是,这里有两个过程。因此需要 IPC。

【讨论】:

  • 我在micksmix.wordpress.com/2011/06/27/named-pipes-unit-for-delphi 找到了一个在 Delphi 中使用命名管道的库。但是没有描述如何使用它(此源代码作者的页面不可用)。有没有关于如何在 Delphi 中打开和写入数据到命名管道的教程?
  • 在 Delphi 中使用命名管道与使用任何其他语言完全相同。查找使用原始 Win32 API 的 C++ 教程并从那里开始。
【解决方案2】:

在我看来,这个解决方案有很多问题。将全局变量用作返回机制的想法已经是一个非常糟糕的想法 - 如果多个进程正在运行,则不是线程安全/复杂的。还值得注意的是,DLL 的所有实例都将共享这个空间。

我肯定会:

  1. 将一个空结构从 C++ 传递给 Delphi,由 Delphi 端填充(或者您需要使用缓冲区而不仅仅是指针的字符串)或

  2. 将指向字段的指针传递给 delphi 进程并允许 DLL 更新这些指针处的值或

  3. 将这些结果存储在 DLL 中的全局变量中,并创建单独的 FUNCTION 调用以从 C++ 中检索值。 IE。 int getValueA()

【讨论】:

  • 正如 David 所建议的那样,可以使用更重的进程间通信机制,但在这种情况下这似乎有点过头了。
  • 在阅读问题时,根据发帖人最近的一系列问题,这里实际上有两个过程。因此需要 IPC。
  • 是的 - 不清楚:他说“将结果传递回调用 DLL 的 C++ 应用程序”。已要求澄清。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多