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

解決


へっぽこサンデー  2008-08-22 17:22:24  No: 31611  IP: 192.*.*.*

他人の作った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-22 18:45:19  No: 31612  IP: 192.*.*.*

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

>TESTLIB(addr(TTest.TestCallback));

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

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

編集 削除
へっぽこサンデー  2008-08-22 21:24:32  No: 31613  IP: 192.*.*.*

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

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

編集 削除
うんと  2008-08-22 21:37:48  No: 31614  IP: 192.*.*.*

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

そうですね。

implementation

var
  test:TTest;

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

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

編集 削除
へっぽこサンデー  2008-08-22 22:18:27  No: 31615  IP: 192.*.*.*

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

編集 削除
へっぽこサンデー  2008-08-22 23:40:28  No: 31616  IP: 192.*.*.*

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

編集 削除
うんと  2008-08-23 01:37:52  No: 31617  IP: 192.*.*.*

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

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

編集 削除
へっぽこサンデー  2008-08-23 09:38:41  No: 31618  IP: 192.*.*.*

失礼しました。

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-23 15:48:48  No: 31619  IP: 192.*.*.*

で、

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

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

編集 削除
うんと  2008-08-23 15:54:16  No: 31620  IP: 192.*.*.*

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

直前に

tst:=self;

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

編集 削除