Advertisement
Guest User

Пример передачи данных произвольного размера между DLL и exe

a guest
Mar 31st, 2014
619
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 11.43 KB | None | 0 0
  1. // Это пример кода для статьи http://www.gunsmoker.ru/2013/04/plugins-9.html
  2.  
  3. // Мы рассмотрим пять (современных) способов передачи данных произвольного
  4. // размера между двумя исполняемыми модулями (например, DLL и exe), которые
  5. // могут быть написаны на разных языках программирования.
  6.  
  7. // В качестве учебной функции мы будем использовать IntToStr:
  8. // передаём число, функция возвращает строку.
  9.  
  10. // Примеры ниже будут использовать экспортируемые функции для наглядности.
  11. // Но не забывайте, что на практике вместо глобально экспортируемых функций
  12. // предпочтительнее использовать методы интерфейсов - ровно как мы постоянно
  13. // это делаем в вышеукпомянутой серии статей.
  14.  
  15. // Примеры приводятся с полной обработкой ошибок и пригодны для использования в
  16. // D3-DXE6 (с учётом UnicodeString = WideString в Delphi 2007 и ниже).
  17.  
  18. //______________________________________________________________________________
  19.  
  20. // Общие модули: для обработки ошибок через HRESULT.
  21. // Как мы уже обсуждали в серии статей, вы можете не использовать эти модули,
  22. // а взять из них нужные определения и функции.
  23. uses
  24.   ActiveX,
  25.   ComObj;
  26.  
  27. //______________________________________________________________________________
  28.  
  29. // Способ №1: BSTR/WideString (возможно только для строк)
  30.  
  31. function EMyFunc1(const I: Integer; out Rslt: WideString): HRESULT; stdcall;
  32. // эквивалентно:
  33. // function EMyFunc1(const I: Integer): WideString; safecall;
  34. begin
  35.   try
  36.     // начало работы метода
  37.     Rslt := IntToStr(I);
  38.     // конец работы метода
  39.  
  40.     // обработка ошибок (этот код будет отсутствовать, если вы делаете не функцию,
  41.     // а safecall-метод у интерфейса)
  42.     Result := S_OK;
  43.   except
  44.     Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID_NULL, '', '');
  45.   end;
  46. end;
  47.  
  48. // Использование функции:
  49. procedure TForm1.Button1Click(Sender: TObject);
  50. var
  51.   MyFunc1: function(const I: Integer): WideString; safecall;
  52.   S: String;
  53. begin
  54.   // начало вызова
  55.   S := MyFunc1(1);
  56.   // конец вызова
  57. end;
  58.  
  59. //______________________________________________________________________________
  60.  
  61. // Способ №2: интерфейс (обобщённый)
  62.  
  63. type
  64.   // Интерфейс
  65.   IRAWData = interface
  66.   ['{093B31B8-1A1D-46B4-9940-86DE0624A09A}']
  67.     function Data: Pointer; safecall;      // указатель на данные
  68.     function Size: NativeUInt; safecall;   // размер данных
  69.   end;
  70.  
  71.   // Примечание: этот интерфейс имеет самый общий вид
  72.   // (нетипизированный указатель и размер в байтах).
  73.   // Это позволяет использовать его для передачи чего угодно, но ценой не самого
  74.   // удобного вызова.
  75.   // К примеру, если вы хотим передать строку, то вместо Pointer мы бы
  76.   // использовали PWideChar, вместо Size указывали бы Length, а конструктор
  77.   // принимал бы строку. Кроме того, данные были бы нуль-терминированы.
  78.   // См. также альтернативный пример этого способа ниже.
  79.  
  80.   // Реализация
  81.   TRAWData = class(TInterfacedObject, IRAWData)
  82.   // В нашей серии статей вместо TInterfacedObject мы используем более
  83.   // продвинутый TCheckedInterfacedObject - с дополнительными проверками и
  84.   // обработкой ошибок. Всегда используйте TCheckedInterfacedObject вместо
  85.   // TInterfacedObject по мере возможности. TInterfacedObject используется
  86.   // здесь только для примера.
  87.   private
  88.     FData: Pointer;
  89.     FSize: NativeUInt;
  90.   protected
  91.     function Data: Pointer; safecall;
  92.     function Size: NativeUInt; safecall;
  93.   public
  94.     constructor Create(const ASize: NativeUInt);
  95.     destructor Destroy; override;
  96.   end;
  97.  
  98. { TRAWData }
  99.  
  100. constructor TRAWData.Create(const ASize: NativeUInt);
  101. begin
  102.   inherited Create;
  103.   FData := AllocMem(ASize);
  104.   FSize := ASize;
  105. end;
  106.  
  107. destructor TRAWData.Destroy;
  108. begin
  109.   FreeMem(FData);
  110.   FData := nil;
  111.   FSize := 0;
  112.   inherited;
  113. end;
  114.  
  115. function TRAWData.Data: Pointer;
  116. begin
  117.   Result := FData;
  118. end;
  119.  
  120. function TRAWData.Size: NativeUInt;
  121. begin
  122.   Result := FSize;
  123. end;
  124.  
  125. function EMyFunc2(const I: Integer; out Rslt: IRAWData): HRESULT; stdcall;
  126. var
  127.   S: String;
  128.   Data: UnicodeString;
  129.   DataSz: Integer;
  130. begin
  131.   try
  132.     // начало метода
  133.     S := IntToStr(I);
  134.  
  135.     Data := S;
  136.     DataSz := Length(Data) * SizeOf(WideChar);
  137.     Rslt := TRAWData.Create(DataSz);
  138.     Move(Pointer(Data)^, Rslt.Data^, DataSz);
  139.     // конец метода
  140.  
  141.     Result := S_OK;
  142.   except
  143.     Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID_NULL, '', '');
  144.   end;
  145. end;
  146.  
  147. // Использование функции:
  148. procedure TForm1.Button2Click(Sender: TObject);
  149. var
  150.   MyFunc2: function(const I: Integer): IRAWData; safecall;
  151.  
  152.   RAWData: IRAWData;
  153.   Data: UnicodeString;
  154.  
  155.   S: String;
  156. begin
  157.   // начало метода
  158.   RAWData := MyFunc2(1);
  159.   SetLength(Data, RAWData.Size div SizeOf(WideChar));
  160.   Move(RAWData.Data^, Pointer(Data)^, RAWData.Size);
  161.   S := Data;
  162.   // конец метода
  163. end;
  164.  
  165. //______________________________________________________________________________
  166.  
  167. // Способ №3: интерфейс (специализированный)
  168.  
  169. type
  170.   // Интерфейс
  171.   IStringData = interface
  172.   ['{093B31B8-1A1D-46B4-9940-86DE0624A09A}']
  173.     function Str: PWideChar; safecall;    // указатель на данные строки
  174.     function Length: Integer; safecall;   // длина строки
  175.   end;
  176.  
  177.   // Реализация
  178.   TStringData = class(TInterfacedObject, IRAWData)
  179.   private
  180.     FStr: UnicodeString;
  181.   protected
  182.     function Str: PWideChar; safecall;
  183.     function Length: Integer; safecall;
  184.   public
  185.     constructor Create(const AStr: String);
  186.   end;
  187.  
  188. { TStringData }
  189.  
  190. constructor TStringData.Create(const AStr: String);
  191. begin
  192.   inherited Create;
  193.   FStr := AStr;
  194. end;
  195.  
  196. function TStringData.Str: PWideChar;
  197. begin
  198.   Result := PWideChar(FStr);
  199. end;
  200.  
  201. function TStringData.Length: Integer;
  202. begin
  203.   Result := Length(FStr);
  204. end;
  205.  
  206. function EMyFunc3(const I: Integer; out Rslt: IStringData): HRESULT; stdcall;
  207. begin
  208.   try
  209.     // начало метода
  210.     Rslt := TStringData.Create(IntToStr(I));
  211.     // конец метода
  212.  
  213.     Result := S_OK;
  214.   except
  215.     Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID_NULL, '', '');
  216.   end;
  217. end;
  218.  
  219. // Использование функции:
  220. procedure TForm1.Button3Click(Sender: TObject);
  221. var
  222.   MyFunc3: function(const I: Integer): IStringData; safecall;
  223.  
  224.   S: String;
  225. begin
  226.   // начало метода
  227.   S := MyFunc3(1).Str;
  228.   // конец метода
  229. end;
  230.  
  231. //______________________________________________________________________________
  232.  
  233. // Способ №4: выделенная функция управления памятью
  234.  
  235. // Примечание: аналогично способу №2, в способах №4 и 5 мы используем
  236. // максимально общие определения (указатель и размер в байтах). Мы можем
  237. // упростить вызовы таких функций, используя специализированные определения
  238. // (как мы сделали это в способе №3). Я не буду приводить специализированные
  239. // примеры для способов №4 и №5. Это остаётся вам домашним заданием.
  240.  
  241. function EMyFunc4(const I: Integer; out AData: Pointer; out ASize: Integer): HRESULT; stdcall;
  242. var
  243.   S: String;
  244.   Data: UnicodeString;
  245. begin
  246.   try
  247.     // начало метода
  248.     S := IntToStr(I);
  249.  
  250.     Data := S;
  251.     ASize := Length(Data) * SizeOf(WideChar);
  252.     GetMem(AData, ASize);
  253.     Move(Pointer(Data)^, AData^, ASize);
  254.     // конец метода
  255.  
  256.     Result := S_OK;
  257.   except
  258.     Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID_NULL, '', '');
  259.   end;
  260. end;
  261.  
  262. // Этот способ требует одной дополнительной функции на каждую DLL
  263. function EFreeFunc(const AData: Pointer): HRESULT; stdcall;
  264. begin
  265.   FreeMem(AData);
  266. end;
  267.  
  268. procedure TForm1.Button4Click(Sender: TObject);
  269. var
  270.   MyFunc4: function(const I: Integer; out AData: Pointer): Integer; safecall;
  271.   FreeFunc: procedure(const AData: Pointer); safecall;
  272.  
  273.   Data: UnicodeString;
  274.   P: Pointer;
  275.   Size: Integer;
  276.  
  277.   S: String;
  278. begin
  279.   // начало метода
  280.   Size := MyFunc4(1, P);
  281.   try
  282.     SetLength(Data, Size div SizeOf(WideChar));
  283.     Move(P^, Pointer(Data)^, Size);
  284.   finally
  285.     FreeFunc(P);
  286.   end;
  287.   S := Data;
  288.   // конец метода
  289. end;
  290.  
  291. //______________________________________________________________________________
  292.  
  293. // Способ №5: использование общего менеджера памяти
  294.  
  295. // Примечание: это разновидность способа №4, где функции выделения/освобождения
  296. // памяти фиксированы и доступны глобально. В качестве таковых я выбрал
  297. // HeapAlloc/HeapFree, но вы, при желании, можете использовать любые другие
  298. // функции.
  299.  
  300. function EMyFunc5(const I: Integer; out AData: Pointer; out ASize: Integer): HRESULT; stdcall;
  301. var
  302.   S: String;
  303.   Data: UnicodeString;
  304. begin
  305.   try
  306.     // начало метода
  307.     S := IntToStr(I);
  308.  
  309.     Data := S;
  310.     ASize := Length(Data) * SizeOf(WideChar);
  311.     AData := HeapAlloc(GetProcessHeap, 0, ASize);
  312.     Move(Pointer(Data)^, AData^, ASize);
  313.     // конец метода
  314.  
  315.     Result := S_OK;
  316.   except
  317.     Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID_NULL, '', '');
  318.   end;
  319. end;
  320.  
  321. procedure TForm1.Button5Click(Sender: TObject);
  322. var
  323.   MyFunc5: function(const I: Integer; out AData: Pointer): Integer; safecall;
  324.  
  325.   Data: UnicodeString;
  326.   P: Pointer;
  327.   Size: Integer;
  328.  
  329.   S: String;
  330. begin
  331.   // начало метода
  332.   Size := MyFunc5(1, P);
  333.   try
  334.     SetLength(Data, Size div SizeOf(WideChar));
  335.     Move(P^, Pointer(Data)^, Size);
  336.   finally
  337.     HeapFree(GetProcessHeap, 0, P);
  338.   end;
  339.   S := Data;
  340.   // конец метода
  341. end;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement