Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > DLL каким макаром можна вернуть из нее результат


Автор: X-Vlad 9.4.2008, 11:16
Привет всем.

Пишу длл. в ней лежат компоненты которые работают с веб камерой... с программы я ее запускаю появляется окно с длл там нажимаю кнопку делается снимок - мне его надо передать обратно в программу. Как это сделать? пробовал 
что типа:

функция в длл
Код

function ShowWebCamDlg(aApplication: tApplication): TImage;
...
Result := aTmpImage;
...


Из длл вроди нельзя передавать обьекты.. какой есть выход? может стоит юзать МемориСтрим?

Может кто нибуть стыкался с подобным? 

Зарание благодарен.



Автор: Alexeis 9.4.2008, 11:33
  Если юзается общий менеджер памяти, то можно заюзать обычный array of byte, можно вернуть интерфейс IStream, который получается через TStreamAdapter, а в проге работать уже с IStream.

Автор: pseud 9.4.2008, 11:54
смотри мой пост:
http://forum.vingrad.ru/index.php?showtopic=205118&view=findpost&p=1472773#

там я рекомендовал передавать картинку строкой при помощи преобразований:

TBitmap -> TStringStream -> string

string -> TStringStream -> TBitmap

чувствую, где-то есть подводный камень, но должно работать )))

Автор: MetalFan 9.4.2008, 12:35
pseud, всетаки AnsiString не стоит передавать в/из DLL.
стоит прислушаться к совету Alexeis. самый разумный вариант.

Автор: X-Vlad 9.4.2008, 12:44
тоесть  в моем варианте должно быть
Код

function ShowWebCamDlg(aApplication: tApplication): IStream;
var
  aStream: TStream;
...
begin
..
aStream := TMemoryStream.Create;
try
  aTmpImage.Picture.Bitmap.SaveToStream(aStream);
  Result := aStream;
finally
  aStream.Free;
end;
...


?

Автор: MetalFan 9.4.2008, 12:47
почти. только "поверх" aStream (кстати нелогичное название локальной переменной) надо создать обертку TStreamAdapter

Автор: X-Vlad 9.4.2008, 12:54
Цитата

надо создать обертку TStreamAdapter 


можешь поподробней?

 smile  smile 

Автор: Alexeis 9.4.2008, 13:19
  Ничего сложного
Код

var
  strm : IStream; 
Begin
  strm := TStreamAdapter.Create(aStream) as IStream;


   Больше ничего не нужно. Переменную strm можно передавать куда угодно, но главное не забыть после окончания работы занулить ее присвоив nil, иначе созданный объект не уничтожиться. Если TStreamAdapter назначить владельцем aStream, то он в добавок еще aStream уничтожит по окончанию работы.
Код

var
  strm : IStream; 
Begin
  strm := TStreamAdapter.Create(aStream, soOwned) as IStream;



  На той стороне можно сделать такой же адаптер для TMemoryStream или просто вызывать методы интерфейса IStream для передачи данных.

Автор: X-Vlad 9.4.2008, 13:39
программа
Код

type 
  TWebCamTool = function(aApplication: TApplication; const aFormCaption: string; abtnCaption: string): IStream;  StdCall;
...
procedure ...
...
var
  LibHandle: THandle;
  GetImageFromWebCam: TWebCamTool;
  aStream: IStream;
begin
  LibHandle := LoadLibrary('webcam.dll');
  try
    if LibHandle = HINSTANCE_ERROR then
      raise EDLLLoadError.Create('Unable to Load DLL');
    @GetImageFromWebCam := GetProcAddress(LibHandle, 'GetImageFromWebCam');
    if not (@GetImageFromWebCam = nil) then begin
      aStream := TStreamAdapter.Create(aStream) as IStream;
      try 
        aStream := GetImageFromWebCam(Application, 'Test', 'Press Btn');      
        Image1.Picture.Bitmap.LoadFromStream(aStream);
      finally
        aStream.Free; 
      end;      
    end
  finally
    FreeLibrary(LibHandle);
  end;
end;


DLL
Код

function ShowWebCamDlg(aApplication: TApplication; const aFormCaption: string; abtnCaption: string): IStream;
var
  aStream: TStream;
  Strm : IStream; 
Begin
...
  aStream := TMemoryStream.Create;
  Strm := TStreamAdapter.Create(aStream, soOwned) as IStream;

   aTmpImage.Picture.Bitmap.SaveToStream(aStream);
  Result := aStream;
...



так? или я чтото непонял?

Автор: Rennigth 9.4.2008, 13:54
X-Vlad, 
1. раз строки передаешь используешь ли общий менеджер памяти?
2. Функция в dll также должна быть stdcall.

Добавлено через 39 секунд
п.с. оттально не смотрел...

