I have only tested on Win98.

I will be testing on NT4 and XP in the next couple days.

In the past, I went to a lot of trouble to destroy every other brush that might be created in compiled code, so I wanted to do the same with these special brushes used to color windows.

With the help I have received in this thread, doing so will be very easy.

Thanks to all.
Posted on 2004-07-02 02:14:41 by msmith
Donkey's help saved the day as usual, but his example does not save the brush handle and therefore it is not destroyed at the termination of the process.


Hi Mike,

I have been thinking about doing a resource tracker type program and thought I would study up on Objects, specifically GDI objects but also generally. As I thought the handles are freed automatically when your process terminates. However as I said originally, though it is not necessary it is good programming practice and important if you are creating and deleting objects during the run of your program, but once it's terminated the objects are all freed.

About Handles and Objects

Note When a process terminates, the system automatically closes handles and deletes objects created by the process. However, when a thread terminates, the system usually does not close handles or delete objects. The only exceptions are window, hook, window position, and dynamic data exchange (DDE) conversation objects; these objects are destroyed when the creating thread terminates.


An object for this article is defined as :

An object is a data structure that represents a system resource, such as a file, thread, or graphic image. An application cannot directly access object data or the system resource that an object represents. Instead, an application must obtain an object handle, which it can use to examine or modify the system resource.
Posted on 2004-07-04 11:38:19 by donkey
Hi Donkey,

Sounds safe, but methinks I have seen a number of inaccuracies in the MSDN docs.

Just last week you found this one:


This message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed. During the processing of the message, it can be assumed that all child windows still exist.


As you correctly pointed out, the parent window is the LAST to be destroyed, directly opposing what the docs say.

I have learned a lot from you over the last couple of years, but I have also leared that the MSDN docs are unreliable and should only be used as a guide (not the gospel). I have also learned and learned and learned that the Windows API is very unorthagonal and not very clean in its design.

Today I have been working on an intercept routine to make a private WindowProc for a given control, doing a SetWindowLong, and processing the raw events. It works very well, but some controls such as ragrid and frame (button with BS_GROUPBOX) don't see the mousedown event. I was hoping to avoid trackmouseevent. Any ideas?

Thanks,

Mike
Posted on 2004-07-04 18:01:36 by msmith
This message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed. During the processing of the message, it can be assumed that all child windows still exist.


Actually that is correct, the windows you are referring to as child windows do not have the WS_CHILD style set and therefore are not considered child windows but modal windows. *Real* child windows such as buttons etc.. receive the message after the parent is notified, however modal windows receive the message before their parent.

TrackMouseEvent would break Win95 compatibility and though that is much less important than it used to be it is something to think about none the less. The message is being sent somewhere, though ti is not processed. You can adapt RAGrid to pass the message but you would have to subclass the groupbox I think. You can also try a mouse hook.
Posted on 2004-07-04 18:20:25 by donkey
Humm, handles (well, objects) *should* be cleaned up automatically on app exit, and I haven't witnessed problems with this on NT. 9x is generally pretty fragile though - it might be worth having an app continously calling a child app that doesn't free it's resources, and see if it leaks.
Posted on 2004-07-04 18:45:14 by f0dder
Hi Donkey,

I am using:



invoke SetWindowLong, dword [edi+44],GWL_WNDPROC,dword [edi+496]


where edi+44 is the descriptor offset of the handle to an ragrid control. edi+496 is the descriptor offset holding the address of the WindowProc set up to grab raw messages from this control.

My <previous> understanding of this operation was that it would allow me to see messages for this control even before the control does. Not so?
Posted on 2004-07-04 18:49:04 by msmith
Hi F0dder,

I agree. Better safe than sorry. I have even tried deleting brushes from a NULL handle and nothing bad happens. This means I can delete all child window's brushes even if they weren't actually created. Each windows descriptor contains a brush at a particular offset, so I just do a DeleteObject on that handle ay WM_DESTROY time whether it needs it or not.

