コールバック関数からクラスメンバにアクセスするには?

解決


へっぽこサンデー  2008-08-23 02:22:24  No: 31611

他人の作ったDLLを簡単に使えるようにコンポーネント化したいのですが、
そのDLLにコールバックして通知する関数を使う際にクラス内の変数が
見えないみたいなのです。

unit uTest;
interface
uses  Windows, SysUtils, Classes, ExtCtrls , StdCtrls;

Function TESTLIB(pfnCallback:pointer):Cardinal;
  stdcall; external 'TEST.Dll';

type
  TTest = class(TObject)
  private
    FTag        : integer;
  protected
    procedure TestCallback(item:Integer);
  public
    constructor Create;
    destructor Destroy;override;
    function Start():boolean;
    property Tag        : integer   read FTag write FTag;
  published
  end;

implementation

    procedure TTest.TestCallback(item:Integer);
    begin
  Tag:=item; <---このTestのプロパティFTagが拒絶される。
    end;
    
function TTest.Start():boolean;
begin
    TESTLIB(addr(TTest.TestCallback));
end;

constructor TTest.Create;
begin
  inherited Create;
end;

destructor TTest.Destroy;
begin
  inherited Destroy;
end;

過去ログ等色々調べたのですが、うまい解決作が見つかりませんでした。
よろしくお願いします。


うんと  2008-08-23 03:45:19  No: 31612

コールバック関数の引数にメソッドを渡してるからでは?

>TESTLIB(addr(TTest.TestCallback));

これはクラス定義をポインタにキャストしてるのが間違いであるだけでなく、
もし具体的なインスタンスのメソッドを渡してもダメでしょう。

呼び出された DLL の中からは、Delphi のクラスインスタンスのメソッドへの
コールバックはできません。普通の関数にするしかないです。


へっぽこサンデー  2008-08-23 06:24:32  No: 31613

>普通の関数にするしかないです。
早速の書き込みありがとうございます。
普通のグローバル?関数のポインターをDLL関数にコールバックポインタとして渡した場合、このクラスを2つ3つと生成した場合の動作はそれぞれが
この一つの関数にコールバックされてしまうのではないのでしょうか?

初歩的な質問で恐縮ですがよろしくご指導ください。


うんと  2008-08-23 06:37:48  No: 31614

>このクラスを2つ3つと生成した場合の動作はそれぞれが
>この一つの関数にコールバックされてしまうのではないのでしょうか?

そうですね。

implementation

var
  test:TTest;

function aCallBack(..)..
begin
  test.Somefuncton(..);
end;

のようにしておいて、呼び出す直前に test に self を代入しておくように
する、とかで回避できますね。


へっぽこサンデー  2008-08-23 07:18:27  No: 31615

うんとさん
ありがとうございました。
目からうろこです。
また、何かありましたらよろしくお願いします。


へっぽこサンデー  2008-08-23 08:40:28  No: 31616

あれ?
解決したかと思ったのですが、うまく動かないです。
このクラスを複数生成した場合、二つ目を生成した段階で一つ目のコールバックが二つ目のクラスメンバにアクセスしてしまってるのかもしれません。
すみませんうんとさん。
もう一度検証お願いできませんか?


うんと  2008-08-23 10:37:52  No: 31617

>もう一度検証お願いできませんか?

どのようなコードを書いたのか知りませんので検証できません。


へっぽこサンデー  2008-08-23 18:38:41  No: 31618

失礼しました。

unit uTest;
interface
uses  Windows, SysUtils, Classes, ExtCtrls , StdCtrls;

Function TESTLIB(pfnCallback:pointer):Cardinal;
  stdcall; external 'TEST.Dll';

type
  TTest = class(TObject)
  private
    FTag        : integer;
  protected
    procedure TestCallback(item:Integer);
  public
    constructor Create;
    destructor Destroy;override;
    function Start():boolean;
    property Tag        : integer   read FTag write FTag;
  published
  end;

implementation
var tst:TTest;

    procedure TTest.TestCallback(item:Integer);
    begin
      Tag:=item;
    end;

    procedure aTestCallback(item:Integer);
    begin
      tst.TestCallback(item);
    end;

function TTest.Start():boolean;
begin
    tst:=self;
    TESTLIB(addr(aTestCallback));
end;

としてみたのですが、どうでしょうか?


うんと  2008-08-24 00:48:48  No: 31619

で、

>このクラスを複数生成した場合、二つ目を生成した段階で一つ目の
>コールバックが二つ目のクラスメンバにアクセスしてしまってるのかもしれません。

これを試したときのコード、および推論にいたる結果を。


うんと  2008-08-24 00:54:16  No: 31620

普通に考えると、DLL の中のコードの実行はよびだしたスレッドの内側で
実行されます。複数のインスタンスを作ったとしても、ですから一つの呼び出しの
実行途中で、二つめの呼び出しの実行が割り込むことはあり得ません。

直前に

tst:=self;

を代入してるのですから、コールバック関数から見える tst のインスタンスは
呼び出したインスタンス以外にあり得ません。もちろん、マルチスレッドで
インスタンスごとにスレッドが違っていれば、おっしゃるようなことが起こり得ますが。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加