delphiunicode和控制台
我正在编写一个与一对硬件传感器接口的C#应用程序。不幸的是,设备上唯一公开的接口需要一个用Delphi编写的dll 我正在编写一个Delphi可执行包装器,它为DLL调用必要的函数,并通过stout返回传感器数据。但是,此数据的返回类型是PWideChar(或PChar),我无法将其转换为ansi以便在命令行上打印 如果我直接将数据传递给WriteLn,则每个字符都会得到“?”。如果我查看字符数组,并尝试通过Ansi转换一次打印一个字符,则只打印少数字符(虽然它们确实确认了数据),而且它们通常会按顺序打印。(打印时只需简单地跳转索引即可。)我还尝试将PWideChar转换为整数直线:“I”对应21321。我可能会计算出所有的转换,但有些数据有很多值 我不确定dll使用的是什么版本的Delphi,但我相信它是4。绝对在7之前 感谢您的帮助 TLDR:需要将UTF-16 PWideChar转换为AnsiString进行打印 示例应用程序:delphiunicode和控制台,delphi,Delphi,我正在编写一个与一对硬件传感器接口的C#应用程序。不幸的是,设备上唯一公开的接口需要一个用Delphi编写的dll 我正在编写一个Delphi可执行包装器,它为DLL调用必要的函数,并通过stout返回传感器数据。但是,此数据的返回类型是PWideChar(或PChar),我无法将其转换为ansi以便在命令行上打印 如果我直接将数据传递给WriteLn,则每个字符都会得到“?”。如果我查看字符数组,并尝试通过Ansi转换一次打印一个字符,则只打印少数字符(虽然它们确实确认了数据),而且它们通常会
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
具有不同的含义。我相信Rob在你的另一个问题中向你解释了这一点。因此,您只需将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#app通信是一种非常迂回的方法。我只是直接从C代码p/调用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);
string str = Marshal.PtrToStringUni(ptr);
string str = Marshal.PtrToStringAnsi(ptr);
如果函数返回一个UTF-16字符串(这似乎是可疑的),那么您可以如下转换IntPtr
:
IntPtr ptr = GetDeviceChannelInfo_HSI(Port, Addr, Channel, ref State);
string str = Marshal.PtrToStringUni(ptr);
string str = Marshal.PtrToStringAnsi(ptr);
或者,如果它实际上是一个ANSI字符串,在我看来很有可能,那么您可以这样做:
IntPtr ptr = GetDeviceChannelInfo_HSI(Port, Addr, Channel, ref State);
string str = Marshal.PtrToStringUni(ptr);
string str = Marshal.PtrToStringAnsi(ptr);
当然,如果返回给您的字符串指针是在堆上分配的,那么您会想调用DLL来释放它。我改变了对该注释的看法-我将给出一个答案:) 根据该代码,如果“state”是一个代码HSI_NO_ERRORCODE,并且没有异常,那么它将把未初始化的字符串“output”写入控制台。可能是任何东西,包括意外显示“S”和“4”以及一系列1个或多个问号答案类型(变量)为PChar。对字符串变量使用长度函数。 使用strlen而不是length
for J := 0 to StrLen(answer)-1 do
PChar(char*)的可访问范围也是0..n-1不幸的是,这导致我的字符串从???变为????单身?字符。编辑TLDR以更好地反映。感谢您对该问题的尝试。该函数输出一个PChar(PWideChar),它确实包含我需要的文本,但字符编码似乎与Delphi的WriteLn不兼容。所以呢???。根据这些值,它似乎是UTF16的一种形式。您的转换方法无法将UTF16转换为Ansi以供使用。@大卫:我怀疑@Jonneh可能是对的。通常不使用
^
后缀运算符。没有它,您的代码工作得更好@大卫:嗯,我甚至试过了,它在简单的赋值(Result:=P
)下也能正常工作,但是如果你坚持执行Result:=P^
,那么Result
将只包含P
的第一个字符。这几乎是有道理的,因为如果P
是PWideChar
,那么P^
是WideChar
,也就是说,一个字符。试试看!您希望输出的文本是什么样子的?也许它不能表示为一个AnsiString?例如,Unicode字符串⌬‽∫∇❦∴φ┶代码>无法“转换”为AnsiString。文本显示为???。一个示例的预期输出是:“ISO 4190250”,但我得到的是“??”。如果我一个字符一个字符地转换,我会得到S和a 4,但是其他的要么是空的,要么会引起奇怪的IO问题。可能是某个地方有一些微妙的错误。我认为您需要显示一个最小的项目(带有代码)来显示这个问题。在我看来是可以的。你只是不知道如何解释结果。你声称你得到了一个PWideChar,但我不认为你真的得到了。您说第一个字符的数值为21321。当解释为两个Ansichar时,即为
。您得到的是一个PAnsiChar
,而不是PWideChar
。你不需要转换任何东西。您只需修复导入单元即可使用正确的类型,即PAnsiChar
,而不是PChar
。制造商为您提供了为Delphi 4编写的代码。在Delphi4中,PChar
与PAnsiChar
相同,因此该版本中的代码是正确的。您正在使用Delphi XE2编译Delphi 4代码,PChar
的含义是