Компоненты COM (Component Object Model) широко используются в среде Windows и часто требуют работы с потоками данных. Разработчики, использующие Delphi для создания или потребления COM-объектов, могут столкнуться с необходимостью маршалинга потоков для их корректной работы.
Проблема маршалинга потоков
Когда вы потребляете COM-объект, написанный на Delphi, который требует потока (stream), возникает проблема с маршалингом. COM-интерфейс, сгенерированный Visual Studio, ожидает параметр типа object, который должен представлять поток:
comReader.LoadFromStream(object stream)
Использование стандартных потоков, таких как FileStream или MemoryStream, приводит к исключению "Invalid argument".
Решение проблемы
Для решения проблемы необходимо создать обертку для .NET потока, которая реализует COM-интерфейс IStream. Это позволит корректно передать поток в COM-объект.
Альтернативный подход
В качестве альтернативы можно использовать UnmanagedMemoryStream для маршалинга произвольных данных. Это может быть полезно, если реализация IStream кажется излишне сложной.
Подтвержденный ответ
Создание обертки для .NET потока, реализующей интерфейс IStream, является рекомендуемым решением. Пример такой обертки можно найти в статье CDS C# Extractor.
Пример кода
unit StreamWrapper;
interface
uses
System.SysUtils,
System.Classes,
System.Helpers,
System.Runtime.InteropServices.ComTypes;
type
TMemoryStreamWrapper = class(HelperForIStream)
private
FStream: TMemoryStream;
function GetSize(out Size: Int64): HRESULT; stdcall;
function CopyOut(var Buffer: NativeUint; var Count: Int64; var Operation: Int64): HRESULT; stdcall;
function CopyIn(const Buffer: NativeUint; var Count: Int64; var Operation: Int64): HRESULT; stdcall;
function Seek(var Operation: Int64; var Offset: Int64; var pnNewPosition: Int64): HRESULT; stdcall;
function SetSize(var grfCommitFlags: Int64; var pnNewSize: Int64): HRESULT; stdcall;
function LockRegion(var grfLocks: Int64; var Offset: Int64; var pnSize: Int64): HRESULT; stdcall;
function UnlockRegion(var grfLocks: Int64; var Offset: Int64; var pnSize: Int64): HRESULT; stdcall;
function Stat(out pstat: STATSTG; var grfStatFlag: Int64): HRESULT; stdcall;
function Clone(var ppstg: ComInterface); HRESULT; stdcall;
function Read(var pvoid: NativeUint; var cb: Int64; var pcbRead: Int64): HRESULT; stdcall;
function Write(const pvoid: NativeUint; var cb: Int64; var pcbWritten: Int64): HRESULT; stdcall;
constructor Create(AStream: TMemoryStream); override;
destructor Destroy; override; sealed;
public
class function CreateStreamWrapper(AStream: TMemoryStream): TMemoryStreamWrapper; static;
end;
{ TMemoryStreamWrapper }
constructor TMemoryStreamWrapper.Create(AStream: TMemoryStream);
begin
inherited Create;
FStream := AStream;
end;
function TMemoryStreamWrapper.GetSize(out Size: Int64): HRESULT;
begin
// Реализация получения размера потока
end;
function TMemoryStreamWrapper.CopyOut(var Buffer: NativeUint; var Count: Int64; var Operation: Int64): HRESULT;
begin
// Реализация копирования данных из потока
end;
{ Реализация остальных методов }
class function TMemoryStreamWrapper.CreateStreamWrapper(AStream: TMemoryStream): TMemoryStreamWrapper;
begin
Result := TMemoryStreamWrapper.Create(AStream);
Result.AddRef;
end;
implementation
uses
System.SysUtils,
System.Classes,
System.Helpers,
System.Servix,
System.OCIXmlrpc_Server;
type
HelperForIStream = interface(IInterface)
['{0000000F-0000-0000-C000-000000000046}']
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Longint; stdcall;
function _Release: Longint; stdcall;
function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
function GetTypeInfo(Index, LCID: Integer; ppTypeInfo: PTypeInfoA): HRESULT; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: PCharSet; NameCount, LCID: Integer; Names: PLongint): HRESULT; stdcall;
function Invoke(This, DispID: Integer; LCID, wFlags: Integer; pDispParams, pVarResult, pExcepInfo, pArgErr: PDispParams): HRESULT; stdcall;
function Read(var pvoid: NativeUint; var cb: Int64; var pcbRead: Int64): HRESULT; stdcall;
function Write(const pvoid: NativeUint; var cb: Int64; var pcbWritten: Int64): HRESULT; stdcall;
function Seek(var Operation: Int64; var Offset: Int64; var pnNewPosition: Int64): HRESULT; stdcall;
function CopyTo(StreamIn, Offset: Int64; var pcb: Int64): HRESULT; stdcall;
function Commit(Int32): HRESULT; stdcall;
property [dwTransaction] Int32 read FCommitCount write FCommitCount;
property [clsid]{0000000F-0000-0000-C000-000000000046} TGUID read FIID_IStream;
end;
{ HelperForIStream }
type
PHelperForIStream = ^HelperForIStream;
THelperForIStream = HelperForIStream;
{ THelperForIStream }
{ IStream интерфейс, реализуемый в .NET потоке }
end.
Заключение
Маршалинг потоков для COM-объектов на Delphi требует внимательного подхода и понимания того, как работает COM-интерфейс IStream. Создание обертки для .NET потока, которая реализует IStream, является эффективным решением для обеспечения корректной работы с COM-объектами, требующими потоков данных.
Маршалинг потоков в COM-объектах на Delphi заключается в создании обертки для .NET потока, реализующей интерфейс IStream, для корректной передачи потока в COM-объект.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.