2015-11-03 5 views
5

я могу скопировать память из буфера в безопасный массив следующийМогу ли я создать VarArray OleVariant из буфера (pByte) и размер без копирования?

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; 

Но есть способ, чтобы непосредственно передать свой указатель и размер в varArray типа OleVariant без копирования памяти?

[Редактировать]

Я могу видеть, что массив внутри OleVariant является SAFEARRAY (определяется как PVarArray = ^TVarArray), так что кажется, что должен быть способ сделать это путем заполнения значений в TVarArray и настройки значения VType и VArray в OleVariant.

ответ

8

Есть ли способ напрямую передать мой указатель и размер в varArray типа OleVariant без копирования памяти?

Делфи OleVariant типа является оболочкой для VARIANT записи OLE в. Единственным типом массива, который поддерживает OLE, является SAFEARRAY, и любой SAFEARRAY, созданный функцией Win32 SafeArrayCreate...(), выделяет и владеет блоком данных, на который он указывает. Вы должны скопировать исходные данные в этот блок.

Чтобы обойти это, вы должны пропустить VarArrayCreate() (который вызывает SafeArrayCreate()) и выделить SAFEARRAY себя с помощью SafeArrayAllocDescriptor/Ex() поэтому не выделяет блок данных. Затем вы можете задать поле массива pvData, чтобы указать на ваш существующий блок памяти, и включить флаг в поле fFeatures, чтобы сообщить SafeArrayDestroy() (который OleVariant звонит, когда ему больше не нужен), чтобы не освобождать блок памяти.

попробовать что-то вроде этого:

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; 

Если вы на самом деле не нужно использовать OLE, например, если вы не отдаете ваш массив приложений других людей через OLE, то вы должны использовать в Delphi Variant типа вместо. Вы можете написать Custom Variant Type для хранения любых необходимых данных, даже ссылку на существующий блок памяти, а затем при необходимости использовать Variant и позволить произвольной реализации типов управлять данными по мере необходимости.

+0

Большое спасибо. –

4

Возможно, вы сможете взломать свой способ использования OleVariant с данными массива, не копируя его.

Однако проблема, с которой вам придется столкнуться, - это когда переменная OleVariant выходит из области видимости.

RTL собирается вызывать SafeArrayDestroy в oleaut32.dll, чтобы уничтожить память, связанную с безопасным массивом, и это закончится неудачей, потому что память не исходила от ожидаемой Windows.

+3

Вы можете позволить Windows выделять память для 'SAFEARRAY', поэтому' SafeArrayDestroy() 'может освободить ее, сохраняя при этом возможность сделать точку SAFEARRAY в определенном пользователем блоке элементов вместо выделения своего собственного. Фокус в том, чтобы использовать 'SafeArrayAllocDescriptor/Ex()' вместо 'SafeArrayCreate()' (который использует VarArrayCreate() '), а затем изменить поле' fFeatures' SAFEARRAY непосредственно, чтобы сообщить 'SafeArrayDestroy()' не освобождать элемент блок. –