www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How to introduce a callback function (class member) to win api ?

reply Shawn Liu <Shawn_member pathlink.com> writes:
import std.c.windows.windows;
class Test{
HANDLE hInstance;
int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) {return
0;}	
void init(){
WNDCLASS wc;
wc.hInstance = hInstance;
wc.lpfnWndProc = &windowProc;	// error !
// ... more
}
}
Mar 23 2005
next sibling parent J C Calvarese <jcc7 cox.net> writes:
Shawn Liu wrote:
 import std.c.windows.windows;
 class Test{
   HANDLE hInstance;
   int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) {return
   0;}	
   void init(){
     WNDCLASS wc;
     wc.hInstance = hInstance;
     wc.lpfnWndProc = &windowProc;	// error !
     // ... more
   }
 }

Unlike Java, D doesn't require (or even encourage) putting everything within an object class. In this case, I don't think you're even allowed to encase the functions in a class. Try something more like this: <code> import std.c.windows.windows; HANDLE hInstance; int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) { return 0; } void init(){ WNDCLASS wc; wc.hInstance = hInstance; wc.lpfnWndProc = &windowProc; // error ! // ... more } </code> If you haven't already, you should examine the \dmd\samples\d\winsamp.d example. -- Justin (a/k/a jcc7) http://jcc_7.tripod.com/d/
Mar 23 2005
prev sibling next sibling parent reply Russell Wilkins <russell.wilkins grovestarsoftware.com> writes:
The windowProc must use the windows calling convention, ie

extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, 
LPARAM lParam) {return 0;}	



Shawn Liu wrote:
 import std.c.windows.windows;
 class Test{
 HANDLE hInstance;
 int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) {return
 0;}	
 void init(){
 WNDCLASS wc;
 wc.hInstance = hInstance;
 wc.lpfnWndProc = &windowProc;	// error !
 // ... more
 }
 }
 
 

Mar 23 2005
next sibling parent reply Shawn Liu <Shawn_member pathlink.com> writes:
In article <d1tptk$1fas$1 digitaldaemon.com>, Russell Wilkins says...
The windowProc must use the windows calling convention, ie

extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, 
LPARAM lParam) {return 0;}	

Neither using the windows calling convention nor declared as static member can't not work, since it is a class member as J C Calvarese said "within an object class". But this is desired, since every instance of this class may access its own data member. Declaring it outside the class as a global function is not acceptable.
Mar 24 2005
next sibling parent "John C" <johnch_atms hotmail.com> writes:
"Shawn Liu" <Shawn_member pathlink.com> wrote in message 
news:d1tt2o$1jqe$1 digitaldaemon.com...
 In article <d1tptk$1fas$1 digitaldaemon.com>, Russell Wilkins says...
The windowProc must use the windows calling convention, ie

extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam,
LPARAM lParam) {return 0;}

Neither using the windows calling convention nor declared as static member can't not work, since it is a class member as J C Calvarese said "within an object class". But this is desired, since every instance of this class may access its own data member. Declaring it outside the class as a global function is not acceptable.

But Windows callbacks know nothing about classes, so passing a pointer to a non-static class member can't be expected to do anything but fail. You need to make the function static or global (with the appropriate calling convention) and store a reference to your class elsewhere -- e.g., in a hashtable, in the internal window data via GWL_USERDATA, or SetProp(). In C++ many people use thunks to work around this. MFC and ATL, for example, replace WNDPROC's hWnd parameter with a pointer to your class instance. C# does a lot of work behind the scenes to interop with native code, so there's built-in support for converting delegates to function pointers. Anyone know how it's done in Java?
Mar 24 2005
prev sibling next sibling parent Chris Sauls <ibisbasenji gmail.com> writes:
Shawn Liu wrote:
 In article <d1tptk$1fas$1 digitaldaemon.com>, Russell Wilkins says...
 
The windowProc must use the windows calling convention, ie

extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, 
LPARAM lParam) {return 0;}	

Neither using the windows calling convention nor declared as static member can't not work, since it is a class member as J C Calvarese said "within an object class". But this is desired, since every instance of this class may access its own data member. Declaring it outside the class as a global function is not acceptable.

