一个问题的答案,对MAKEOBJECTINSTANCE和DISPATCH方法感兴趣的来看看(0分)

D

darkiss

Unregistered / Unconfirmed
GUEST, unregistred user!
In article <3c091f64_2@dnews>, Litianyou wrote:
> There are so much amazing functions such as MakeObjectInstance ,Dispatch ,
> no more than 20 lines of asm code used nearly every where in VCL framework .
> I interested that why MakeObjectInstance appears in VCL and why its argument
> is a method of a class.

The purpose of the function to make it possible to use a class method as
message handler for an API window. Read Charles Petzold's "Programming
Windows" to learn about how Windows programming works on the API level. YOu
need to understand that if you want to make sense of the low-level stuff in
the VCL that deals with controls.

Basically each window is an instance of a *window class* on the API level. The
terminology used here by the API docs is very similar to that used in
object-oriented programming, but Windows is not an object-oriented framework
beneath the surface (yet, this may change with .NET). There are quite a number
of window classes already provided by the system (for all the standard and
common controls, for example) but an application can (and usually does)
register its own classes using the RegisterClass API function (not to be
confused with Classes.RegisterClass, which is a VCL function with a different
purpose). If you look at the win32 topic for RegisterClass you'll notice that
it takes a record (WNDCLASS) with a bunch of data that relates to the class.
The most important field of this record is lpfnWndProc, this field takes the
address of a window function for the class. The window function does all the
custom message processing for the window class, it is called by WIndows
whenver the window receives a message. There is a default window function
(DefWindowProc) defined in the API, there are special window functions used by
the diverse control classes, some also defined in the API (like DefEditProc).
A custom window function needs to pass all messages it does not handle itself
to the default window function.

The window function has to adhere to a specific declaration. If you were to
create an API-level window class in Delphi the window function would be
something like

Function MyWindowProc( wnd: HWND
msg: DWORD
wparam: WPARAM
lparam: LPARAM
): Longint
stdcall;
begin
Case msg Of
.... specific messages handled here
Else
Result := DefWindowProc( wnd, msg, wparam, lparam );
End;
end


It uses data types declared in the Windows unit. All four parameters boil down
to 4-byte types, basically LongInt or LongWord (equivalent to Integer or
Cardinal in 32 bit Delphi). The window function identifies the window to work
on by the passed window handle (wnd). An API-level program typically faces the
problem of storing data for a particular window, for which it can store a
pointer to a data record into the windows internal data structure using
SetWindowLong, for example.

The window function needs to be a standalone function, it cannot be a method
of a Delphi class. This is due to the fact that a method has an additional
hidden parameter, the Self reference for the object the method is to work on.
So the VCL has to solve the problem of how to tie a Delphi class to an API
window class and how to tie a specific instance of the Delphi class (an
object) to a specific instance of the Windows class (a window represented by a
window handle, a HWND).

How can the VCL find the object for a window, if all it gets in the window
function is the windows handle? MakeObjectInstance is the answer to this. When
you call it with a method reference the method pointer passed to the function
contains not only the methods address but also the address of the object (a
method pointer is 8 bytes in size, see TMethod in the online help). The
function uses this information to create a little piece of code dynamically.
The code contains the objects address, which is used as the Self parameter for
the method when it is called later. The function returns the address of the
generated code stub, which can then be used as a window function address for
the RegisterClass call (or to subclass an exisitng API window by replacing its
window proc). Windows then calls the code stub, the stub rearranges the
parameters into a TMessage record and calls the method pointer it has stored.
The objects method (TWinControl.MainWndProc in the VCL) calls the method
pointer stored by the WindowProc property. The default here is
TControl.WndProc but you can replace the content of WindowProc for a control,
which offers a means to "subclass" VCL controls without creating a derived
class, much like API-style subclassing (which replaces the window function for
a window) works. TControl.WndProc is frequently overriden in derived classes,
which typically end up calling it as the inherited method. TControl.WndProc
calls Dispatch to hand the message record to the basic (non-Windows) message
dispatcher build into TObject.

> Why Dispatch method can 'recogenize' a message not
> only defined by Windows but also by users and dispatch them to corresponding
> message handler .

Looks like you are a potential customer for books like Ray Lishners "Secrets
of Delphi 2" and "Delphi in a Nutshell" <g>. As a start read the Object Pascal
Language Guide (on your CD as PDF), especially the later chapters on memory
management and program control.

For each class that has a message handler the compiler will create a special
lookup table, the "dynamic method table" (DMT). A reference to the DMT is
stored into the class record, which is also created by the compiler for every
class and contains things like the virtual method table (VMT). The DMT stores
pairs of message IDs and method addresses, so finding a handler for a specific
message is a simple task of doing a linear search over the DMT. If no handler
is found the search continues in the parent classes DMT, and so on until
TObject is reached. If no handler has been found in any of the classes in the
hierarchy the DefaultHandler method is called. The routine doing this lookup
just used the first DWORD in the message record, so that has to be the
messages ID. Whatever additional data is stored in the message record is only
of interest for the method that then actually handles the message, so Dispatch
is not limited to TMessage.

Peter Below (TeamB) 100113.1101@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.
 
顶部