Jump to page: 1 2
Thread overview
How to introduce a callback function (class member) to win api ?
Mar 24, 2005
Shawn Liu
Mar 24, 2005
J C Calvarese
Mar 24, 2005
Russell Wilkins
Re: How to introduce a callback function (class member) to win api
Mar 24, 2005
Shawn Liu
Mar 24, 2005
John C
Mar 24, 2005
Chris Sauls
Mar 25, 2005
Mike Parker
Mar 24, 2005
BERO
Mar 25, 2005
John Reimer
Mar 25, 2005
John C
Mar 25, 2005
John Reimer
Mar 25, 2005
John C
Mar 25, 2005
Mike Parker
Mar 25, 2005
Shawn Liu
Mar 25, 2005
John Reimer
March 24, 2005
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
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
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
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
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
"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
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
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
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
"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