Автор: X-Vlad 9.4.2008, 14:16
что за фишка такая.. добавляю в uses модуль Types, все равно матерится что незнает что такое IStream?

Автор: Alexeis 9.4.2008, 14:17
X-Vlad, не не так. 

Код

      aStream := TStreamAdapter.Create(aStream) as IStream;
      try 
        aStream := GetImageFromWebCam(Application, 'Test', 'Press Btn');     


1я строчка бесполезная, так как указатель затирается указателем полученным от GetImageFromWebCam.

Код

Image1.Picture.Bitmap.LoadFromStream(aStream);


тоже не проканнает, так как Bitmap.LoadFromStream не умеет тянуть из IStream.

Так что одно из 2х, либо создавать временный TMemoryStream и перекачивать в него данные из aStream, или написать своего потомка от TStream который в своих функциях будет вызывать методы IStream. Т.е. в конструкторе он получит указатель на внешний IStream.

Автор: X-Vlad 9.4.2008, 14:25
Alexeis, 

Цитата

что за фишка такая.. добавляю в uses модуль Types, все равно матерится что незнает что такое IStream? 


что это?

Автор: Rennigth 9.4.2008, 14:30
X-Vlad, а делфя у тя какая?

Автор: Alexeis 9.4.2008, 14:31
uses AciveX;

Автор: X-Vlad 9.4.2008, 14:32
ActiveX - не матерится smile

Добавлено @ 14:44
Цитата

Так что одно из 2х, либо создавать временный TMemoryStream и перекачивать в него данные из aStream, или написать своего потомка от TStream который в своих функциях будет вызывать методы IStream. Т.е. в конструкторе он получит указатель на внешний IStream.


