4

私のシナリオでポインターを適切に使用する方法について、私は少し困惑しています。リソースが埋め込まれた DLL があります。これらのリソースの 1 つのバイナリ データを呼び出し元のアプリに戻す関数をこの DLL に公開します。この場合、JPG 画像ファイルを埋め込んでいます。私の DLL は、ファイルをリソース ストリームに正しくロードします。ただし、そこからアプリに戻すのは面倒です。

これが私のDLLのコードです(JPGがロードされ、名前が付けられていますSOMERESOURCE):

library ResDLL;

{$R *.dres}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

{$R *.res}

function GetResource(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall;
var
  S: TResourceStream;
  L: Integer;
  Data: array of Byte;
begin
  Result:= False;
  try
    S:= TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
    try
      S.Position:= 0;
      L:= S.Size;
      Length:= L;
      SetLength(Data, L);
      S.Read(Data[0], L);
      Buffer:= @Data[0];
      Result:= True;
    finally
      S.Free;
    end;
  except
    Result:= False;
  end;
end;

exports
  GetResource;

begin
end.

そして、ここに私のアプリのコードがあります( aTBitBtnとのみTImage):

function GetResource(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall; external 'ResDLL.dll';

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  Buffer: array of Byte;
  Size: Integer;
  S: TMemoryStream;
  P: TPicture;
begin
  if GetResource('SOMERESOURCE', @Buffer[0], Size) then begin
    S:= TMemoryStream.Create;
    try
      SetLength(Buffer, Size);
      S.Write(Buffer, Size);
      S.Position:= 0;
      P:= TPicture.Create;
      try
        P.Graphic.LoadFromStream(S);
        Image1.Picture.Assign(P);
      finally
        P.Free;
      end;
    finally
      S.Free;
    end;
  end else begin
    raise Exception.Create('Problem calling DLL');
  end;
end;

DLL 呼び出し全体が成功したように見えますが、受信したデータは空 (0 でいっぱい) です。Dataのようなものを としてどのように呼び出す必要があるData[0]か、どのシナリオで を使用する必要があるか、またどのシナリオで を使用する必要があるかについて、私は好奇心でいっぱいです@Data。私はそのコードを完全に DLL に書きました。私はそのような作業に慣れていないので、どこかで失敗したに違いありません。どこが間違っていますか?

4

3 に答える 3

10

DLL 側でGetResource()は、リソース データをローカル配列に読み取り、関数に渡されるバッファーにコピーしません。ローカル配列をBufferポインターに割り当てても、ポイントされているデータはコピーされません。

アプリ側では、リソース データを書き込むためのBitBtn1Click()メモリを割り当てていません。GetResource()たとえそうであったとしても、バッファをTMemoryStream正しく書き込んでいません。あったとしても、 を正しくロードしTMemoryStreamていません。TPicture

バッファの問題を解決するには、いくつかの方法があります。

1)GetResource()バッファーを割り当てて、それをアプリに返し、解放が完了したら、アプリがバッファーを DLL に戻すようにします。

library ResDLL;

{$R *.dres}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

{$R *.res}

function GetResourceData(const ResName: PChar; var Buffer: Pointer;
  var Length: Integer): Bool; stdcall;
var
  S: TResourceStream;
  L: Integer;
  Data: Pointer;
begin
  Result := False;
  try
    S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
    try
      L := S.Size;
      GetMem(Data, L);
      try
        S.ReadBuffer(Data^, L);
        Buffer := Data;
        Length := L;
      except
        FreeMem(Data);
        raise;
      end;
      Result := True;
    finally
      S.Free;
    end;
  except
  end;
end;

procedure FreeResourceData(Buffer: Pointer); stdcall;
begin
  try
    FreeMem(Buffer);
  except
  end;
end;

exports
  GetResourceData,
  FreeBufferData;

begin
end.

.

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Image1: TImage;
    procedure BitBtn1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

uses
  Vcl.Imaging.jpeg;

{$R *.dfm}

function GetResourceData(const ResName: PChar; var Buffer: Pointer;
  var Length: Integer): Bool; stdcall; external 'ResDLL.dll';

procedure FreeResourceData(Buffer: Pointer); stdcall; external 'ResDLL.dll';

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  Buffer: Pointer;
  Size: Integer;
  S: TMemoryStream;
  JPG: TJPEGImage;
begin
  if GetResourceData('SOMERESOURCE', Buffer, Size) then
  begin
    try
      S := TMemoryStream.Create;
      try
        S.WriteBuffer(Buffer^, Size);
        S.Position := 0;
        JPG := TJPEGImage.Create;
        try
          JPG.LoadFromStream(S);
          Image1.Picture.Assign(JPG);
        finally
          JPG.Free;
        end;
      finally
        S.Free;
      end;
    finally
      FreeResourceData(Buffer);
    end;
  end else begin
    raise Exception.Create('Problem calling DLL');
  end;
end;

end.

2) アプリでリソースのサイズを DLL にクエリさせ、バッファを割り当てて DLL に渡して入力します。

library ResDLL;

{$R *.dres}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

{$R *.res}

