我有一个C DLL,其中包含许多我从Delphi调用的函数。其中一个函数(比如Func1)返回一个指向结构的指针 - 这一切都正常。通过调用Func1创建的结构存储在DLL中的全局池中。使用第二个函数(Func2),我得到一个指向包含指针数组的内存块的指针,我可以使用偏移量访问数组元素。
我需要能够将返回指针中的地址(从Func1)复制到数组中的任何内存位置(来自Func2)。我的想法是,我可以构建指向预定义结构的指针数组,并使用指针偏移直接从Delphi访问元素。
我尝试使用:
CopyMemory(Pointer(NativeUInt(DataPointer) + offset), PStruct, DataSize);
其中DataPointer是我的数组的开头,而PStruct是从Func1返回的,但这不会复制我需要的地址。
在.NET中它使用Marshal.WriteIntPtr工作并使用Reflector查看底层代码我认为我需要比CopyMemory更复杂的东西。有人在Delphi中有任何想法吗?
编辑:这是从R语言DLL返回的向量结构的包装器的一部分。我有一个基矢量类,我从中导出特定的矢量类型。我有数字向量工作的包装器,所以我的基类看起来很好,这是我得到DataPointer的地方:
function TRVector<T>.GetDataPointer: PSEXPREC;
var
offset: integer;
h: PSEXPREC;
begin
// TVECTOR_SEXPREC is the vector header, with the actual data behind it.
offset := SizeOf(TVECTOR_SEXPREC);
h := Handle;
result := PSEXPREC(NativeUInt(h) + offset);
end;
在数字向量中设置值很容易(忽略错误处理):
procedure TNumericVector.SetValue(ix: integer; value: double);
var
PData: PDouble;
offset: integer;
begin
offset := GetOffset(ix); // -- Offset from DataPointer
PData := PDouble(NativeUInt(DataPointer) + offset);
PData^ := value;
end;
对于字符串向量,我需要(i)创建一个具有预定长度的指针的基本向量,如同数字向量一样(ii)将我的输入数组中的每个字符串转换为R内部字符串(CHARSXP) R mkChar函数(iii)将字符串结构的地址分配给基向量中的相应元素。字符串数组被传递到我的vector类(TCharacterVector)的构造函数中,然后我为数组中的每个字符串调用SetValue(见下文)。
我应该按照Remy的建议考虑PPointer,但这种方法或阵列方法似乎都不起作用。下面是使用Remy的数组方法和一些用于检查地址的指针变量的代码。我只是使用老式的指针算法,并在调试时显示为运行显示的地址:
procedure TCharacterVector.SetValue(ix: integer; value: string);
var
PData: PSEXPREC;
offset: integer;
offset2: integer;
PTest: PSEXPREC;
PPtr: Pointer;
PPtr2: Pointer;
begin
offset := GetOffset(ix);
PPtr := PPointer(NativeUInt(DataPointer) + offset); // $89483D8
PData := mkChar(value); // $8850258
// -- Use the following code to check that mkChar is working.
offset2 := SizeOf(TVECTOR_SEXPREC);
PTest := PSEXPREC(NativeUInt(PData) + offset);
FTestString := FTestString + AnsiString(PAnsiChar(PTest));
//PPointerList(DataPointer)^[ix] := PData;
//PPtr2 := PPointer(NativeUInt(DataPointer) + offset); // Wrong!
PPointerArray(DataPointer)^[ix] := PData;
PPtr2 := PPointerArray(DataPointer)^[ix]; // $8850258 - correct
end;
我原以为PData中的地址($ 8850258)现在会在PPtr2中,但我一直盯着这看,所以我肯定我错过了一些明显的东西。
Edit2:R.NET中使用的SetValue代码如下(忽略对空字符串的测试):
private void SetValue(int index, string value)
{
int offset = GetOffset(index);
IntPtr stringPointer = mkChar(value);
Marshal.WriteIntPtr(DataPointer, offset, stringPointer);
}
从反射器,Marshal.WriteIntPtr使用以下C:
public static unsafe void WriteInt32(IntPtr ptr, int ofs, int val)
{
try
{
byte* numPtr = (byte*) (((void*) ptr) + ofs);
if ((((int) numPtr) & 3) == 0)
{
*((int*) numPtr) = val;
}
else
{
byte* numPtr2 = (byte*) &val;
numPtr[0] = numPtr2[0];
numPtr[1] = numPtr2[1];
numPtr[2] = numPtr2[2];
numPtr[3] = numPtr2[3];
}
}
catch (NullReferenceException)
{
throw new AccessViolationException();
}
}
答案 0 :(得分:3)
您说要将结构指针本身复制到数组中,但您显示的代码是尝试复制指针指向的结构数据。如果您真的只想复制指针本身,请不要使用CopyMemory()
。只需按指定指定指针:
const
MaxPointerList = 255; // whatever max array count that Func2() allocates
type
TPointerList = array[0..MaxPointerList-1] of Pointer;
PPointerList = ^TPointerList;
PPointerList(DataPointer)^[index] := PStruct;
您对NativeUInt
的使用表明您使用的是可能支持{$POINTERMATH}
指令的Delphi版本,因此您可以利用它,例如:
{$POINTERMATH ON}
PPointer(DataPointer)[index] := PStruct;
或者,使用PPointerArray
单元中预先存在的System
类型:
{$POINTERMATH ON}
PPointerArray(DataPointer)[index] := PStruct;