如何释放传递给 C# 的 Delphi PByte?
How to free a Delphi PByte passed to C#?
我设法将 PByte 从 Delphi DLL 传递到 C# 并读取 PByte:
public void DoSomething(string company, string claimNumber, string language)
{
var buffersize = 0;
IntPtr pnt = new IntPtr();
try
{
//Get some data from Delphi dll
if (DelphiController.DoSomething(ref buffersize, ref pnt))
{
byte[] managedArray = new byte[buffersize];
Marshal.Copy(pnt, managedArray, 0, buffersize);
//Do something with managedArray...
}
}
finally
{
//how to free pnt?
DelphiController.FreeMemory(pnt, buffersize);
}
}
Delphi函数:
function DoSomething(var buffersizeArr: integer; var pnt: PByte): Wordbool; stdcall; export;
var
arrB: Tarray<Byte>;
begin
//some code
arrB := TFile.ReadAllBytes(fileName);
buffersizeArr := length(arrB);
pnt := @arrB[0];
//some more code
end;
到目前为止一切正常,所以现在我想释放 pnt
分配的内存。我试图将 pnt
传递回 Delphi DLL,但我无法释放内存并且总是得到 Invalid Pointer Operation
异常。
function FreeMemory(pnt: Pointer; size: integer): Wordbool; stdcall; export;
var
p: Pointer;
begin
try
FreeMem(pnt, size); //throws invalid pointer exception
result := true;
except
on e:Exception do
begin
result := false;
end;
end;
end;
那么此时释放内存的正确方法是什么?
Delphi 代码已损坏。您正在返回在函数 returns 之前销毁的动态数组的地址。换句话说,您的 Delphi 代码 returns 指向已释放内存的指针。你的问题不应该是 "how do I deallocate the memory" 而是你应该问 "how do I stop the memory from being deallocated"!
相反,您应该这样做:
function DoSomething(var buffersizeArr: integer; var pnt: PByte): Wordbool; stdcall;
var
arrB: Tarray<Byte>;
begin
arrB := ...;
buffersizeArr := Length(arrB);
pnt := CoTaskMemAlloc(buffersizeArr);
Move(Pointer(arrB)^, pnt^, buffersizeArr);
Result := ...;
end;
然后您可以使用 Marshal.FreeCoTaskMem
.
在 C# 代码中释放数组
我选择在这里使用 CoTaskMemAlloc
是因为它是从共享堆分配的,因此您可以轻松地从 C# 中解除分配。您同样可以使用 LocalAlloc
,然后在 C# 中使用 Marshal.FreeHGlobal
解除分配。如果您使用 GetMem
在 Delphi 中分配,那么您还需要导出 Delphi 函数来执行释放,因为 GetMem
使用内部 Delphi 堆。
一些小笔记:
export
Delphi 关键字被忽略,您可以将其删除。
- 将
new
与 IntPtr
一起使用毫无意义。你可以写 IntPtr pnt = IntPtr.Zero
.
- 如果您声明 p/invoke 使用
out
参数而不是 ref
那么您不需要初始化作为参数传递的变量。
我设法将 PByte 从 Delphi DLL 传递到 C# 并读取 PByte:
public void DoSomething(string company, string claimNumber, string language)
{
var buffersize = 0;
IntPtr pnt = new IntPtr();
try
{
//Get some data from Delphi dll
if (DelphiController.DoSomething(ref buffersize, ref pnt))
{
byte[] managedArray = new byte[buffersize];
Marshal.Copy(pnt, managedArray, 0, buffersize);
//Do something with managedArray...
}
}
finally
{
//how to free pnt?
DelphiController.FreeMemory(pnt, buffersize);
}
}
Delphi函数:
function DoSomething(var buffersizeArr: integer; var pnt: PByte): Wordbool; stdcall; export;
var
arrB: Tarray<Byte>;
begin
//some code
arrB := TFile.ReadAllBytes(fileName);
buffersizeArr := length(arrB);
pnt := @arrB[0];
//some more code
end;
到目前为止一切正常,所以现在我想释放 pnt
分配的内存。我试图将 pnt
传递回 Delphi DLL,但我无法释放内存并且总是得到 Invalid Pointer Operation
异常。
function FreeMemory(pnt: Pointer; size: integer): Wordbool; stdcall; export;
var
p: Pointer;
begin
try
FreeMem(pnt, size); //throws invalid pointer exception
result := true;
except
on e:Exception do
begin
result := false;
end;
end;
end;
那么此时释放内存的正确方法是什么?
Delphi 代码已损坏。您正在返回在函数 returns 之前销毁的动态数组的地址。换句话说,您的 Delphi 代码 returns 指向已释放内存的指针。你的问题不应该是 "how do I deallocate the memory" 而是你应该问 "how do I stop the memory from being deallocated"!
相反,您应该这样做:
function DoSomething(var buffersizeArr: integer; var pnt: PByte): Wordbool; stdcall;
var
arrB: Tarray<Byte>;
begin
arrB := ...;
buffersizeArr := Length(arrB);
pnt := CoTaskMemAlloc(buffersizeArr);
Move(Pointer(arrB)^, pnt^, buffersizeArr);
Result := ...;
end;
然后您可以使用 Marshal.FreeCoTaskMem
.
我选择在这里使用 CoTaskMemAlloc
是因为它是从共享堆分配的,因此您可以轻松地从 C# 中解除分配。您同样可以使用 LocalAlloc
,然后在 C# 中使用 Marshal.FreeHGlobal
解除分配。如果您使用 GetMem
在 Delphi 中分配,那么您还需要导出 Delphi 函数来执行释放,因为 GetMem
使用内部 Delphi 堆。
一些小笔记:
export
Delphi 关键字被忽略,您可以将其删除。- 将
new
与IntPtr
一起使用毫无意义。你可以写IntPtr pnt = IntPtr.Zero
. - 如果您声明 p/invoke 使用
out
参数而不是ref
那么您不需要初始化作为参数传递的变量。