将指针中的地址复制到不同的内存地址

时间:2015-11-17 01:30:28

标签: delphi pointers

我有一个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();
  }
}

1 个答案:

答案 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;