View mode: basic / threaded / horizontal-split · Log in · Help
March 24, 2005
How to introduce a callback function (class member) to win api ?
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
}
}
March 24, 2005
Re: How to introduce a callback function (class member) to win api ?
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/
March 24, 2005
Re: How to introduce a callback function (class member) to win api ?
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
> }
> }
> 
>
March 24, 2005
Re: How to introduce a callback function (class member) to win api
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.
March 24, 2005
Re: How to introduce a callback function (class member) to win api ?
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;}   
> 
static extern(Windows) int windowProc (HANDLE hwnd, uint msg, WPARAM 
wParam, LPARAM lParam) {return 0;}

if you write it inside class
March 24, 2005
Re: How to introduce a callback function (class member) to win api
"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?
March 24, 2005
Re: How to introduce a callback function (class member) to win api
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
March 25, 2005
Re: How to introduce a callback function (class member) to win api
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.
March 25, 2005
Re: How to introduce a callback function (class member) to win api ?
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
March 25, 2005
Re: How to introduce a callback function (class member) to win api ?
"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.
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home