Below is a simple program that creates a window that only has a bunch of labels and text boxes on it to display some of the various window creation parameters passed to the window procedure from the CreateWindowEx() call. Compile and run the following program, CreateStruct.bas. You'll find adequate comments within the program's code describing how it works. We'll continue with the discussion afterwards...
#Compile Exe "CreateStruct" 'CreateStruct.bas
#Include "Win32api.inc"
'Labels
%IDC_LABEL1 = 1500 'These are equates and serve as
%IDC_LABEL2 = 1505 'control identifiers for the
%IDC_LABEL3 = 1510 'various controls used in a
%IDC_LABEL4 = 1515 'program. This particular program
%IDC_LABEL5 = 1520 'only has one main window that
%IDC_LABEL6 = 1525 'contains nine labels and nine
%IDC_LABEL7 = 1530 'text boxes (edit controls). You
%IDC_LABEL8 = 1535 'don't need to make window handles
%IDC_LABEL9 = 1540 'global in a program because if
'you know a control's parent and
'Edit Controls 'its control id you can get its
%IDC_EDIT1 = 1550 'window handle through a call tp
%IDC_EDIT2 = 1555 'GetDlgItem(hParent,%CONTROL_ID)
%IDC_EDIT3 = 1560
%IDC_EDIT4 = 1565
%IDC_EDIT5 = 1570
%IDC_EDIT6 = 1575
%IDC_EDIT7 = 1580
%IDC_EDIT8 = 1585
%IDC_EDIT9 = 1590
Type WndEventArgs 'Type for passing window procedure parameters.
wParam As Long 'Allows me to shorten parameter list. .NET does
lParam As Long 'this all the time. See, for example pea for
hWnd As Dword 'PaintEventArgs in the OnPaint()Message Handler.
End Type
Type MyUserData 'Here is a type I created just to show you how to pass
iAnyOldInt As Long 'control/program specific user data from a point in a
szAnyOldString As Asciiz*32 'program where a CreateWindowEx() call is being made to
End Type 'the location in a program where the WndProc() resides
Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long
Local lpCreateStruct As CREATESTRUCT Ptr
Local ptrMud As MyUserData Ptr
Local hCtrl As Dword 'Right here we are in a message
Local hInst As Dword 'handler for the %WM_CREATE
'message, and the %WM_CREATE
lpCreateStruct=Wea.lParam 'message will only be received
hInst=@lpCreateStruct.hInstance 'once for each window that is
ptrMud=@lpCreateStruct.lpCreateParams 'being created. In other words,
'there is a one to one relationship
hCtrl= _ 'First Label 'between a CreateWindow() Call and
CreateWindowEx _ 'a %WM_CREATE message. The last
( _ 'parameter in the Window Procedure
0, _ 'is lParam. It was transported
"static", _ 'here as part of the WndEventArgs
"@lpCreateStruct.@lpszName", _ 'type - Wea.lParam. This
%WS_CHILD Or %WS_VISIBLE, _ 'parameter's meaning during a
10,10,200,25, _ 'WM_CREATE message is a pointer to
Wea.hWnd, _ 'a CREATESTRUCT type that Windows
%IDC_LABEL1, _ 'itself filled out at the time of
hInst, _ 'the CreateWindowEx() call down in
Byval 0 _ 'WinMain. The way we 'get at' that
) 'data here is to declare a pointer
'to a CREATESTRUCT type. You will
hCtrl= _ 'First edit 'be able to find this type in
CreateWindowEx _ 'Win32Api.inc. Note the very first
( _ 'local variable in this procedure
%WS_EX_CLIENTEDGE, _ 'Local lpCreateStruct As CREATESTRUCT ptr.
"Edit", _ 'You'll note the very first line
@lpCreateStruct.@lpszName, _ 'in this procedure sets this
%WS_CHILD Or %WS_VISIBLE, _ 'variable to the address contained
220,10,200,25, _ 'in Wea.lParam. At this point then
Wea.hWnd, _ 'we are able to access every member
%IDC_EDIT1, _ 'of this type here because
hInst, _ 'PowerBASIC knows how a CREATESTRUCT
Byval 0 _ 'is laid out, and so can access
) 'its member variables through an
'ordinary variable of this type,
hCtrl= _ 'Second Label 'or through a pointer to this type
CreateWindowEx _ 'as is the case here. Further,
( _ 'the first field or member
0, _ 'variable of the CREATESTRUCT
"static", _ 'type is itself a pointer
"@lpCreateStruct.@lpszClass", _ 'lpCreateParams'. Down in
%WS_CHILD Or %WS_VISIBLE, _ 'WinMain() a variable of type
10,45,200,25, _ 'MyUserData was declared and
Wea.hWnd, _ 'member iAnyOldInt was set to
%IDC_LABEL2, _ '12345 and member szAnyOldString
hInst, _ 'was set equal to "Any Old String".
Byval 0 _ 'The variable itself was named
) 'mud which is short for MyUserData.
'The term Varptr(mud) was placed in the last parameter of the CreateWindowEx()
'call, and this of course would be the address of that variable - which is a
'pointer.
hCtrl= _ 'Second edit 'So, that address will show up
CreateWindowEx _ 'here in fnWndProc_OnCreate() as
( _ 'the first member variable of the
%WS_EX_CLIENTEDGE, _ 'CREATESTRUCT type pointed to by
"Edit", _ 'the Wea.lParam variable. That
@lpCreateStruct.@lpszClass, _ 'does sound rather complicated but
%WS_CHILD Or %WS_VISIBLE, _ 'it really doesn't have to be
220,45,200,25, _ 'unless you make it so! You will
Wea.hWnd, _ 'note though that we did have to
%IDC_EDIT2, _ 'create a pointer to a MyUserData
hInst, _ 'type to get at this information
Byval 0 _ 'just as we had to create a pointer
) 'to a CREATESTRUCT to get at the
'overall containing type. Although
'this program looks long it is very
hCtrl= _ 'Third Label 'repetitious in that most of it is
CreateWindowEx _ 'just code for creating nine
( _ 'labels and nine textboxes. That's
0, _ 'what all this code over at the
"static", _ 'left is about. It shouldn't be
"@lpCreateStruct.hInstance", _ 'anything you don't understand at
%WS_CHILD Or %WS_VISIBLE, _ 'this point if you have followed
10,80,200,25, _ 'my tutorials up to this point.
Wea.hWnd, _ 'One thing I did here that I'd
%IDC_LABEL3, _ 'like to finally point out to you
hInst, _ 'because I feel it may contain
Byval 0 _ 'possibilities for confusion
) 'concerns the difference between
'accessing a character string
hCtrl= _ 'Third edit 'contained within a user defined
CreateWindowEx _ 'type and accessing a character
( _ 'string through a pointer member
%WS_EX_CLIENTEDGE, _ 'of a user defined type. Note that
"Edit", _ 'the very first bit of info that
Str$(@lpCreateStruct.hInstance), _ 'displays on the program's Form
%WS_CHILD Or %WS_VISIBLE, _ 'is the name of the app which is
220,80,200,25, _ 'defined down in WinMain as
Wea.hWnd, _ '"CreateStruct". It is accessed
%IDC_EDIT3, _ 'here through the term - '
hInst, _ '@lpCreateStruct.@lpszName. The
Byval 0 _ 'reason for the first '@' symbol
) 'is because lpCreateStruct is a
'pointer and we want what is stored at the address held in the pointer. The
'reason for the 2nd '@' symbol is exactly the same. The lpszName member of
'the type doesn't hold a string
hCtrl= _ 'but rather a pointer to a string.
CreateWindowEx _ 'Contrast this with the MyUserData
( _ 'type I created for this program.
0, _ 'If you look at the 2nd member of
"static", _ 'this type you'll see the variable
"@lpCreateStruct.cx", _ 'szAnyOldString As Asciiz*32. In
%WS_CHILD Or %WS_VISIBLE, _ 'this case the string contains 32
10,115,200,25, _ 'bytes of space where a string of
Wea.hWnd, _ 'up to 31 characters plus a null
%IDC_LABEL4, _ 'byte may be stored. This is
hInst, _ 'entirely different from the
Byval 0 _ 'lpszName and lpszClass member
) 'variables of the CREATESTRUCT type
'where each of these two variables
hCtrl= _ 'Fourth edit 'only occupies 4 bytes - just
CreateWindowEx _ 'enough for a 32 bit pointer on 32
( _ 'bit systems. So you see a type
%WS_EX_CLIENTEDGE, _ 'can actually hold memory to
"Edit", _ 'contain an Asciiz string, or it
Str$(@lpCreateStruct.cx), _ 'can contain just a pointer member
%WS_CHILD Or %WS_VISIBLE, _ 'that contains just the integer
220,115,200,25, _ 'address of a string stored
Wea.hWnd, _ 'somewhere else. If a user defined
%IDC_EDIT4, _ 'type actually stores the whole
hInst, _ 'string itself it is accessed like
Byval 0 _ 'so - @ptrMud.szAnyOldString. No
) '2nd '@' symbol is needed. If,
'however, the type only contains a pointer to a string stored somewhere else
'then the 2nd '@' symbol is needed - @lpCreateStruct.@lpszName.
hCtrl= _
CreateWindowEx _
( _
0, _
"static", _
"@lpCreateStruct.cy", _
%WS_CHILD Or %WS_VISIBLE, _
10,150,200,25, _
Wea.hWnd, _
%IDC_LABEL5, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
%WS_EX_CLIENTEDGE, _
"Edit", _
Str$(@lpCreateStruct.cy), _
%WS_CHILD Or %WS_VISIBLE, _
220,150,200,25, _
Wea.hWnd, _
%IDC_EDIT5, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
0, _
"static", _
"@lpCreateStruct.x", _
%WS_CHILD Or %WS_VISIBLE, _
10,185,200,25, _
Wea.hWnd, _
%IDC_LABEL6, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
%WS_EX_CLIENTEDGE, _
"Edit", _
Str$(@lpCreateStruct.x), _
%WS_CHILD Or %WS_VISIBLE, _
220,185,200,25, _
Wea.hWnd, _
%IDC_EDIT6, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
0, _
"static", _
"@lpCreateStruct.y", _
%WS_CHILD Or %WS_VISIBLE, _
10,220,200,25, _
Wea.hWnd, _
%IDC_LABEL7, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
%WS_EX_CLIENTEDGE, _
"Edit", _
Str$(@lpCreateStruct.y), _
%WS_CHILD Or %WS_VISIBLE, _
220,220,200,25, _
Wea.hWnd, _
%IDC_EDIT7, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
0, _
"static", _
"@ptrMud.iAnyOldInt", _
%WS_CHILD Or %WS_VISIBLE, _
10,255,200,25, _
Wea.hWnd, _
%IDC_LABEL8, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
%WS_EX_CLIENTEDGE, _
"Edit", _
Str$(@ptrMud.iAnyOldInt), _
%WS_CHILD Or %WS_VISIBLE, _
220,255,200,25, _
Wea.hWnd, _
%IDC_EDIT8, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
0, _
"static", _
"@ptrMud.szAnyOldString", _
%WS_CHILD Or %WS_VISIBLE, _
10,290,200,25, _
Wea.hWnd, _
%IDC_LABEL9, _
hInst, _
Byval 0 _
)
hCtrl= _
CreateWindowEx _
( _
%WS_EX_CLIENTEDGE, _
"Edit", _
@ptrMud.szAnyOldString, _
%WS_CHILD Or %WS_VISIBLE, _
220,290,200,25, _
Wea.hWnd, _
%IDC_EDIT9, _
hInst, _
Byval 0 _
)
fnWndProc_OnCreate=0
End Function
Function fnWndProc_OnDestroy(Wea As WndEventArgs) As Long
Call PostQuitMessage(0)
fnWndProc_OnDestroy=0
End Function
Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
Local Wea As WndEventArgs
Select Case As Long wMsg
Case %WM_CREATE
Wea.hWnd=hWnd : Wea.wParam=wParam : Wea.lParam=lParam
fnWndProc=fnWndProc_OnCreate(Wea)
Exit Function
Case %WM_DESTROY
Call PostQuitMessage(0)
fnWndProc=fnWndProc_OnDestroy(Wea)
Exit Function
End Select
fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
Local winclass As WndClassEx
Local szAppName As Asciiz*16
Local mud As MyUserData
Local Msg As tagMsg
Local hWnd As Dword
mud.iAnyOldInt=12345 'We've declared a type variable of MyUserData just above
mud.szAnyOldString="Any Old String" 'named 'mud', which is short for MyUserData. It contains
szAppName="Pointers" 'an arbitrary integer and an arbitrary string so as to
winclass.cbSize=SizeOf(winclass) 'show how to pass window creation data to a window
winclass.style=%CS_HREDRAW Or %CS_VREDRAW 'procedure through a CreateWindowEx() call. Up in
winclass.lpfnWndProc=CodePtr(fnWndProc) 'fnWndProc_OnCreate() I give a detailed explanation of
winclass.cbClsExtra=0 'how this information is extracted there. Note the last
winclass.cbWndExtra=0 'parameter of the CreateWindowEx() call contains the term
winclass.hInstance=hIns 'VarPtr(mud). This is how the address of this data is being
winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION) 'passed to the Window Procedure
winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
winclass.hbrBackground=%COLOR_BTNFACE+1
winclass.lpszMenuName=%NULL
winclass.lpszClassName=VarPtr(szAppName)
winclass.hIconSm=LoadIcon(hIns, ByVal %IDI_APPLICATION)
RegisterClassEx winclass
hWnd=CreateWindowEx(0,szAppName,"CreateStruct",%WS_OVERLAPPEDWINDOW,200,100,445,360,0,0,hIns,Byval Varptr(mud))
Call ShowWindow(hWnd,iShow)
While GetMessage(Msg,%NULL,0,0)
TranslateMessage Msg
DispatchMessage Msg
Wend
Function=msg.wParam
End Function
This program - CreateStruct.bas, certainly shows you how to put label and text box child window controls on a Form/Dialog, but I hope you get more from it than that. On a certain basic level it is about pointers and the CREATESTRUCT type Windows uses to maintain window data. On a deeper level though I would like you to start thinking about our use of the CreateWindowEx() call in WinMain() which creates our main program window, and the CreateWindowEx() calls in the Window Procedure that creates all the child windows such as the labels and text boxes. You should have noted that for our main program window we had to fill out the fields of a WNDCLASSEX type, RegisterClassEx() that type variable, and finally make a call to CreateWindowEx() with the third parameter the registered class name, i.e., "CreateStruct" in the above example. When we did that we also had to create a window procedure to process windows messages for that window.
When you look at similar CreateWindowEx() calls in the Window Procedure that create child windows you see that we were able to skip all these steps and simply pass the name of the class we wanted to instantiate to the CreateWindowEx() function in the third parameter to the call, and we got our label, text box, or whatever with no further work required on our part. We didn't have to register any classes, create any window procedures, or anything like that to get these windows. Obviously, these controls are built into Windows so we can just use them with little work on our part, other than learning about their different functionalities made accessible to us through their various window style parameters.
This is all well and good but I don't want things to be too easy for you! This particular tutorial is about pointers, dynamic memory allocation and 'instance data', and at least in terms of the last, I'm willing to bet you may not even know what 'instance data' is! So here is my plan. Let's try to actually construct one of these child window controls we just used in CreateStruct.bas. 'Which one, you ask'? Well, we used labels and edit controls, so those are the choices. The edit controls would be too hard, so lets look at labels, as uninteresting as labels are.
So start CreateStruct up and take a close look at the labels. What do you see? Actually, you don't even see separate windows for each label, all you really see is a text string. Well, that's about all there really is to a label, just a text string drawn in a child window atop the parent located at the position specified by the x,y coordinate pair in the CreateWindowEx() call that creates the label. In terms of drawing a text string on a window, if you recall back in both Form2.bas and again in Form3.bas we covered drawing text on a window in considerable detail. If your memory is growing dim revisit that material briefly, particularly the TextOut() Api function that actually makes it happen.
So what must be done if we want to make our own label instead of using the "static" window class that Windows has provided for us? Well, first thing is we'll need to register a window class just like we've done for all the main program windows we've created so far in this tutorial. And registering a window class necessitates the provision of a Window Procedure to handle messages for our 'custom' window class. I'm excited about this project. Lets see how far we can get. Lets first create a main window, kind of just a 'Hello, World!' program. Here would be that, at least...
#Compile Exe "Label1.exe"
#Include "Win32api.inc"
Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
Select Case As Long wMsg
Case %WM_DESTROY
PostQuitMessage 0
fnWndProc=0
Exit Function
End Select
fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
Local winclass As WndClassEx
Local szAppName As Asciiz*16
Local Msg As tagMsg
Local hWnd As Dword
szAppName="Label1"
winclass.cbSize=SizeOf(winclass)
winclass.style=%CS_HREDRAW Or %CS_VREDRAW
winclass.lpfnWndProc=CodePtr(fnWndProc)
winclass.cbClsExtra=0
winclass.cbWndExtra=0
winclass.hInstance=hIns
winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
winclass.hbrBackground=%COLOR_BTNFACE+1
winclass.lpszMenuName=%NULL
winclass.lpszClassName=VarPtr(szAppName)
Call RegisterClassEx(winclass)
hWnd=CreateWindowEx(0,szAppName,"Label1",%WS_OVERLAPPEDWINDOW,200,100,325,300,0,0,hIns,ByVal 0)
Call ShowWindow(hWnd,iShow)
While GetMessage(Msg,%NULL,0,0)
TranslateMessage Msg
DispatchMessage Msg
Wend
Function=msg.wParam
End Function
Compile and run that and you should get a pretty boring basic blank window that however, will serve as the parent for our label which we now try to create. As I mentioned above our first step is to attempt to register a window class for our label. Lets assign a class name of "MyLabel". Also, we'll need to provide a window procedure for the class, and lets name that fnLabelWndProc. Finally, lets create and register the class during processing of the WM_CREATE message for the main window. Below is the program I just described. Please compile and run Label2.bas...
#Compile Exe "Label2.exe" 'Label2.bas Just registers a "MyLabel" window
#Include "Win32api.inc" 'class and tests to be sure it registers OK.
Function fnLabelWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
fnLabelWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
Select Case As Long wMsg
Case %WM_CREATE
Local hInst As Dword
Local lpCreateStruct As CREATESTRUCT Ptr
Local szClassName As Asciiz*16
Local wc As WndClassEx
lpCreateStruct=lParam
hInst=@lpCreateStruct.hInstance
szClassName="MyLabel" : wc.lpfnWndProc=CodePtr(fnLabelWndProc)
wc.lpszClassName=VarPtr(szClassName)
wc.style=%CS_HREDRAW Or %CS_VREDRAW
wc.cbClsExtra=0 : wc.cbWndExtra=0
wc.hInstance=hInst
wc.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
wc.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
wc.hbrBackground=%COLOR_BTNFACE+1
wc.lpszMenuName=%NULL
wc.cbSize=SizeOf(wc)
If IsFalse(RegisterClassEx(wc)) Then
MsgBox("We've Failed In Our Attempt To Register Our MyLabel Class!!!")
End If
Case %WM_DESTROY
PostQuitMessage 0
fnWndProc=0
Exit Function
End Select
fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
Local winclass As WndClassEx
Local szAppName As Asciiz*16
Local Msg As tagMsg
Local hWnd As Dword
szAppName="Label2"
winclass.cbSize=SizeOf(winclass)
winclass.style=%CS_HREDRAW Or %CS_VREDRAW
winclass.lpfnWndProc=CodePtr(fnWndProc)
winclass.cbClsExtra=0
winclass.cbWndExtra=0
winclass.hInstance=hIns
winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
winclass.hbrBackground=%COLOR_BTNFACE+1
winclass.lpszMenuName=%NULL
winclass.lpszClassName=VarPtr(szAppName)
Call RegisterClassEx(winclass)
hWnd=CreateWindowEx(0,szAppName,"Label2",%WS_OVERLAPPEDWINDOW,200,100,325,300,0,0,hIns,ByVal 0)
Call ShowWindow(hWnd,iShow)
While GetMessage(Msg,%NULL,0,0)
TranslateMessage Msg
DispatchMessage Msg
Wend
Function=msg.wParam
End Function
When you ran this program it looked the same as what we had with Label1.bas, and that was a blank screen. Well, give me a chance! We're still building! What have we accomplished so far though? Well, we added WM_CREATE processing code to the Main Window's Window Procedure, and within that code we managed to register a window class that would be our new "MyLabel" class. And we know it registered OK because when we ran it we didn't get the message box stating that RegisterClassEx() failed (see the msgbox() above). Finally, we provided an empty window procedure for our new "MyLabel" class. We had to do that or the program would not have compiled.
OK, so what's the next step then in getting this newly created class to produce a label for us? Well, a class doesn't really produce any output. In terms of the oft used analogy in object oriented programming the class is the cookie cutter and the object is the cookie. All the class is, is a template out of which actual and specific instances of windows are created. So we need to make an actual window of this new class with the CreateWindowEx() function call. So lets think about that some. Referring back to CreateStruct.bas and the various CreateWindowEx() calls we made to create the various labels in that program we realize that we want our label control to work pretty much just like other windows controls. We'll want to pass into our label the text that is to appear in the label through that third 'szCaption' parameter. Well, lets try adding a couple CreateWindowEx() calls to the program we have so far. If you look at the parameters you'll see we need a control id for each label so I've added %IDC_LABEL1 and %IDC_LABEL2 to the top of the program. Also, lets add a %WS_BORDER style to the typical %WS_CHILD Or %WS_VISIBLE styles so we'll see the windows if they are even created. Try Label3.bas below...
#Compile Exe "Label3.exe" ‘Actually attempts to create two labels of our
#Include "Win32api.inc" ‘custom label class. There is no text yet in
%IDC_LABEL1 = 1201 ‘the labels but at least they are visible.
%IDC_LABEL2 = 1202
Function fnLabelWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
fnLabelWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
Select Case As Long wMsg
Case %WM_CREATE
Local hInst As Dword
Local lpCreateStruct As CREATESTRUCT Ptr
Local szClassName As Asciiz*16
Local wc As WndClassEx
Local hLabel1 As Dword, hLabel2 As Dword
lpCreateStruct=lParam
hInst=@lpCreateStruct.hInstance
szClassName="MyLabel"
wc.lpfnWndProc=CodePtr(fnLabelWndProc)
wc.lpszClassName=VarPtr(szClassName)
wc.style=%CS_HREDRAW Or %CS_VREDRAW
wc.cbClsExtra=0
wc.cbWndExtra=0
wc.hInstance=hInst
wc.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
wc.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
wc.hbrBackground=%COLOR_BTNFACE+1
wc.lpszMenuName=%NULL
wc.cbSize=SizeOf(wc)
If IsFalse(RegisterClassEx(wc)) Then
MsgBox("We've Failed In Our Attempt To Register Our MyLabel Class!!!")
fnWndProc=-1
Exit Function
End If
hLabel1= _ 'Try To Make 1st Label
CreateWindowEx _
( _
0, _ 'No Extended Styles
"MyLabel", _ 'Class Name We Conjured Up
"Label1", _ 'Caption or Window Text To Be In Label
%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER, _ 'Lets add %WS_BORDER to see if this is working
20,10,50,20, _ 'Where the label should show up on the Form
hWnd, _ 'Parent is main window
%IDC_LABEL1, _ 'Control ID for label (see equates)
hInst, _ 'We got this from Window Creation CREATESTRUCT
ByVal 0 _ 'We're not passing any special window creation data
)
hLabel2= _ 'Try To Make 2nd Label
CreateWindowEx _
( _
0, _ 'No Extended Styles
"MyLabel", _ 'Class Name We Conjured Up
"Label2", _ 'Caption or Window Text To Be In Label
%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER, _ 'Lets add %WS_BORDER to see if this is working
20,45,50,20, _ 'Where the label should show up on the Form
hWnd, _ 'Parent is main window
%IDC_LABEL2, _ 'Control ID for label (see equates)
hInst, _ 'We got this from Window Creation CREATESTRUCT
ByVal 0 _ 'We're not passing any special window creation data
)
fnWndProc=0
Exit Function
Case %WM_DESTROY
PostQuitMessage 0
fnWndProc=0
Exit Function
End Select
fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function
Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
Local winclass As WndClassEx
Local szAppName As Asciiz*16
Local Msg As tagMsg
Local hWnd As Dword
szAppName="Label3"
winclass.cbSize=SizeOf(winclass)
winclass.style=%CS_HREDRAW Or %CS_VREDRAW
winclass.lpfnWndProc=CodePtr(fnWndProc)
winclass.cbClsExtra=0
winclass.cbWndExtra=0
winclass.hInstance=hIns
winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
winclass.hbrBackground=%COLOR_BTNFACE+1
winclass.lpszMenuName=%NULL
winclass.lpszClassName=VarPtr(szAppName)
Call RegisterClassEx(winclass)
hWnd=CreateWindowEx(0,szAppName,"Label3",%WS_OVERLAPPEDWINDOW,200,100,325,300,0,0,hIns,ByVal 0)
Call ShowWindow(hWnd,iShow)
While GetMessage(Msg,%NULL,0,0)
TranslateMessage Msg
DispatchMessage Msg
Wend
Function=msg.wParam
End Function
Well, what do you think? Are we making progress or not? The CreateWindowEx() call must be succeeding, right? We did get windows created because you must have seen the outlines of the labels due to the addition of that %WS_BORDER style? OK, we got label child windows, so what's next? We certainly need the label text right? If we can get that we've succeeded! OK, we know how to output text to a window because we've spent a lot of time studying Form2.bas and Form3.bas in this tutorial, right? Right! So then we have some more difficult questions to answer. First is where are we going to put a TextOut() call to draw the label's text? Could we put it right after the RegisterClassEx() function call where we registered the "MyLabel" class? Yes, we could, but if we are going to do that then why are we even going to the trouble of creating a label custom control that will act and behave like other standard windows controls in that the data used by the control is passed in during a CreateWindowEx() call? If we are going to do that we might as well dispense with the whole idea of a custom class and just use TextOut() directly on the window for a label to the text boxes instead of creating a control. OK, we want to make a label control so as to get on with this tutorial and find out whatever it is that I'm trying to teach here.