如何解决通过Delphi DLLx64填充C#字符串/ PWideChar
在前面,这是我的第一个帖子/问题,所以请保持温柔
我在使用Delphi(10.3)dll时遇到问题,该dll应该可以填充字符串,或更确切地说是PWideChar。
奇怪的是,它在编译32位而不是64位时有效吗?
如标题中所述,dll函数是由c#应用程序调用的,但是由于Delphi Debugger问题,我正在使用VCL App测试该函数。
这是我的DLL代码:
function GetName(var id: integer; var name : PWideChar) : integer; stdcall;
var
tempstr : string;
test : PwideChar;
begin
tempstr := GetNameByID(id) // this just writes the name to a local variable
name := PWideChar(tempstr); //this throws a Access violation in System.Move
result := 0;
end;
以及呼叫应用代码:
function Getname(var id: integer; var name : PWideChar) : integer; stdcall; external '<dllname>.dll';
procedure TryGetName;
var
returnedName : string;
temp : PWideChar;
tempint : Integer;
Begin
returnedName:= 'here is som content which is definitly longer then the name to return';
temp := PWideChar(returnedName);
tempint := 0;
GetName(tempint,temp);
end;
因此,这在32位上可以正常工作,但在64位上,DLL中的名称分配会在System.Move函数中引发访问冲突,在该函数中将分析NativeInt。
到目前为止,我尝试了类似的各种变体
StrLCopy
StringToWideChar
或将字符串移动到局部变量。似乎DLL无法访问Host应用程序正在创建的WideChar数组,但是我不明白为什么这取决于编译体系结构?
我显然需要使用PWideChar,因为该名称应写入C#字符串中,并由 ref 或 out 传递。
有人知道为什么会这样吗?
编辑:@Remy Lebau:绝对正确,该代码甚至无法编译,因为我以Var形式传递了0,我之所以这样写下来,是因为我在将代码分解为基本内容时很懒。我相应地调整了代码
解决方法
您的DLL函数正在通过var
引用获取参数,这意味着可以修改调用者的变量(id
参数不需要此变量,因为该函数不会更改其值)。
该函数只是重新分配其name
参数(从而更改了调用者的PWideChar
变量)以指向另一个内存地址。它实际上并不会尝试将任何内容写入所指向的内存中,因此您不可能从Move()
中得到任何错误,因为它不应该从一开始就被调用。
如果实际上正在调用Move()
,那么您在此处显示的代码不是您在实际项目中使用的实际代码。实际上,此处显示的代码甚至不应该编译,因为您无法像执行操作那样将0
常量传递给var
参数。
话虽如此,即使编译并运行了代码而没有崩溃,也请注意,您所显示的DLL函数正在将调用者的PWideChar
变量设置为指向函数退出时释放的内存。如果调用者此后尝试从该内存中读取任何内容,则会导致问题。
听起来像您希望调用者分配DLL函数填充数据的内存。如果是这样,那么您所显示的代码完全不正确。尝试类似这样的尝试:
function GetName(id: integer; name: PWideChar; namesize: integer): integer; stdcall;
var
tempstr: UnicodeString;
size: Integer;
begin
tempstr := GetNameByID(id);
size := Length(tempstr) + 1;
if (name <> nil) and (namesize >= size) then
begin
Move(PWideChar(tempstr)^,name^,size * SizeOf(WideChar));
Dec(size);
end;
Result := size;
end;
然后您的VCL应用程序可以执行以下操作:
function Getname(id: integer; name: PWideChar; namesize: integer): integer; stdcall; external '<dllname>.dll';
procedure TryGetName;
var
returnedName: string;
begin
SetLength(returnedName,100);
if GetName(0,PWideChar(returnedName),Length(returnedName) + 1) <= Length(returnedName) then
begin
// use returnedName as needed...
end;
end;
或者:
function Getname(id: integer; name: PWideChar; namesize: integer): integer; stdcall; external '<dllname>.dll';
procedure TryGetName;
var
returnedName: string;
size: Integer;
begin
size := GetName(0,nil,0);
SetLength(returnedName,size - 1);
GetName(0,size);
// use returnedName as needed...
end;
您的C#应用可以执行以下操作:
[DllImport("<dllname>.dll",CharSet = CharSet.Unicode)]
public static extern int GetName(int id,StringBuilder name,int namesize);
void TryGetName()
{
StringBuilder buf = new StringBuilder(100);
int size = GetName(0,buf,buf.Capacity + 1);
if (size <= buf.Capacity)
{
string returnedName = buf.ToString(0,size);
// use returnedName as needed...
}
}
或者:
[DllImport("<dllname>.dll",int namesize);
void TryGetName()
{
int size = GetName(0,0);
StringBuilder buf = new StringBuilder(size - 1);
size = GetName(0,size);
string returnedName = buf.ToString(0,size);
// use returnedName as needed...
}
话虽如此,您可以让DLL分配一个新字符串以返回给调用者,而不是使用输出参数,例如:
function GetName(id: integer): PWideChar; stdcall;
var
tempstr : UnicodeString;
size: Integer;
begin
tempstr := GetNameByID(id);
size := (Length(tempstr) + 1) * SizeOf(WideChar);
Result := PWideChar(CoTaskMemAlloc(size));
if Result <> nil then
Move(PWideChar(tempstr)^,Result^,size);
end;
然后您的VCL应用可以执行以下操作:
function Getname(id: integer): PWideChar; stdcall; external '<dllname>.dll';
procedure TryGetName;
var
returnedName: PWideChar;
begin
returnedName := GetName(0);
if returnedName <> nil then
try
// use returnedName as needed...
finally
CoTaskMemFree(returnedName);
end;
end;
您的C#应用程序可以做到这一点:
[DllImport("<dllname>.dll",CharSet = CharSet.Unicode)]
public static extern string GetName(int id);
void TryGetName()
{
string returnedName = GetName(0);
// use returnedName as needed...
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。