Actually you don't have to make it global, just declare it private. And because private works on the module level, it will still have access to members of your class. To make sure it uses the correct instance of the class, use a lookup based on hwnd. Maybe something like: # private extern(Windows) # int windowProc (HANDLE hwnd, UINT msg, WPARAM wp, LPARAM lp) { # Test test = Test.instances[hwnd]; # if (test is null) { # throw new Exception("test.d: Invalid window handle"); # } # // ... other stuff ... # } # # class Test { # # private static Test[HANDLE] instances; # # public this() { # // ... # init(); # } # # public ~this() { # delete Test.instances[this.hwnd]; # } # # protected void init() { # // ... acquire hwnd somehow ... # Test.instances[this.hwnd] = this; # // ... # } # } -- Chris Sauls
Mar 24 2005
prev sibling parent Mike Parker <aldacron71 yahoo.com> writes:
Shawn Liu wrote:
 In article <d1tptk$1fas$1 digitaldaemon.com>, Russell Wilkins says...
 
The windowProc must use the windows calling convention, ie

extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, 
LPARAM lParam) {return 0;}	

Neither using the windows calling convention nor declared as static member can't not work, since it is a class member as J C Calvarese said "within an object class". But this is desired, since every instance of this class may access its own data member. Declaring it outside the class as a global function is not acceptable.

In C++ a common solution to this problem is to use the SetWindowLong API call to set the GWL_USERDATA to a pointer to your window object. Then in the message proc, you fetch the object pointer with GetWindowLong and call your custom message handler. You can read more about this technique here: http://www.gamedev.net/reference/articles/article1810.asp. The problem with this in D is that, according to the spec (http://www.digitalmars.com/d/garbage.html in the section 'Pointers and the Garbage Collector') casting a pointer to a non-pointer type is undefined behavior. I don't know of any other way to associate a message handler with a class instance. Since this is not feasible in D, you should instead take advantage of D's module scoping rules as suggested in another reply. Use the window handle as a key to an associative array containing your window instances. ******************************************************************** class MyWindow { // make this protected so that subclasses can override it in other modules protected int handleMessage(UINT msg, WPARAM wparam, LPARAM lparam) { // the Win32 API specifies that some 0 should be returned for most handled messages, but a handful should return 1. Return the appropriate value for handled messages, and -1 for those you don't handle return -1; } } // all new window objects should have a reference stored in this aa MyWindow[HWND] windowInstances; // because this function is in the same module as MyWindow, it has access to *all* of MyWindow's members and methods - public, protected, and private extern(Windows) int MyWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { MyWindow* myWin = (hwnd in windowInstances); int ret = 1; if(myWin !== null) { ret = myWin.handleMessage(msg, wparam, lparam); } // if the object didn't handle the message, hand off to the default window procedure if(ret == -1) return DefWindowProc(hwnd, msg, wparam, lparam); // the message was handled return ret; } ******************************************************************** If you don't need to ever subclass MyWindow, you can eliminate the handleMessage method altogether and manipulate the data directly from MyWindowProc. But it's probably best to go with the protected method just in case you one day decide you want to subclass it after all. Please note that I typed this code without testing it, so no guarantees that it compiles or works as is.
Mar 24 2005
prev sibling parent BERO <berobero users.sourceforge.net> writes:
Russell Wilkins wrote:
 The windowProc must use the windows calling convention, ie
 
 extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, 
 LPARAM lParam) {return 0;}   
 

wParam, LPARAM lParam) {return 0;} if you write it inside class
Mar 24 2005
prev sibling parent reply John Reimer <brk_6502 yahoo.com> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Shawn Liu wrote:
 import std.c.windows.windows;
 class Test{
 HANDLE hInstance;
 int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) {return
 0;}	
 void init(){
 WNDCLASS wc;
 wc.hInstance = hInstance;
 wc.lpfnWndProc = &windowProc;	// error !
 // ... more
 }
 }
 
 