можешь на примере моего кода подсказать как это закодировать? а то я чтото не вьежаю :(

Автор: X-Vlad 9.4.2008, 20:16
Alexeis

вот код ДЛЛ и самой программы... можешь подправить? 
Я неработал никогда с TStreamAdapter. Сделал вот так - матерится AV

dll
Код

...
  function GetImageFromWebCam(aApplication: TApplication; const aFormCaption: string; abtnCaption: string): IStream; StdCall;

var
  frmWebCam: TfrmWebCam;

implementation

{$R *.dfm}

function GetImageFromWebCam(aApplication: TApplication; const aFormCaption: string; abtnCaption: string): IStream;
var
  aStrm: IStream;
  aStream: TStream;
begin
  Application.Handle := aApplication.Handle;
  with TfrmWebCam.Create(aApplication) do
    try
      LoadSystemCamDevice;
      Caption := aFormCaption;
      btnSnapShot.Caption := abtnCaption;
      ShowModal;
      aStream := TMemoryStream.Create;
      aStrm := TStreamAdapter.Create(aStream) as IStream;
      imgSnapShot.Picture.Bitmap.SaveToStream(aStream);
      Result := aStrm;
    finally
      Free;
    end;
end;
...


application
Код

...
procedure TForm1.LoadClick(Sender: TObject);
var
  LibHandle: THandle;
  GetImageFromWebCam: TWebCamTool;
  aOut: TStream;
  aStream: IStream;
begin
  LibHandle := LoadLibrary('webcam.dll');
  try
    if LibHandle = HINSTANCE_ERROR then
      raise EDLLLoadError.Create('Unable to Load DLL');
    @GetImageFromWebCam := GetProcAddress(LibHandle, 'GetImageFromWebCam');
    if not (@GetImageFromWebCam = nil) then begin

      aOut := TMemoryStream.Create;
      aStream := GetImageFromWebCam(Application, 'Test', 'Press Btn');
      try
        aOut := TStream(aStream);
        Image1.Picture.Bitmap.LoadFromStream(aOut);
      finally
        aOut.Free;
      end;
    end;
  finally
    FreeLibrary(LibHandle);
  end;
end;
...


помоги плз.  smile 

Автор: Alexeis 9.4.2008, 20:22
X-Vlad, попробуй в приложении использовать такой класс. Его нужно создать на основе полученного IStream и дальше использовать для загрузки из потока. Этот класс свои вызовы подменит вызовами из IStream .
Код

type
  TExternStream = class(TStream)
  protected
    FSource : IStream;
    function GetSize: Int64; override;
    procedure SetSize(const NewSize: Int64); override;
  public
    constructor Create(Source : IStream);
    destructor Destroy; override;
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  T : TStreamAdapter;
begin

end;

{ TExternStream }


constructor TExternStream.Create(Source: IStream);
begin
  inherited Create;
  FSource := Source;
end;

destructor TExternStream.Destroy;
begin
  FSource := nil;
  inherited;
end;

function TExternStream.GetSize: Int64;
var
  LastPos : LargeInt;
begin
  FSource.Seek(0, STREAM_SEEK_CUR, LastPos);
  FSource.Seek(0, STREAM_SEEK_END, Result);
  FSource.Seek(LastPos, STREAM_SEEK_SET, LastPos);
end;

function TExternStream.Read(var Buffer; Count: Integer): Longint;
begin
  if FSource.Read(@Buffer, Count, @Result) <> S_OK
  then
    Result := 0;
end;

function TExternStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
  FSource.Seek(Offset, byte(Origin), Result);
end;

procedure TExternStream.SetSize(const NewSize: Int64);
begin
  FSource.SetSize(NewSize);
end;

function TExternStream.Write(const Buffer; Count: Integer): Longint;
begin
  if FSource.Write(@Buffer, Count, @Result) <> S_OK
  then
    Result := 0;
end;

Автор: X-Vlad 9.4.2008, 20:26
ты можешь на моем примере показать (наглядно)? извини что туплю :( Учятся как говорится по докам, книгам и чужом коде smile 

Если несложно. smile 

Автор: Alexeis 9.4.2008, 20:36
X-Vlad, у тебя ошибка тут
Код

      try
        aOut := TStream(aStream);


  Объект исполняет интерфейс, но сам интерфейс не является объектом, его можно приводить только к предку, т.е. IUnkown.

Вместо этого следует создать экземпляр TExternStream и передать ему указатель на aStream, и дальше в программе использовать экземпляр класса TExternStream.

И еще у тебя утечка в Dll тут 
Код

aStrm := TStreamAdapter.Create(aStream) as IStream;

В таком виде aStream никто не уничтожит и он осатанеться висеть в памяти, правильно будет так

Код

aStrm := TStreamAdapter.Create(aStream, soOwned) as IStream;


Добавлено через 3 минуты и 19 секунд
Цитата(X-Vlad @  9.4.2008,  19:26 Найти цитируемый пост)
ты можешь на моем примере показать (наглядно)?

Вместо 
Код

   try
        aOut := TStream(aStream);


Нужно 

Код

try
  aOut := TExternStream.Create(aStream);

..........
  aOut.Free; //Тут уничтожиться поток созданный в Dll, так как указатель на aStream занулиться. 


В остальном вроде все верно.

Добавлено через 4 минуты и 44 секунды
     aOut := TMemoryStream.Create; - тоже нужно убрать, эта строчка абсолютно лишняя.

Добавлено через 6 минут и 39 секунд
  В реализации TExternStream могут быть ошибки. Я его проверил только на синтаксис, в действии проверять долго  smile 

Автор: X-Vlad 9.4.2008, 20:43
в программе зделал вот так:
Код

    if not (@GetImageFromWebCam = nil) then begin
      aStream := GetImageFromWebCam(Application, 'Test', 'Press Btn');
      aOut := TExternStream.Create(aStream);
      try
        Image1.Picture.Bitmap.LoadFromStream(aOut);
        Image1.Refresh;
      finally
        aOut.Free;
      end;
    end;
  finally
    FreeLibrary(LibHandle);
  end;


в длл: 
Код

function GetImageFromWebCam(aApplication: TApplication; const aFormCaption: string; abtnCaption: string): IStream;
var
  aStrm: IStream;
  aStream: TStream;
begin
  Application.Handle := aApplication.Handle;
  with TfrmWebCam.Create(aApplication) do
    try
      LoadSystemCamDevice;
      Caption := aFormCaption;
      btnSnapShot.Caption := abtnCaption;
      ShowModal;
      aStream := TMemoryStream.Create;
      aStrm := TStreamAdapter.Create(aStream, soOwned) as IStream;
      imgSnapShot.Picture.Bitmap.SaveToStream(aStream);
      imgSnapShot.Picture.Bitmap.SaveToFile('c:\test.bmp'); // просто чтобы проверить что в битмапе есть картинка
      Result := aStrm;
    finally
      FSysDev.Free;
      FilterGraph.ClearGraph;
      FilterGraph.Active := false;
      Free;
    end;
end;


ничего незагружается в Image в программе. и вылетает AV на FreeLibrary(LibHandle);

в программе подключен ShareMem и в длл. Стоят первые в дпр файлах.

в чем грабли?

Автор: X-Vlad 9.4.2008, 21:18
ауу

Автор: Alexeis 10.4.2008, 00:43
  А ну да, есть еще одна недоработка. Когда происходит запись в поток его указатель смещается в конец. Если потом из него читать, то разумеется ничего не прочтется. Перед чтением следует установить указатель потока в нуль. 

Код

aOut := TExternStream.Create(aStream);
aOut.Position := 0;


Думаю после этого все заработает smile .

Автор: X-Vlad 10.4.2008, 12:47
Большое спасибо. Работает. smile +1

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)