我正在编写一个与一对硬件传感器连接的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的函数的返回类型。
答案 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中,PChar
是PAnsiChar
的别名。在现代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