Solved

Convert method ptr to proc addr for callback

Posted on 1998-12-18
16
364 Views
Last Modified: 2010-04-04
I know this can be done. I remember seeing some code that DOES it in the VCL, but I'll be damned if I can find it again. Can anyone remember what this support function is called?
0
Comment
Question by:wamoz
  • 8
  • 4
  • 4
16 Comments
 
LVL 4

Expert Comment

by:dwwang
ID: 1351894
For an API to use your procedure as a callback function, you only need to pass its address to it:

procedure myproc(...);
begin
end;

MyAPI(@myproc,...)
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351895
I want to pass an object *method*.

Method pointers and procedure pointers are not compatible.

I quote the Delphi help:
"Global procedure pointer types and method pointer types are always mutually incompatible. In other words, a global procedure or function cannot be assigned to a method pointer variable, and a method cannot be assigned to a global procedure pointer variable."

Although the reason for this incompatibility is not stated, I can tell you that this is because calls to methods also pass context info - essentially a handle to the calling object, so that the method knows which instance data to use.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1351896
However, I see no much difference between them, this is from Delphi's VCL source:

  TClientDataSet = class(TDataSet)
  private
  ...
    function CalcFieldsCallBack(RecBuf: PChar): DBIResult; stdcall;
  ...
  end;

Check(FDSBase.SetFieldCalculation(Integer(Self), @TClientDataSet.CalcFieldsCallback));

Or maybe you can make it more clear?

Regards,
Wang
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351897
I want to pass an object *method*.

Method pointers and procedure pointers are not compatible.

I quote the Delphi help:
"Global procedure pointer types and method pointer types are always mutually incompatible. In other words, a global procedure or function cannot be assigned to a method pointer variable, and a method cannot be assigned to a global procedure pointer variable."

Although the reason for this incompatibility is not stated, I can tell you that this is because calls to methods also pass context info - essentially a handle to the calling object, so that the method knows which instance data to use.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351898
Whoops, sorry about the double post.

I think you may be onto something -

@myobj.methname may well resolve to a method call on a specific instance...

I'll try it and let you know.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1351899
By the way, if you call the function within an object's body,
you can still use @myProc, since it's identical to @myobj.myproc.

Regards,
Wang
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351900
I hope so, because I just tried @myobj.myproc and Delphi spat out a compiler error 'Variable required'.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351901
I finally worked out why @myobj.myproc is illegal.

The required syntax is @myCLASS.myproc
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 12

Expert Comment

by:rwilson032697
ID: 1351902
wamoz - you are almost correct. I hope this will clear it up:

Delphi has a type for method instances, its TMethod defined as a record with two fields: code and data both pointers. The reason for this (as you have already surmised) is that a method is (usually - I'll detail this out later) only sensible in the context of an object (= class instance), thus you need to know both where the method is in the class (the code pointer) and where the instance of the object is (the data pointer).

You could, using the RTTI, extract the TMethod information for a method in an object and pass the code member of that structure to the call back function. This would in fact be equivalent to your last comment (ie: @myCLASS.myproc).

However, there is one last gotcha (which is why I said usually above) and that is the callback should be a class method and it must not reference any data members (either directly or via other methods it calls).

I hope this helps.

Cheers, Raymond.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351903
Ray, it makes things clearer but it doesn't do me any good.

What I'm actually trying to *do* is turn a Delphi form into an AppBar. I found some info in MSDN in a Visual Studio topic called Application Desktop Toolbars, but as I may have remarked this is a not really designed for Delphi and in typical sloppy MS code style, uses a bunch of global variables.

I can do it like this, but if anyone tries to instantiate two copies of the form all hell will break loose due to the unmanaged violation of encapsulation.

As you are undoubtedly aware, a form is an object that instantiates a bunch of owned objects (controls) and the form unit is an instance auto-created on loading the unit due to the VAR clause in the interface section of the unit.

I'm trying to implement the business of registering and managing the form window as an appbar using SHAppbarMessage() in methods of the form class.

What's messing me up is the need to pass a callback address for notification messages to whatever it is that manages appbars.

Any suggestions?

I have just noticed that the callback is told the hWnd of the window in question so I suppose I can cope, I just think it's a shambles of a way to write code.
0
 
LVL 12

Accepted Solution

by:
rwilson032697 earned 50 total points
ID: 1351904
Ah - I see now! No wonder you have a problem here...

If you are referring to the uCallbackMessage member of the APPBARDATA structure passed to the SHAppbarMessage() API call then it isn't a callback function, but a call back message that is sent to the window you give it in the HWND member.

 (I have not tried this but it should do the trick)

Declare a new message:

const
  MYCALLBACKMSG = WM_USER + 1000;

declare a handler for it in the app bar form class:

  procedure AppBarCallBack(var Message : TMessage); message MYCALLBACKMSG;

then in its implementation you do what you need to do:

procedure TAppBarFormAppBarCallBack(var Message : TMessage);

begin
. // There's may be a special message type for these callbacks. If so replace TMessage with it in the declaration.
end;

I think this will sort you out!

Cheers,

Raymond.

For reference here is the API help on the APPBARDATA structure:

typedef struct _AppBarData { // abd

    DWORD  cbSize;           // sizeof(APPBARDATA)
    HWND   hWnd;             // handle of appbar
    UINT   uCallbackMessage; // see below
    UINT   uEdge;            // see below
    RECT   rc;               // see below
    LPARAM lParam;           // see below
} APPBARDATA, *PAPPBARDATA;
 

Contains information that the system uses to process appbar messages.

Members

uCallbackMessage

Application-defined message identifier. The application uses the specified identifier for notification messages that it sends to the the appbar identified by the hWnd member. This member is used when sending the ABM_NEW message.

uEdge

Flag that specifies an edge of the screen. This member can be one of the following values:

ABE_BOTTOM      Bottom edge
ABE_LEFT      Left edge
ABE_RIGHT      Right edge
ABE_TOP      Top edge
This member is used when sending the ABM_GETAUTOHIDEBAR, ABM_QUERYPOS, ABM_SETAUTOHIDEBAR, and ABM_SETPOS messages.

rc

RECT structure that contains the bounding rectangle, in screen coordinates, of an appbar or the Windows taskbar. This member is used when sending the ABM_GETTASKBARPOS, ABM_QUERYPOS, and ABM_SETPOS messages.

lParam

Message-dependent value. This member is used with the ABM_SETAUTOHIDEBAR message.

Remarks

This structure is used with the SHAppBarMessage function.

See Also

SHAppBarMessage


0
 
LVL 4

Expert Comment

by:dwwang
ID: 1351905
Raymond is right.

However, the problem is because of the question you asked, if you initially ask about the SHAppbarMessage(), I can have the correct answer too :-)