Can't remember the original author of this one, but the topic showed up some time ago on this group. If the original author can step forward and identify himself as the creator of this nifty template, please do. I've attached the d example with this post. It basically uses a template class to attach a object method to a static function. The static function is then assigned to a windows struct as a callback. This still uses the deprecated casting style, but that's easily fixed. It's fairly straightforward. I'm sure, with some creativity, the idea could be improved upon. -JJR
Mar 24 2005
next sibling parent reply "John C" <johnch_atms hotmail.com> writes:
"John Reimer" <brk_6502 yahoo.com> wrote in message 
news:d20ceg$2067$1 digitaldaemon.com...
 Shawn Liu wrote:
 import std.c.windows.windows;
 class Test{
 HANDLE hInstance;
 int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) 
 {return
 0;}
 void init(){
 WNDCLASS wc;
 wc.hInstance = hInstance;
 wc.lpfnWndProc = &windowProc; // error !
 // ... more
 }
 }

Can't remember the original author of this one, but the topic showed up some time ago on this group. If the original author can step forward and identify himself as the creator of this nifty template, please do. I've attached the d example with this post. It basically uses a template class to attach a object method to a static function. The static function is then assigned to a windows struct as a callback. This still uses the deprecated casting style, but that's easily fixed. It's fairly straightforward. I'm sure, with some creativity, the idea could be improved upon. -JJR

--------------------------------------------------------------------------------
 class Callback(R, T1, T2, T3, T4) {

  private alias R delegate(T1, T2, T3, T4) Method;
  private extern (Windows) alias R function(T1, T2, T3, T4) Function;

  public this(Method method) {
    method_ = method;
  }

  public Function opCast() {
    return &callback;
  }

  public static extern (Windows) R callback(T1 arg1, T2 arg2, T3 arg3, T4 
 arg4) {
    return method_(arg1, arg2, arg3, arg4);
  }

  private static Method method_;

 };

 alias Callback!(int, Handle, uint, uint, int) WndProc;

 class Control {

  void registerClass() {
    WNDCLASS wc;
    wc.lpfnWndProc = (WNDPROC)(new WndProc(&windowProc));
  }

  private int windowProc(Handle handle, uint message, uint param1, int 
 param2) {
    // Now I have access to the class's non-static members. Hurrah.
    ...
  }
 }

Ah, I'm afraid it was me who posted that code. The problem is that each time you create a Callback instance, the static method_ field gets replaced, making it unsuitable for reuse. I haven't tries this yet, but it might work if each instance of Control had a unique ID which is assigned in registerClass() and associated with the window handle when it's created with GWL_ID. this(Method method, int id) { methods_[id] ~= method; } static extern (Windows) R callback(T1 arg1, T2 arg2, T3 arg3, T4 arg4) { return methods_[GetWindowLong(cast(Handle)arg1, GWL_ID)](arg1, arg2, arg3, arg4); } static Method[int] methods_; John.
Mar 25 2005
parent reply John Reimer <brk_6502 yahoo.com> writes:
John C wrote:

 Ah, I'm afraid it was me who posted that code. The problem is that each time 
 you create a Callback instance, the static method_ field gets replaced, 
 making it unsuitable for reuse.
 
 I haven't tries this yet, but it might work if each instance of Control had 
 a unique ID which is assigned in registerClass() and associated with the 
 window handle when it's created with GWL_ID.
 
       this(Method method, int id) {
           methods_[id] ~= method;
       }
 
       static extern (Windows) R callback(T1 arg1, T2 arg2, T3 arg3, T4 arg4) 
 {
           return methods_[GetWindowLong(cast(Handle)arg1, GWL_ID)](arg1, 
 arg2, arg3, arg4);
       }
 
       static Method[int] methods_;
 
 John. 
 
 

