How can I define callback function of timeSetEvent as an instance method?
TUDPBC = class(TObject)
private
hTimer: word;
...
public
procedure sendUDPBC;
...
end;
procedure DoTimer(uTimerID, uMessage: UINT; dwUser,dw1,dw2: DWORD); stdcall;
procedure TUDPBC.sendUDPBC; //send UDP Broadcast
begin
...
hTimer := TimeSetEvent(FTimeOut, uRes, DoTimer, 0, TIME_ONESHOT); //need DoTimer as a TUDPBC class method
...
end;
How can I define callback function of timeSetEvent as an instance method?
The simple answer is that you cannot. The signature of the callback is not something that you can vary. It is defined by the API and must be a simple unbound procedure with this signature:
typedef void ( CALLBACK *LPTIMECALLBACK)(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser,
DWORD_PTR dw1,
DWORD_PTR dw2
);
What you can do though is pass a pointer to timeSetEvent that contains the address of your instance. The pattern runs like this:
procedure TimeProcCallback(uTimerID, uMessage: UINT; dwUser,dw1,dw2: DWORD_PTR); stdcall;
begin
TUDPBC(dwUser).TimeProc(uTimerID, uMessage);
end;
This is the callback that you pass to timeSetEvent. Your class would look like this:
type
TUDPBC = class
private
FTimerID: MMRESULT;
procedure TimeProc(uTimerID, uMessage: UINT);
end;
This method of the class will be called by the callback function and so have access to the instance variables.
Set the timer like this:
FTimerID := timeSetEvent(TimeOut, uRes, TimeProcCallback, DWORD_PTR(Self), TIME_ONESHOT);
So, you pass the address of the instance in the dwUser argument. That is then passed on to your callback. That callback can, in turn, call an instance method.
Related
In the following, the method Obtain() works, but the GetAs() method would be neater for callers.
However, I can't figure out how to pass an interface as a generic parameter and obtain its GUID.
Declaration:
TInterfaceRegistry = class
private
fRegistry: TDictionary<TGUID, IInterface>;
public
...
procedure Register(const IID: TGUID; IntfObj: IInterface; const Replace: Boolean = False);
function Obtain(const IID: TGUID; out IntfObj): Boolean;
function GetAs<I: IInterface>(): I;
end;
Implementation:
function TInterfaceRegistry.Obtain(const IID: TGUID; out IntfObj): Boolean;
var
Found: IInterface;
begin
Result := fRegistry.TryGetValue(IID, Found);
if Result then
if not Supports(Found, IID, IntfObj) then
raise EBatSoft.Create(SEBatSoftBug);
end;
function TInterfaceRegistry.GetAs<I>(): I;
begin
if not Obtain(I, Result) then
raise EBatSoft.Create(SEDoesNotImplement);
end;
Calling it looks like this...
IntfReg.Register(IMyIntf, TMyImpl.Create);
Getting implementation:
var
Intf: IMyIntf;
begin
// Less type-safe (can pass anything into 2nd parameter)
if IntfReg.Obtain(IMyIntf, Intf) then
...
// Fully type-safe, and simple
Intf := IntfReg.GetAs<IMyIntf>();
You have to use typeinfo via GetTypeData(TypeInfo(I)).GUID.
Keep in mind though this might return an empty guid if you did not declare any for the given interface you are using while the non generic approach simply would not compile.
I'm trying to use MMSystem to call midiInOpen but I'm unsure on how to pass the dwCallback (midiInProc).
I've taken a look at Winapi.MMSystem.pas and the midiInOpen function is declared as the following which gives no clues as what to pass for dwInstance:
{$EXTERNALSYM midiInOpen}
function midiInOpen(lphMidiIn: PHMIDIIN; uDeviceID: UINT;
dwCallback, dwInstance: DWORD_PTR; dwFlags: DWORD): MMRESULT; stdcall;
Does the callback function in Delphi have to be declared in a certain way or can I pass the address of procedure that has the same definition as the midiInProc defined in the WinAPI manual?
Per the midiInOpen() documentation:
dwCallback
Pointer to a callback function, a thread identifier, or a handle of a window called with information about incoming MIDI messages. For more information on the callback function, see MidiInProc.
If you use a callback function, it must match the signature of MidiInProc:
void CALLBACK MidiInProc(
HMIDIIN hMidiIn,
UINT wMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2
);
For example
procedure MyMidiInCallback(hMidiIn: HMIDIIN; wMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR); stdcall;
begin
// do something...
end;
...
var
uDeviceID: UINT;
hMidiIn: HMIDIIN;
begin
uDeviceID := ...;
midiInOpen(#hMidiIn, uDeviceID, DWORD_PTR(#MyMidiInCallback), 0, CALLBACK_FUNCTION);
...
midiInClose(hMidiIn);
end;
In one file, I have a piece of code that goes like this:
// foo.pas
unit Foo;
type
FooExecutorMethod = function (param: Pointer) : Integer of Object;
FooExecutor = class
private
FMethod: TiTefExecutorMethod;
FMethodParam: Pointer;
FMethodResult: Integer;
public
function RunMethod(method: TiTefExecutorMethod; param: Pointer) : Integer;
end;
implementation
function FooExecutor.RunMethod(method: FooExecutorMethod; param: Pointer) : Integer;
begin
FMethod:= method; // Never gets assigned :(
FMethodParam:= param;
// Some threading logic here.
end;
In another file in the same project, I got something like this:
// bar.pas
uses Foo;
type
bar = class
protected
executor: FooExecutor;
function doSomething(params: Pointer): Integer;
Constructor Create(params: Pointer);
implementation
Function bar.doSomething(params: Pointer) : Integer;
begin
// Do something with the pointer
Result := 1;
end;
Constructor bar.Create(params: Pointer)
var
r: Integer;
begin
executor := FooExecutor.Create
r := executor.RunMethod(doSomething, params);
end;
Problem is, bar.DoSomething never gets executed. While debugging FooExecutor.RunMethod, the evaluator does not show a value for its "method" parameter - it shows the following instead:
E2035 Not enough actual parameters
And thus I am unable to use a reference to bar.DoSomething.
What am I doing wrong?
The method is never executed because you never call it. All you ever do is pass around a reference to the method. But you never call it.
You would call it like this:
function FooExecutor.RunMethod(method: FooExecutorMethod; param: Pointer): Integer;
begin
Result := method(param);
end;
Obviously your code is trying to do something else, but this is how to call the method.
I've got some problems with EnumWindows function that uses callback function from a nested class:
TProcessWatch = class(TObject)
private
...
type
TProcessInfo = class(TObject)
private
type
PEnumCallbackParam = ^TEnumCallbackParam;
TEnumCallbackParam = class
A : Integer;
...
end;
private
FOwner : TProcessWatch;
function FEnumWindowsCallback(hWindow : HWND; lParam : LPARAM) : BOOL; export;
procedure SomeProc;
...
end;
private
FProcesses : TProcessInfo;
...
public
...
Within the SomeProc there is a call to EnumWindows
EnumCallbackParam := TEnumCallbackParam.Create;
try
EnumCallbackParam.A := 0;
EnumWindows(#TProcessWatch.TProcessInfo.FEnumWindowsCallback, LongInt(#EnumCallbackParam));
...
finally
EnumCallbackParam.Free;
end;
And here is a FEnumWindowsCallback function listing:
function TProcessWatch.TProcessInfo.FEnumWindowsCallback(hWindow: HWND;
lParam : LPARAM): BOOL; export;
var
CallbackParam : PEnumCallbackParam;
begin
CallbackParam := Pointer(lParam); // A is inaccessible
Result := True;
...
end;
In runtime, when EnumWindows is called, FEnumWindowsCallback is always receiving hWindow = 0 and lParam is pointing to inaccessible value.
All this working fine if callback function is declared private in the form, but when I've tried to make this function private in nested class it went wrong.
Why? And how to make this working? The goal is to make FEnumWindowsCallback and all other involved functions private in TProcessWatch.
The callback is declared wrongly. It should be:
class function EnumWindowsCallback(hWindow: HWND;
lParam: LPARAM): BOOL; static; stdcall;
You were using the wrong calling convention, and an instance method.
Other comments:
EnumCallbackParam is already a pointer. You can pass it as the param.
Cast to LPARAM rather then LongInt so that you code will work if you ever compile to 64 bit.
The export keyword has no meaning in 32 or 64 bit Delphi. It is ignored and you should not use it since it adds clutter and may confuse.
Is there some trick how to get pointer of a member function in Lazarus / delphi?
I have this code which won't compile.... Error is
in Delphi:
variable required
in Lazarus:
Error: Incompatible types: got "<procedure variable type of function(Byte):LongInt of object;StdCall>" expected "Pointer"
The code:
TClassA = class
public
function ImportantFunc(AParameter: byte): integer; stdcall;
end;
TClassB = class
public
ObjectA: TClassA;
ImportantPtr: pointer;
procedure WorkerFunc;
end;
function TClassA.ImportantFunc(AParameter: byte): integer; stdcall;
begin
// some important stuff
end;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := #ObjectA.ImportantFunc; // <-- ERROR HERE
end;
Thanks!
A member function cannot be represented by a single pointer. It needs two pointers, one for the instance and one for the code. But that's implementation detail and you just need to use a method type:
type
TImportantFunc = function(AParameter: byte): integer of object; stdcall;
You can then assign ImportantFunc to a variable of this type.
Since you are using stdcall I suspect you are trying to use this as a Windows callback. That's not possible for a member function. You need a function with global scope, or a static function.
type
TImportantFunc = function(AParameter: byte): integer of object;stdcall;
ImportantPtr: TImportantFunc;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := ObjectA.ImportantFunc; // <-- OK HERE
end;
ObjectA.ImportantFunc is not a memory location, so address operator # can't be applied to it - hence compiler error. It is 2 pointers, #TClassA.ImportantFunc (method code) and ObjectA (Self argument). An answer to your question depends on what you really need - code pointer, Self, both or none.
If you need just to scope a function name use static class method
TClassA = class
public
class function ImportantFunc(Instance: TClassA; AParameter: byte): integer;
stdcall; static;
end;