The docs even say that if the handle is invalid (surely a NULL handle is invalid), it will just return an error in eax.
Posted on 2004-07-04 18:56:11 by msmith
I agree as well, better safe than sorry, but as I said and all of my tests from NT4, 95, 98SE and 2K show that though it is good programming practice it is not necessary. And several places in Win32 documentation it states this explicitly.
Posted on 2004-07-04 19:26:03 by donkey
Hi Donkey,

Please tell me what you know about SetWindowLong as described above as far as intercepting messages meant for a control.

Mike
Posted on 2004-07-04 19:57:07 by msmith
Hi Mike,

You would use SetWindowLong to "subclass" or redirect the WndProc for a particular control, you can also do this for a complete class of controls through SetClassLong. Because the class information is not copied to your process until you actually create a window of that class, SetClassLong requires that a window handle be used to identify the class, precluding the GPF that would result from writing to class memory that doesn't exist.

Both SetWindowLong and SetClassLong work in essentially the same way, to subclass the control and intercept it's messages you would do the following...

invoke SetWindowLong, [hWnd], GWL_WNDPROC, offset NewWndProc

mov [OldWndProc], eax

Then you set up a normal looking WndProc for the control (or class)

NewWndProc FRAME hwnd, uMsg, wParam, lParam

; Process any messages here

invoke CallWindowProc, [OldWndProc], [hwnd], [uMsg], [wParam], [lParam]
RET
ENDF


Note that the OldWndProc must be called if you want to pass on any message to the control, to block the message from being sent to the control do not call OldWndProc.

This can be rather cumbersome if you wish to look at the same message for multiple controls of different types. To handle this I also occasionally use a global type subclassing that stores the OldWndProc in the GWL_USERDATA of the control, you would have to allocate a dword in the controls structure...

invoke SetWindowLong, [hWnd], GWL_WNDPROC, offset NewWndProc

invoke SetWindowLong, [hWnd], GWL_USERDATA, eax

NewWndProc FRAME hwnd, uMsg, wParam, lParam

; Process any messages here

invoke GetWindowLong, [hwnd], GWL_USERDATA
invoke CallWindowProc, eax, [hwnd], [uMsg], [wParam], [lParam]
RET
ENDF


There are some controls that do not work well with this method, a ListView for example.

Probably the best for your purposes is to set it up with SetClassLong, that way you can be assured that all "GroupBoxes" for example will be filtered through the subclassing procedure.
Posted on 2004-07-04 20:50:19 by donkey
Hi Donkey,


To handle this I also occasionally use a global type subclassing that stores the OldWndProc in the GWL_USERDATA of the control, you would have to allocate a dword in the controls structure...


Why do I need to allocate a dword for GWL_USERDATA?

It is my understanding that GWL_USERDATA is already allocated as a long in the structure and needs no allocation. BTW, I already use it for a pointer to my descriptor for each control.

The docs even point out that it is set to a zero.

I can't use SetClassLong because I need individual control.

I still cannot understand why the intercept doesn't occur in every case regardless of the control since we are supposed to be getting the event instead of the control. The only way the control should see the event is if we do a callWindowproc to pass the event the the intended recipient.

Thanks,

Mike
Posted on 2004-07-04 23:48:36 by msmith
As I said, I use GWL_USERDATA occasionally to hold the old WndProc address it is easy to keep track of that way but it does have problems. First Listviews seem to have a problem when being destroyed there are a few other problems so it is not a general purpose solution. For that reason I use the OldWndProc type one more often. You can use any storage scheme you like. But you must store the old address somewhere and call it with CallWindowProc. If you wish you can allocate a variable for it or store it in some kind of structure but it must be preserved and called.

Be sure that the control is set to receive the messages and events you are looking for by responding to WM_GETDLGCODE
Posted on 2004-07-04 23:54:13 by donkey