Hmmm... good catch. I never thought of that problem! The solution above, though a little busier, might just work. It really would be nice if there were a more straightforward solution. I think this problem is fairly common to bump into. Thanks for the ideas. - JJR
Mar 25 2005
parent reply "John C" <johnch_atms hotmail.com> writes:
"John Reimer" <brk_6502 yahoo.com> wrote in message 
news:d20lmo$2akm$1 digitaldaemon.com...
 John C wrote:

 Ah, I'm afraid it was me who posted that code. The problem is that each 
 time you create a Callback instance, the static method_ field gets 
 replaced, making it unsuitable for reuse.

 I haven't tries this yet, but it might work if each instance of Control 
 had a unique ID which is assigned in registerClass() and associated with 
 the window handle when it's created with GWL_ID.

       this(Method method, int id) {
           methods_[id] ~= method;


Of course, the above line should read: methods_[id] = method;
       }

       static extern (Windows) R callback(T1 arg1, T2 arg2, T3 arg3, T4 
 arg4) {
           return methods_[GetWindowLong(cast(Handle)arg1, GWL_ID)](arg1, 
 arg2, arg3, arg4);
       }

       static Method[int] methods_;

 John.

Hmmm... good catch. I never thought of that problem! The solution above, though a little busier, might just work. It really would be nice if there were a more straightforward solution. I think this problem is fairly common to bump into. Thanks for the ideas. - JJR

Note that some callbacks provide an extra parameter you can use to pass a pointer to your class instance, but WNDPROC doesn't. I think a built-in unified delegate/function is the answer. But until we get that in D, I hope someone smarter than me can work out an interim solution. Callbacks are such an integral part of Windows programming and it's a shame object-oriented languages generally don't provide a way to deal with them in an OO way. In C++ I could use thunks (at least in VC++), but I've not been able to translate them to D. C# does a lot of work behind the scenes to allow delegates to be used where C-style function pointers are expected. Delphi has a similar closure concept. Anyone know how Java copes with callbacks?
Mar 25 2005
parent Mike Parker <aldacron71 yahoo.com> writes:
John C wrote:
 "John Reimer" <brk_6502 yahoo.com> wrote in message 
 news:d20lmo$2akm$1 digitaldaemon.com...

 concept. Anyone know how Java copes with callbacks? 
 

Assuming you mean callbacks into the system API, such as Win32, it doesn't. All of that is hidden from you by the standard Java APIs. The only cases where you may encounter C-style callbacks is when using native code via JNI, but in that case you are using C or C++ anyway.
Mar 25 2005
prev sibling parent reply "Shawn Liu" <liuxuhong.cn gmail.com> writes:
"John Reimer" <brk_6502 yahoo.com> wrote :d20ceg$2067$1 digitaldaemon.com...
 Shawn Liu wrote:
 import std.c.windows.windows;
 class Test{
 HANDLE hInstance;
 int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) 
 {return
 0;}
 void init(){
 WNDCLASS wc;
 wc.hInstance = hInstance;
 wc.lpfnWndProc = &windowProc; // error !
 // ... more
 }
 }


I post this because I am reading the DWT and SWT source codes. SWT group implements this via a Callback class, but mostly depends on the JNI motheds in native dll file. I didn't read the c code yet. DWT has a way to map the delegates and extern(Windows). Delegates are stored in global arrays. This is not oo way. And I wonder if every instance of a window registers one or more callbacks, the global array may exhaust.
Mar 25 2005
parent John Reimer <brk_6502 yahoo.com> writes:
Shawn Liu wrote:
 "John Reimer" <brk_6502 yahoo.com> wrote :d20ceg$2067$1 digitaldaemon.com...
 
Shawn Liu wrote:

import std.c.windows.windows;
class Test{
HANDLE hInstance;
int windowProc (HANDLE hwnd, uint msg, WPARAM wParam, LPARAM lParam) 
{return
0;}
void init(){
WNDCLASS wc;
wc.hInstance = hInstance;
wc.lpfnWndProc = &windowProc; // error !
// ... more
}
}


I post this because I am reading the DWT and SWT source codes. SWT group implements this via a Callback class, but mostly depends on the JNI motheds in native dll file. I didn't read the c code yet. DWT has a way to map the delegates and extern(Windows). Delegates are stored in global arrays. This is not oo way. And I wonder if every instance of a window registers one or more callbacks, the global array may exhaust.

Yes, DWT uses a technique of mapping delegates to function pointers by using arrays of each and a mapping function to route the calls. It works but is laborious and a real eyesore (especially working with differing argument numbers). It's appropriate for the project. I just wish there were a simpler way to do this. -JJR
Mar 25 2005