I'm trying to code my first assembler (MASM32) program, and I need help...
-- by the way: excuse me for silly questions (I'm a beginner) & excuse me for my bad english (I'm Italian).

What the program should do:
take an array of longs from another program and represent them as a graph in a window

I've chosen to make a DLL (so it's simple to pass data, for the caller) which declares it's own Window Class, creates a window and has it's own Window Procedure.

Since there can be more than one instance of the graph-window, the array of points cannot be global, it has to be local to the window procedure, otherwise every window'd display the same graph.

Some_EXE
|
| array_of_Y_points (data to be passed from the exe to the dll)
|
Chart_DLL (my program)
DrawChart (function called by Some_EXE)
|
? array_of_Y_points
|
WndProc (window procedure)



Problem:
How do I pass the array_of_Y_points from DrawChart to WndProc?


My solution:
I stored a pointer (pY) to the array in the window extra bytes (cbWndExtra) using:

invoke SetWindowLong, hWnd,0,pY ; write pY in cbWndExtra
invoke GetWindowLong, hWnd,0 ; read pY from cbWndExtra

What I get is an Access Violation ERROR!
everything's ok as long as I OPEN or RESIZE graph-windows, but if I close one... mayhem
actually, if I've opened windows 1,2 and 3, and now close window 3, nothing happens but if I close window 1 or 2 and try to resize another: error.

Should I use HeapCreate & HeapAlloc and make a copy of the entire array?
Isn't there a simple solution where I have NOT to copy the array?
And anyway, why does my solution not work?

Thank you very much!
Posted on 2003-04-18 16:06:44 by MayBe
Is the window created with cbWndExtra memory allocated ? You can also pass the pointer to the array in the GWL_USERDATA nIndex:

invoke SetWindowLong, hWnd,GWL_USERDATA,pY
invoke GetWindowLong, hWnd,GWL_USERDATA
Posted on 2003-04-18 16:15:49 by donkey
A standard template i like to do in practice with window procedures is:
; ####### WINDOW INSTNACE PRIVATE MEMORY ###############


PMem STRUC
hParent dd ?
hChild dd ?
Moving_x dd ?
Moving_y dd ?
KillTimerFlag dd ?
IsOpen dd ?
CloseBox RECT <>
PMem ENDS

; ####### WINDOW INSTANCE PROCEEDURE ###############

WindowProc PROC USES EDI ESI EBX hWnd:HWND, uMsg:DWORD, wParam:DWORD, lParam:DWORD

; Get the Private Memory pointer back in EBX (reserved register for this)
[b]invoke GetWindowLong, hWnd, GWL_USERDATA
mov EBX, EAX[/b]

.if uMsg == WM_CREATE

; Allocate Private Memory as defined in the structure
[b]invoke GetProcessHeap
invoke HeapAlloc, eax, NULL, SIZEOF PMem
mov EBX, EAX
invoke SetWindowLong, hWnd, GWL_USERDATA, EAX[/b]

; any other inits

.elseif uMsg == WM_DESTROY

[b]invoke GetProcessHeap
invoke HeapFree, EAX, NULL, EBX[/b]

.elseif uMsg == ...

; Get instance specific memory back (example use)
mov edx, [ebx].PMem.hParent

.elseif uMsg == ...

; Set instance specific memory (example use)
mov [ebx].PMem.hChild, NULL

.elseif uMsg == ...

.endif

ret
WindowProc ENDP


Its an excellent and easy way of not using Global memory. Its well suited for Custom controls, since each control will operate with the same Window Proceedure, but must have memory private, and unique to the window (and its handle).

:alright:
NaN
Posted on 2003-04-18 23:28:54 by NaN
Isnt ebx get trashed at WM_DESTROY ?.I guess we should store allocated memory handle at global variable
Posted on 2003-04-19 10:22:47 by LaptoniC
I think you are missing one of essential facts about DLLs here:

In each DLL attached to a process the code section is the same (well mapped) but the .data section is LOCAL to the process :)

the only problem will be IF you want to have multple charts from the same exe...

But then just allocate some memory for the points each time and increment a "global" counter and keep a link array between each data set and window handles
Posted on 2003-04-19 10:44:46 by BogdanOntanu
LaptoniC, No not at all.

Every time the proc is entered the "USES" statement pushes EBX ESI and EDI. This preserves system values for these registers, and opens the playing field for use to use them freely.

The only time EBX has false information from the GetWindowLong is when the window is being created (WM_CREATE). There is not heap memory allocated as of yet. But Ironically, the WM_CREATE does just this and saves the pointer to SetWindowLong, as well as corrects the EBX register for any remaining work in the startup.

On WM_DESTROY messages, the EBX is fist set from the stored heap memory pointer, and the WM_DESTROY message is handled. EBX still has the proper pointer to memory. This is why i reserve ebx for this. Its now becomes a pointer to private memory on a per instance basis. The segragation is done by GetWindowLong's hWindow parameter. You put a different window handle, and you get a different window's heap memory. Since windows gives the proc the proper handle each time an event happens, this is always in sinc, and will always retrieve the proper heap memory from the instance that the event occurs to.

Hope this clears things up a bit!
:alright:
NaN
Posted on 2003-04-19 10:46:57 by NaN
Thanks for explaining :alright:
Posted on 2003-04-19 14:23:13 by LaptoniC
Thank you very much, everybody!

I will try to do as you wrote, NaN.
Posted on 2003-04-22 15:46:18 by MayBe
Be a bit careful with this approach, though - at least with dialog routines. As far as I've been able to see (only by quick tests, no deep research at all), you get a couple of messages before WM_INITDIALOG - at least a WM_POWER (I think). So you can't, for instance, just check for WM_INITDIALOG and otherwise jump blindly to a dispatch table gotten from GetWindowLong :^)
Posted on 2003-04-22 16:05:21 by f0dder
I was digging into the internals of WTL/ATL recently and discovered that it has a very nice and clean way to associate a C++ class with a specific window.
What it does is to register the class with a 'startup wndproc', which gets called as soon a window instance is created. This startup proc reads from a global, shared data structure to get the class pointer which was temporarily stored there (one pointer per thread), then writes a piece of assembly code to a part of the class (mov dword ptr , pThis / jmp actual_wndproc), and moves the wndproc pointer of that window to that piece of code.
This way each window has its unique wndproc pointer that points to a piece of code that modifies the hWnd parameter to the right 'this' pointer. Then it just calls the original wndproc, but now *with* a this pointer.
Here's a nice article explaining it in detail:
http://staff.develop.com/onion/Articles/cpprep0399.htm

Thomas
Posted on 2003-04-22 16:56:51 by Thomas