Delphi Unicode和Console

时间:2012-04-06 16:54:18

标签: delphi

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

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

如果我直接将数据传递给WriteLn,我会得到'?'对于每个角色。如果我查看字符数组并尝试使用Ansi转换一次打印一个字符,则只打印一些字符(它们确实会确认数据)并且它们通常会无序打印。 (打开索引只是跳转。)我也尝试将PWideChar转换为整数:'我'对应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的函数的返回类型。

3 个答案:

答案 0 :(得分:2)

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具有不同的含义。我确定Rob在你的另一个问题中向你解释了这一点。因此,您可以通过声明DLL导入返回PAnsiChar而不是PChar来简单地修复它。像这样:

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

完成此操作后,您可以按照与上述相同的方式分配字符串变量。

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


我确实认为将DLL编写器包装到DLL并通过stdout与C#app进行通信是一种非常迂回的方法。我只是直接从C#代码调用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来释放返回给你的字符串指针,假设它是在堆上分配的。

答案 1 :(得分:0)

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

根据该代码,如果“state”是代码<> HSI_NO_ERRORCODE并没有异常,然后它会将未初始化的字符串“output”写入控制台。这可能是任何事情,包括意外地显示“S”和“4”以及一系列1个或多个问号

答案 2 :(得分:0)

答案类型(变量)是PChar。使用长度函数对字符串变量有用。 使用strlen而不是length。

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

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