2

我正在编写一个与一对硬件传感器接口的 C# 应用程序。不幸的是,在设备上公开的唯一接口需要提供用 Delphi 编写的 dll。

我正在编写一个 Delphi 可执行包装器,它调用 DLL 的必要函数并通过 stout 返回传感器数据。但是,此数据的返回类型是 PWideChar(或 PChar),我无法将其转换为 ansi 以便在命令行上打印。

如果我直接将数据传递给 WriteLn,我会得到“?” 对于每个字符。如果我查看字符数组并尝试使用 Ansi 转换一次打印一个字符,则只会打印几个字符(尽管它们确实确认了数据),并且它们通常会乱序打印。(打印时暴露的索引只是跳来跳去。)我还尝试将 PWideChar 转换为整数:'I' 对应于 21321。我可能会找出所有的转换,但有些数据有很多值。

我不确定 dll 使用的是什么版本的 Delphi,但我相信它是 4。肯定在 7 之前。

任何帮助表示赞赏!

TLDR:需要将 UTF-16 PWideChar 转换为 AnsiString 进行打印。

示例应用:

program SensorReadout;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows,
  SysUtils,
  dllFuncUnit in 'dllFuncUnit.pas'; //This is my dll interface.

var  state: integer;
     answer: PChar;
     I: integer;
     J: integer;
     output: string;
     ch: char;

begin
  try
    for I := 0 to 9 do
    begin
      answer:= GetDeviceChannelInfo_HSI(1, Ord('a'), I, state); //DLL Function, returns a PChar with the results.  See example in comments.

      if state = HSI_NO_ERRORCODE then
      begin
        output:= '';
        for J := 0 to length(answer) do
        begin
          ch:= answer[J]; //This copies the char. Originally had an AnsiChar convert here.
          output:= output + ch;
        end;
        WriteLn(output);
      end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn(I);
end.`

问题是 PAnsiChar 需要是源自 DLL 的函数的返回类型。

4

4 回答 4

2

转换PWideCharAnsiString

function WideCharToAnsiString(P: PWideChar): AnsiString;
begin
  Result := P;
end;

代码从 UTF-16、以 null 结尾的 PWideChar 转换为AnsiString. 如果您在输出中得到问号,那么您的输入不是 UTF-16,或者它包含无法在您的 ANSI 代码页中编码的字符。

我的猜测是,实际发生的情况是您的 Delphi DLL 是使用预 Unicode Delphi 创建的,因此使用 ANSI 文本。但是现在您正试图从PChar具有不同含义的后 Unicode Delphi 链接到它。我确定 Rob 在您的另一个问题中向您解释了这一点。因此,您可以通过将 DLL 导入声明为 returnPAnsiChar而不是PChar. 像这样:

function GetDeviceChannelInfo_HSI(PortNumber, Address, ChNumber: Integer;
  var State: Integer): PAnsiChar; stdcall; external DLL_FILENAME;

完成此操作后,您可以按照我上面描述的类似方式分配给字符串变量。

您需要吸收的是,在旧版本的 Delphi 中,PCharPAnsiChar. 在现代 Delphi 中,它是PWideChar. 这种不匹配将解释您报告的所有内容。


我确实想到将 Delphi 包装器编写到 DLL 并通过 stdout 与您的 C# 应用程序进行通信是一种非常迂回的方法。我只是直接从 C# 代码 p/invoke DLL。您似乎认为这是不可能的,但这很简单。

[DllImport(@"mydll.dll")]
static extern IntPtr GetDeviceChannelInfo_HSI(
    int PortNumber, 
    int Address, 
    int ChNumber,
    ref int State
);

像这样调用函数:

IntPtr ptr = GetDeviceChannelInfo_HSI(Port, Addr, Channel, ref State);

如果该函数返回一个 UTF-16 字符串(这似乎值得怀疑),那么您可以IntPtr像这样转换:

string str = Marshal.PtrToStringUni(ptr);

或者,如果它实际上是一个对我来说很可能的 ANSI 字符串,那么您可以这样做:

string str = Marshal.PtrToStringAnsi(ptr);

然后当然你会想调用你的DLL来释放返回给你的字符串指针,假设它是在堆上分配的。

于 2012-04-06T17:01:06.050 回答
0

改变了我对评论的看法-我会回答:)

根据该代码,如果“状态”是代码 <> HSI_NO_ERRORCODE 并且没有异常,则它将未初始化的字符串“输出”写入控制台。这可能是任何内容,包括意外显示“S”和“4”以及一系列 1 个或多个问号

于 2012-04-06T18:17:39.040 回答
0

答案类型(变量)是 PChar。使用适合字符串变量的长度函数。使用 strlen 而不是长度。

 for J := 0 to StrLen(answer)-1 do

PChar(char *) 的可访问范围也是 0..n-1

于 2012-04-07T10:15:51.493 回答
0

要将 UTF-16 PWideChar 转换为 AnsiString,您可以使用简单转换:

var
  WStr: WideString;
  pWStr: PWideString;
  AStr: AnsiString;
begin
  WStr := 'test';
  pWStr := PWideString(WStr);
  AStr := AnsiString(WideString(pWStr));
end;
于 2021-11-25T13:22:32.363 回答