Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create a VarArray OleVariant from a buffer (pByte) and size without copying?

I can copy the memory from the buffer into the safe array as follows

  function GetVarArrayFromBuffer(ABuffer : pByte; ASizeInBytes: Cardinal) : OleVariant;
  var
    LVarArrayPtr: Pointer;     
  begin
    Result := VarArrayCreate([0, ASizeInBytes - 1], varByte);
    LVarArrayPtr := VarArrayLock(Result);
    try
      Move(ABuffer^, LVarArrayPtr^, ASizeInBytes);
    finally
      VarArrayUnLock(Result);
    end;
  end;

But, is there a way to directly pass my pointer and size into a varArray type OleVariant without copying memory?

[Edit]

I can see that the array inside the OleVariant is a SAFEARRAY (defined as PVarArray = ^TVarArray), so it seems like there should be a way to do this by populating the values in a TVarArray and setting the VType and VArray values in the OleVariant.

like image 833
Jasper Schellingerhout Avatar asked Nov 03 '15 20:11

Jasper Schellingerhout


2 Answers

is there a way to directly pass my pointer and size into a varArray type OleVariant without copying memory?

Delphi's OleVariant type is a wrapper for OLE's VARIANT record. The only type of array that OLE supports is SAFEARRAY, and any SAFEARRAY created by a Win32 SafeArrayCreate...() function allocates and owns the data block that it points to. You have to copy your source data into that block.

To bypass that, you would have to skip VarArrayCreate() (which calls SafeArrayCreate()) and allocate the SAFEARRAY yourself using SafeArrayAllocDescriptor/Ex() so it does not allocate a data block. Then you can set the array's pvData field to point at your existing memory block, and enable the FADF_AUTO flag in its fFeatures field to tell SafeArrayDestroy() (which OleVariant calls when it does not need the SAFEARRAY anymore) to not free your memory block.

Try something like this:

uses
  ..., Ole2, ComObj;

// Delphi's Ole2 unit declares SafeArrayAllocDescriptor()
// but does not declare SafeArrayAllocDescriptorEx()...
function SafeArrayAllocDescriptorEx(vt: TVarType; cDims: Integer; var psaOut: PSafeArray): HResult; stdcall; external 'oleaut32.dll';

function GetVarArrayFromBuffer(ABuffer : pByte; ASizeInBytes: Cardinal) : OleVariant;
var
  SA: PSafeArray;
begin
  OleCheck(SafeArrayAllocDescriptorEx(VT_UI1, 1, SA)); 

  SA.fFeatures := SA.fFeatures or FADF_AUTO or FADF_FIXEDSIZE;
  SA.cbElements := SizeOf(Byte);
  SA.pvData := ABuffer;
  SA.rgsabound[0].lLbound := 0;
  SA.rgsabound[0].cElements := ASizeInBytes;

  TVarData(Result).VType := varByte or varArray;
  TVarData(Result).VArray := PVarArray(SA);
end;

If you don't actually need to use OLE, such as if you are not passing your array to other people's applications via OLE, then you should use Delphi's Variant type instead. You can write a Custom Variant Type to hold whatever data you want, even a reference to your existing memory block, and then use Variant as needed and let your custom type implementation manage the data as needed.

like image 115
Remy Lebeau Avatar answered Oct 16 '22 04:10

Remy Lebeau


You may be able to hack your way into having an OleVariant with your array data in it without copying it.

However, a problem you are going to have is when the OleVariant variable falls out of scope.

The RTL is going to call SafeArrayDestroy in oleaut32.dll to destroy the memory associated with the safe array, and that's going to fail because the memory did not come from where Windows expected.

like image 4
500 - Internal Server Error Avatar answered Oct 16 '22 02:10

500 - Internal Server Error