(同步个人博客 http://sxysxy.org/blogs/70 到csdn)
回顾
上节解决了只用ruby语言制作窗口过程的问题。只用ruby语言的话,可以用局部变量_self = self,然后在Fiddle::Closure的匿名子类里面的call方法,借助_self变量访问ruby世界的”窗口对象”。
但是用C语言的话,怎么办呢?
CreateWindowEx这个函数
最后一个参数,lpParam。一般我们都把它设为0,也就是无用。但是现在他就要派上用场了。这个参数可以传递一个值,这个值会在窗口创建WM_CREATE消息发生时又传递给窗口,而窗口拥有一个存放用户自定义数据的地方,可以用SetWindowLong(GWL_USERDATA)来设置用户数据。于是我们就可以把ruby对象的指针的值放到用户自定义数据里面…打开XYGui源代码的xy_mainwindow.rb,发现
def create
@handle = WinAPI.call("user32", "CreateWindowEx", 0 | @ex_style, @className, @title,
@style,
@x, @y, @width, @height,
0, 0,
@app.instance, selfval)
end
selfval是个c扩展:
VALUE XYWidget_selfval(VALUE self)
{
return INT2NUM(self);
/*
In ruby, INT2NUM(self) is the real value of self
*/
}
然后窗口过程(XYGui_ext.c:
static LRESULT CALLBACK XYWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
VALUE __arg1__;
VALUE __arg2__;
unsigned __tmp1__;
unsigned __tmp2__;
char *__tmpcptr1__;
HDC hdc;
//Store the 'self' when creating the window
VALUE self;
if(uMsg == WM_CREATE)
{
self = (VALUE)(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLong(hWnd, GWL_USERDATA, (LONG)self);
}
self = (VALUE)GetWindowLong(hWnd, GWL_USERDATA);
switch(uMsg)
{
....
这样我们就可以在c代码里面得到ruby的对象了。然后一切就都好办了。
记得注册窗口类,XYApp是怎么回事我下节会讲:
static LRESULT CALLBACK XYWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static VALUE XYApp_registerClass(VALUE self)
{
WNDCLASS wc;
RtlZeroMemory(&wc, sizeof(wc));
wc.lpfnWndProc = XYWndProc; // XYWndProc !
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = RSTRING_PTR(rb_iv_get(self, "@name"));
wc.hInstance = GetModuleHandle(NULL);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
RegisterClass(&wc);
return self;
}
然后请读者参考xy_window的实现,自己做一个对窗口的简单封装。