我正在编写一个与一对硬件传感器连接的 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 中,PChar
是 PAnsiChar
的别名。在现代 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/