Below is a fully working code in Delphi(from UDDF):

--------------------------------------------------------------------------------------

// This needs to be in your public declarations @ the top of the pas file
procedure TForm1.IconCallBackMessage( var Mess : TMessage ); message WM_USER + 100;

procedure TForm1.FormCreate(Sender: TObject);
var
   nid : TNotifyIconData;
begin
     with nid do
     begin
           cbSize := SizeOf( TNotifyIconData );
           Wnd := Form1.Handle;
           uID := 1;
           uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
           uCallbackMessage := WM_USER + 100;
           hIcon := Application.Icon.Handle;
           szTip := 'This is the hint!';
     end;
     Shell_NotifyIcon( NIM_ADD, @nid );
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
   nid : TNotifyIconData;
begin
     with nid do
     begin
           cbSize := SizeOf( TNotifyIconData );
           Wnd := Form1.Handle;
           uID := 1;
           uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
           uCallbackMessage := WM_USER + 100;
           hIcon := Application.Icon.Handle;
           szTip := 'This is the hint!';
// All the above is probably not needed.
     end;
     Shell_NotifyIcon( NIM_DELETE, @nid );
end;

procedure TForm1.IconCallBackMessage( var Mess : TMessage );
var
   sEventLog : String;
begin
     case Mess.lParam of
// Do whatever you wish here. For example popup up a menu on a right click.
          WM_LBUTTONDBLCLK  : sEventLog := 'Left Double Click';
          WM_LBUTTONDOWN    : sEventLog := 'Left Down';
          WM_LBUTTONUP      : sEventLog := 'Left Up';
          WM_MBUTTONDBLCLK  : sEventLog := 'M Dbl';
          WM_MBUTTONDOWN    : sEventLog := 'M D';
          WM_MBUTTONUP      : sEventLog := 'M U';
          WM_MOUSEMOVE      : sEventLog :=  'movement';
          WM_MOUSEWHEEL     : sEventLog := 'Wheel';
          WM_RBUTTONDBLCLK  : sEventLog := 'r dbl';
          WM_RBUTTONDOWN    : sEventLog := 'r down';
          WM_RBUTTONUP      : sEventLog := 'r up';
     end;
end;
--------------------------------------------------------------------------------

Regards,
Wang
0
 
LVL 1

Author Comment

by:wamoz
ID: 1351906
Ray,

I've got it to work and the doco is VERY misleading.
It *is* a callback proc address, there are about four messages it passes and the way I got the MS code to work sensibly was to create a unit and treat it as a notional Flyweight object for which all state is extrinsic (qv Design Patterns).

Actually I think this is what the coder had in mind. Percieved like that it is much less a mess and doesn't depend on the class of the form, so maybe I owe someone an apology.

Now I have a new issue - full-window drag causes horrible flickering when docking because my code detects end-of-window-drag via WM_WINDOWPOSCHANGED and you get one of those for every redraw under full-window drag.

I'd really like to discuss this with someone - you, preferably, since you seem to comprehend API programming and to be moderately iinterested in the question.

I'm quite happy to share my code with you. My email address is peterw@wamoz.com, so if you're prepared to engage in a private email exchange please drop me a line.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1351907
Hi wamoz.

Its interesting that you say the callback is a proc address. Is this a dual purpose field (dwwang posted complete source which treats it as a window message...)

I am not an API guru, though I am happy to contribute in any way I can.

As an initial thought, you could intercept the WM_ERASEBKGRND (spelling?) message and clobber it while dragging. This should have the effect of eliminating the flicker (assuming it is caused by continual redrawing while dragging the window).

Cheers,

Raymond.

0
 
LVL 1

Author Comment

by:wamoz
ID: 1351908
I finally have the horrid thing working.

MS uses a callback to deliver messages because some of the messages must be handled synchronously and using SendMessage would lock up the message queue.

In a sense I have provided my own workaround to the initally stated problem. I know there is an answer to the question because I have seen it, but nobody's been forthcoming in that respect, and we've all moved on from there.

I have created a project that does nothing but manage a form as an AppBar. It can dock/undock, has inertia and correctly performs various housekeeping required by Windows, such as converting certain WM messages to ABM messages and forwarding them via SHAppBarMessage, and hiding the AppBar during desktop tiling and cascading operations.

The code is a bit epic to post here, so I've made it available from my website:

http://www.wamoz.com/wombat/applicat.htm

By the way, chaps, my name is Peter.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1351909
Peter: I'll understand if you reject my answer - it sounds like you had a really torrid time with this!

Cheers,

Raymond.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now