Delphi Unicode 和控制台

标签 delphi

我正在编写一个与一对硬件传感器连接的 C# 应用程序。不幸的是,设备上公开的唯一接口(interface)需要提供用 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 的函数的返回类型。

最佳答案

要将 PWideChar 转换为 AnsiString:

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

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

我的猜测是,实际发生的情况是您的 Delphi DLL 是使用 Unicode 之前的 Delphi 创建的,因此使用 ANSI 文本。但现在您尝试从后 Unicode Delphi 链接到它,其中 PChar 具有不同的含义。我确信罗布在您的其他问题中向您解释了这一点。因此,您可以通过声明 DLL 导入返回 PAnsiChar 而不是 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 来释放返回给您的字符串指针。

关于Delphi Unicode 和控制台,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10046559/

相关文章:

sqlite - 在sqlite数据库(如firebird)的触发器中使用post_event

delphi - 无法转换作为接口(interface)的 TList<T> 项

delphi - 在运行时获取数值而不是日期值

delphi - 使用 for 循环将字符串转换为字符数组 string[0] 始终为 null

mysql - 德尔福 dxExpress MySQL : invalid LAST_INSERT_ID value

delphi - Delphi:使Unicode RAR-Component 2.0

delphi - 未找到 RTL150.BPL

delphi - 如何使用 Delphi 2009 和 Unicode 字符串正确调用 GetLongPathName?

ios - Delphi XE4 和 Xamarin Monotouch 在针对 iOS 的方式上有何不同?

android - 如何删除 delphi firemonkey android 平台的 ListView 中的删除按钮?