function GetResourceData(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall;
var
  S: TResourceStream;
  L: Integer;
  Data: Pointer;
begin
  Result := False;
  try
    S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
    try
      L := S.Size;
      if Buffer <> nil then
      begin
        if Length < L then Exit;
        S.ReadBuffer(Buffer^, L);
      end;
      Length := L;
      Result := True;
    finally
      S.Free;
    end;
  except
  end;
end;

exports
  GetResourceData;

begin
end.

.

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Image1: TImage;
    procedure BitBtn1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

uses
  Vcl.Imaging.jpeg;

{$R *.dfm}

function GetResourceData(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall; external 'ResDLL.dll';

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  Buffer: array of Byte;
  Size: Integer;
  S: TMemoryStream;
  JPG: TJPEGImage;
begin
  if GetResourceData('SOMERESOURCE', nil, Size) then
  begin
    SetLength(Buffer, Size);
    if GetResourceData('SOMERESOURCE', @Buffer[0], Size) then
    begin
      S := TMemoryStream.Create;
      try
        S.WriteBuffer(Buffer[0], Size);
        S.Position := 0;
        // alternatively, use TBytesStream, or a custom
        // TCustomMemoryStream derived class, to read
        // from the original Buffer directly so it does
        // not have to be copied in memory...

        JPG := TJPEGImage.Create;
        try
          JPG.LoadFromStream(S);
          Image1.Picture.Assign(JPG);
        finally
          JPG.Free;
        end;
      finally
        S.Free;
      end;
      Exit;
    end;
  end;
  raise Exception.Create('Problem calling DLL');
end;

end.

または:

library ResDLL;

{$R *.dres}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

{$R *.res}

function GetResourceData(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall;
var
  S: TResourceStream;
  L: Integer;
  Data: Pointer;
begin
  Result := False;
  if (Buffer = nil) or (Length <= 0) then Exit;
  try
    S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
    try
      L := S.Size;
      if Length < L then Exit;
      S.ReadBuffer(Buffer^, L);
      Length := L;
      Result := True;
    finally
      S.Free;
    end;
  except
  end;
end;

function GetResourceSize(const ResName: PChar): Integer; stdcall;
var
  S: TResourceStream;
begin
  Result := 0;
  try
    S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
    try
      Result := S.Size;
    finally
      S.Free;
    end;
  except
  end;
end;

exports
  GetResourceData,
  GetResourceSize;

begin
end.

.

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Image1: TImage;
    procedure BitBtn1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

uses
  Vcl.Imaging.jpeg;

{$R *.dfm}

function GetResourceData(const ResName: PChar; Buffer: Pointer;
  var Length: Integer): Bool; stdcall; external 'ResDLL.dll';

function GetResourceSize(const ResName: PChar): Integer; stdcall; external 'ResDLL.dll';

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  Buffer: array of Byte;
  Size: Integer;
  S: TMemoryStream;
  JPG: TJPEGImage;
begin
  Size := GetResourceSize('SOMERESOURCE');
  id Size > 0 then
  begin
    SetLength(Buffer, Size);
    if GetResourceData('SOMERESOURCE', @Buffer[0], Size) then
    begin
      S := TMemoryStream.Create;
      try
        S.WriteBuffer(Buffer[0], Size);
        S.Position := 0;
        JPG := TJPEGImage.Create;
        try
          JPG.LoadFromStream(S);
          Image1.Picture.Assign(JPG);
        finally
          JPG.Free;
        end;
      finally
        S.Free;
      end;
      Exit;
    end;
  end;
  raise Exception.Create('Problem calling DLL');
end;

end.
于 2013-02-04T06:54:37.113 に答える
8

DLLから関数をエクスポートする必要はまったくありません。DLLのモジュールハンドルをホスト実行可能ファイルから直接使用できます。

すでにモジュールハンドルをリソースストリームコンストラクターに渡しています。実行可能ファイルのモジュールハンドルを渡します。代わりに、ライブラリのモジュールハンドルを渡します。

var
  hMod: HMODULE;
....
hMod := LoadLibrary('ResDLL');
try
  S:= TResourceStream.Create(hMod, ...);
  ....
finally
  FreeLibrary(hMod);
end;

DLL内の関数を呼び出したくない場合、それがリソースのみのDLLである場合は、代わりに次を使用LoadLibraryExします。LOAD_LIBRARY_AS_IMAGE_RESOURCE

hMod := LoadLibraryEx('ResDLL', 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE);

おそらく、DLLがすでにロードされていることをご存知でしょう。たとえば、実行可能ファイルに暗黙的にリンクされます。その場合、またはGetModuleHandleではなく、より簡単に使用できます。LoadLibraryLoadLibraryEx

hMod := GetModuleHandle('ResDLL');
S:= TResourceStream.Create(hMod, ...);

簡単な説明のために、すべてのエラーチェックを省略したことに注意してください。

于 2013-02-04T09:05:00.633 に答える
2

ストリームを DLL からアプリケーションに渡すもう 1 つの方法は、インターフェイス化されたストリームを使用することです。

implementation
uses MemoryStream_Interface;
{$R *.dfm}

Type
TGetStream = Procedure(var iStream:IDelphiStream);stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
 h:THandle;
 p:TGetStream;
 ms :IDelphiStream;
 j:TJpegImage;
begin
   ms := TInterfacedMemoryStream.Create;
   h := LoadLibrary('ShowStream.dll');
   if h <> 0 then
      try
      @p := GetProcAddress(h,'GetJpegStream');
      p(ms);
      ms.Position := 0;
      j := TJpegImage.create;
      Image1.Picture.Assign(j);
      j.Free;
      Image1.Picture.Graphic.LoadFromStream(TInterfacedMemoryStream(ms));
      finally
      FreeLibrary(h);
      end;
end;

IDelphiStream のコードはhttp://www.delphipraxis.net にあります。
上記のページのコードには著作権情報がないため、MemoryStream_Interface の内容をこの投稿にコピーしません。

于 2013-02-04T07:56:41.427 に答える