delphi 如何获取对象内部方法的指针?

cbwuti44  于 2023-10-18  发布在  其他
关注(0)|答案(2)|浏览(148)

我有一个名为JsSetCallback的方法,它被定义为:

function JsSetCallback(Instance: JsValueRef; const CallbackName: UnicodeString; Callback: JsNativeFunction; CallbackState: Pointer; UseStrictRules: Boolean = True): JsValueRef;

对于它的第三个参数,我必须指定一个JsNativeFunction回调,它被定义为:

type
  JsNativeFunction = function(callee: JsValueRef; isConstructCall: bool; arguments: PJsValueRef; argumentCount: Word; callbackState: Pointer): JsValueRef; stdcall;

现在,假设我有一个名为Window_AtobJsNativeFunction方法,假设我希望它成为JsSetCallback方法的第三个参数。我会写:

function Window_Atob(Callee: JsValueRef; IsConstructCall: bool; Args: PJsValueRefArray; ArgCount: Word; CallbackState: Pointer): JsValueRef; stdcall;
begin
  // ...
end;

JsSetCallback(JsCreateObject, 'atob', @Window_Atob, nil);

一切都会好起来的。现在,让我们假设Window_Atob是一个类的一部分,像这样:

type
  TTestObject = class(TObject)
  public
    function Window_Atob(Callee: JsValueRef; IsConstructCall: bool; Args: PJsValueRefArray; ArgCount: Word; CallbackState: Pointer): JsValueRef; stdcall;
  end;

function TTestObject.Window_Atob(Callee: JsValueRef; IsConstructCall: bool; Args: PJsValueRefArray; ArgCount: Word; CallbackState: Pointer): JsValueRef; stdcall;
begin
  // ...
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TestObject := TTestObject.Create;
  JsSetCallback(JsCreateObject, 'atob', @TestObject.Window_Atob, nil);
  TestObject.Free;
end;

这样的调用将在第15行失败,并出现以下编译器错误:E2036 Variable required。这个错误是由我调用@TestObject.Window_Atob触发的。
Embarcadero defines this compiler error为“当你试图获取表达式或常量的地址时”。然而,我不知道如何解决这个问题。如果Window_Atob方法在一个对象中,是否可以得到它的指针地址?

ijxebb2r

ijxebb2r1#

一个方法有一个隐藏的Self参数,这就是为什么你不能使用你的TTestObject.Window_Atob。只是签名不对。
如果您尝试将TTestObject.Window_Atob转换为JsNativeFunction,则会出现编译器错误“E2009 Incompatible types:“常规过程和方法指针”。
现在,Self参数指向方法所属(或“操作”)的对象(=类的示例)。
如果你有一个不需要与特定对象关联的方法,你可以将它声明为一个 *class方法 *:

type
  TTestObject = class(TObject)
  public
    class function Window_Atob(Callee: JsValueRef; ...): JsValueRef; stdcall;
  end;

现在它将不再有指向特定对象的隐藏Self参数。相反,它将有一个隐藏的Self参数指向类本身!因此,这种方法也会有错误的签名。
但是如果你把它声明为一个 static 类方法,它就不会有任何隐藏参数。从本质上讲,方法变成了一个普通的过程或函数,但在类的范围内:

type
  TTestObject = class(TObject)
  public
    class function Window_Atob(Callee: JsValueRef; ...): JsValueRef; static; stdcall;
  end;

现在TTestObject.Window_Atob与您的程序类型兼容。
显然,将方法设为类方法意味着您不能再在其中访问Self。但这是另一回事。

yv5phkfx

yv5phkfx2#

JsSetCallback()需要一个独立的函数,而不是一个类方法。你不能使用非静态类方法,因为它有一个隐藏的Self参数,JsNativeFunction没有考虑。
因此,您可以使用独立函数,也可以使用静态类方法(如Andreas Rejbrand's answer中所示)。
不过,还有另一个选择。JsSetCallback()似乎有一个用户定义的CallbackState参数,该参数被传递到回调函数中。如果是这样,那么你可以使用一个代理函数作为回调函数,将你的对象指针传递给代理,然后代理可以调用你的类方法在对象上,例如:

type
  TTestObject = class(TObject)
  public
    function Window_Atob(Callee: JsValueRef; IsConstructCall: bool; Args: PJsValueRefArray; ArgCount: Word): JsValueRef;
  end;

function TTestObject.Window_Atob(Callee: JsValueRef; IsConstructCall: bool; Args: PJsValueRefArray; ArgCount: Word): JsValueRef;
begin
  // ...
end;

function Window_Atob_proxy(callee: JsValueRef; isConstructCall: bool; arguments: PJsValueRef; argumentCount: Word; callbackState: Pointer): JsValueRef; stdcall;
begin
  Result := TTestObject(callbackState).Window_Atob(JsValueRef, IsConstructCall, Args, ArgCount);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TestObject := TTestObject.Create;
  JsSetCallback(JsCreateObject, 'atob', @Window_Atob_proxy, TestObject);

  // do something that will trigger the callback ...

  // Note that when you are ready to destroy the object,
  // make sure you remove the callback as well so it does
  // not try to access an invalid object reference later...
  JsSetCallback(JsCreateObject, 'atob', nil, nil);

  TestObject.Free;
end;

相关问题