Programming Windows 95 and Windows NT

History

April 2005: This document was written in 1999 and not updated since. Maybe it is still of some value to someone and since it was a lot of work I decided to still keep it on the new website. I did not change anything of the content but I am planning to check that the associated sourcecode still works and maybe even update it.

August 1999: After one year this is the second version. Since I got a second child, another job and many other things to do I only made it to chapter 8. I improved the documentation by commenting the examples. As before this concerns only those parts special to Eiffel or Visual Eiffel. Now chapters 1 thru 8 are covered. The code now is organized in 4 main parts: the example directories, the XWINLIB directory, the XPOOL directory and the PTZLD_LIB directory. I tried to put some common things in ancestcor classes because it is supports learnability without making the C and Eiffel versions uncomparable.

August 1998: First version covering chapters 1 thru 4

Abstract

This document describes the Project of translating the C examples of Charles Petzold's book "Programming Windows 95" 4th edition into Eiffel using Visual Eiffel from Object Tools. (Any constructive critique welcome)

Introduction

Because many people in the Visual Eiffel mailing list and other people said Charles Petzold's book (Programming Windows 95) is a must if one wants to write programs or understand how this OS works I decided to buy the current version of it.

Some years ago at Windows 3.0 times I had to make a presentation about writing a GDI program. I was shocked when I saw that I had to write 3 pages of source code just to show a simple empty window. Because of the near and far pointers and the segments I have never been very keen to write those C-programs under Windows. That's the reason why I did not spend more effort learning this stuff those days.

With the flat memory model it is much easier (even in C) to write programs for Windows. So when I saw the first Petzold example (a Hello World version) I decided to try it with Visual Eiffel.

I think Petzold makes his example programs interesting by using features one wouldn't expect. The "Hello World" program doesn't just print Hello Windows 95 on a window. It also plays a wave file which says Hello Windows 95. I was especially interested if I could play this wave file using VE. With the help of Roger Browne I made it. Thank you Roger !

After this I thought it would be a very good motivation for learning to program the Win32 API if I could do it with Visual Eiffel rather than with C. I was also wondering if there would be any limits say that I could do something in C but not in Eiffel. Fortunately up to now I managed to do all of them in Eiffel.

So I tried to reprogram every example of Petzold's book in Visual Eiffel. To be honest: at the moment not everyone is done. Chapter 1 thru 8 are finished. I would be glad if there is anyone interested to take over some other examples of the book.

If you want to examine or use the examples you should have Petzold's book. I won't give any explanations of the code or how the programs work because he did it already. What I will document here is just the special things involved to translate them to Visual Eiffel.

I tried to keep the Visual Eiffel versions of the examples as close as possible to the original C-examples to facilitate the comparison of the two source texts. In many situation one thinks that it would be better to do it in a more object oriented way. I didn't do that because it's done already in GRAPE.

For the second version I tried to put some common things in ancestcor classes because it is supports learnability without making the C and Eiffel versions uncomparable.

Libraries and ancestor classes

The examples make heavy use of library WINLIB. One of the few OO things I am using is a general application class. When you look at the C-examples you recognize very soon that about 2 to 3 pages of the source code are almost the same in every example. And in every new example they have to be there again. This really cries for OO and so I created an application class from which almost every example application class inherits from. This class and some other helper classes are in a common cluster which I called PTZLD_LIB.

While converting the C-code to VE I recognized that some WIN32 API functions (WIN 32 it is really huge) and data structures are not yet in WINLIB. Thus I created XWINLIB which implements those things I missed while implementing the examples hoping that some day they will go into WINLIB and I can remove them. Since the first version Object Tools already did a good job here and even removed bugs wich have been in earlier Versions.

BTW: It would be good to have Version 3 of Visual Eiffel and even better to have a professional or power version because in some examples the code then can be 'beautyfied'. But the light version will be fine as well.

The Examples

I won't repeat Petzold's text here (read the book). I just describe the things that were necessary to change or extend to get the examples running in Eiffel.

Some fundamental things are described in section General Hints. The description to the examples have links into that section where appropriate.

Chapter 2 : Hello World

See introduction about playing a wav file. It uses the Win32 API PlaySoundA to achieve this.

Chapter 3 : Sysmets 1

no comment yet.

Chapter 3 : Sysmets 2

In user_wnd_proc I used an if-elseif-...-end construct to handle the different cases like sbc.SB_LINEUP and so on. I think in the first version there was a comment in the code saying that I get 'invalid_inspect_value' errors at runtime when I tried to use the inspect approach.

At that time I didn't know what this message meant and simply used if's. In the meanwhile I found out that this behaviour is actually defined by Eiffel the Language itself. It means that an inspect should get well defined values which are taken care of by the corresponding when clauses.

If none of the when's can handle the value then the else branch if existing will be used. Actually this is a very good behaviour of Eiffel because the programmer really is driven to think about the cases which could occur. Of course one can still cheat by using the else clause.

In our case Windows sends all kinds of messages (mostly not interesting to us) to our routine and we will get a lot of these invalid_inspect_value errors if we use the inspect without default. With this knowledge I converted my if branch back to an inspect construct and appended an empty else branch. You will see this approach appear in many of the following examples.

Chapter 3 : Sysmets 3

no comment yet.

Chapter 4 : Devcaps 1

no comment yet.

Chapter 4 : Linedemo

no comment yet.

Chapter 4 : Bezier

One interesting point here is how we check if a bit is set or not. See Further Tips and Tricks/Checking bits in this document

Chapter 4 : Whatsize

Here we make use of class SCIENTIFIC_FORMAT which helps us a lot to translate the original printf statement.

Chapter 4 : Randrect

-- 22.06.1999: removed peekmessage from this class since it is now in WINLIB.WAPI_MESSAGE_ AND_MESSAGE_QUEUE_FUNCTIONS

Chapter 4 : Clover

No comments yet.

Chapter 4 : Endjoin

in wm_create I use manifest arrays. Note that these start by default at index 1. This is the reason why I use the +1 in (iEnd.item(i+1) of wm_paint for example.

Chapter 4 : Scramble

It seems that lockWindowUpdate(wGetDesktopWindow) unfortunately causes the application to hang on NT 4.0. I don't know why (and did not investigate too much). It doesn't seem to hang on Win95. As an improvement to the last version the platform is checked at runtime in order to behave properly:

if not is_platform_win32_nt then
  lockWindowUpdate(wGetDesktopWindow)
  end
										

is_platform_win32_nt is implemented in class X_WAPI_ADD_WIN32API which get this information calling the Win32 API getVersionEx. lockWindowUpdate is supposed to deactivate actualisation of certain windows temporarily. I have the feeling that lockWindowUpdate works properly but wGetDesktopWindow does not.

But with the workaround we still have a problem: After scramble is finished with drawing the desktop is screwed up. You have to minimize and maximize an application to have a proper screen again. Any tips welcome !

Chapter 4 : Metafile

Uses the old playMetafile.

Chapter 4 : EMF 1

Uses the new playEnhMetaFile.

Chapter 4 : EMF 2

Saves the enhanced metafile to a file on disk using createEnhMetaFile and closeEnhMetaFile.

Chapter 4 : EZFont

not a real example. It is used in example Justify 1

Chapter 4 : Justify 1

The justify routine differs from the original C-routine. It makes use of some better Eiffel features which makes the source much more readable. Don't make life more complicated as it is ;-)

Chapter 5 : Sysmets

There are only a few changes to Sysmets of chapter 3. The changes support the keyboard as input.

Chapter 5 : Keylook

Displays the codes of the keyboard.

Chapter 5 : Typer

Basic Editor.

Chapter 5 : Connect

No comments yet.

Chapter 6 : Checker 1

No comments yet.

Chapter 6 : Checker 2

Same as Checker 1 but with keyboard support (arrow keys, space bar).

Chapter 6 : Checker 3

Same as Checker 1 but uses subwindows (see createWindow command in wm_create). win_main is overridden here to register the window prcedure for the subwindows:

win_main(appName, appTitle, className, wndProc : POINTER;
  style : BIT 32) is
  do
  register_child_class (ChildNameSTR.to_external, $child_wnd_proc, style)
  precursor(appName, appTitle, className, wndProc, style)
  end										
  

Try to resize the window. It takes much more time compared to the other two checker versions. It is more obvious on slower systems like a 133Mhz Pentium of course. Performance is much worse.

Chapter 6 : Blokout 1

No comments yet.

Chapter 6 : Blokout 2

No comments yet.

Chapter 7 : Beeper 1

Overrides is_app_startable to try to create the timers before the messageloop starts.

Chapter 7 : Beeper 2

No comments yet.

Chapter 7 : Digclock

The difficult thing here was routine set_international. On Windows 95 we read win.ini (like in the original C-source) to get the locale information. On NT we read it through the Win32-API. Play around with control panel/regional settings to see how the display changes. (not every format works).

About every 10 seconds the clock hangs for about a second and then skips a second about 3 seconds later. I don't know why. Setting the timer to 500 msec didn't help. Turning off garbage collection didn't help either. I don't have a clue. If you have please let me know.

Chapter 6 : Anaclock

Like in Digclock every 10 seconds the clock hangs. Same problem. I converted some typical C-Programmer behaviour stuff to Eiffel: routines get_rotated_point, rotate_points usage of 3 arrays of WAPI_POINTs for the 3 hands of the clock which makes routine draw_hands much more readable.

Chapter 7 : Bounce

No comment yet.

Chapter 8 : BtnLook

There are two ways Petzold gets the handle to the Application Instance. Both are done in wm_create:

  • ((LPCREATESTRUCT)lParam)->hInstance
  • (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE)

For simplicity I use the second version in my examples which look like this in Eiffel: api.getWindowLong(hwnd, gwl.GWL_HINSTANCE)

Using the first version is rather complicated because of the typecast. But for those who'd like to go the stony way (and those who want to prove there's no limits for Eiffel) there is a solution for this as well (although it uses memcpy which isn't very Eiffelish (alternatives welcome as usual)):

It would work in exactly the same way as I did it in the next example 'OwnerDrw'. You would have to define a class LPCREATESTRUCT, have a local expanded variable of this class and copy the memory from lParam->hInstance to that variable.

Chapter 8 : OwnerDrw

For the event wm.WM_DRAWITEM I had to do something special:

(see source code !)in wm.WM_DRAWITEM the lParam contains (in INTEGER form) a pointer to a WAPI_DRAW_ITEM_STRUCT structure. At the moment I don't have any idea how to get drw to reference the memory that lParam points to. The trick I use instead is I copy the memory lParam points to to $drw. 48 bytes are copied which is the size of this 'struct'. This is done with the help of MEMORY.mem_copy.

If you have the professional version of VE then there is a more elegant way to do it. See the comments in the source for a detailed description.

Chapter 8 : Colors 1

The special thing here again is window subclassing.

  ptr.set_item($scroll_proc)
  h := api.setWindowLong(
    hwndScroll.item(i),
    gwl.GWL_WNDPROC,
    ptr.to_integer)
  OldChildWndProc.put(h, i)

The pointer to the original window procedure of each scrollbar is stored in OldChildWndProc and at the same time the pointer of routine scroll_proc is assigned. This means that from now on scroll_proc is used for the event handling of the scroll bars.

In scroll_proc we now have the chance to do something on our own. But to reuse the original functionality (like drawing the scrollbar) we call the original routine at the end of scroll_proc :

  Result := callWindowProc(
    OldChildWndProc.item(i),
    hwnd, message, wParam, lParam)

Chapter 8 : PopPad1

This demonstrates the 32K limit of the edit class:

1. open the file text.txt which is smaller than 32KB with notepad or another editor. copy and paste the text into the edit field of Poppad1. It works.

2. open the file text_big.txt which is bigger than 32KB with notepad or another editor. copy and paste the text into the edit field of Poppad1. The text is cut and an error message "Edit control out of space" appears.

Chapter 8 : Environ

I had a hard time to find out how to get all existing environment strings in the windows world. I use the Win32 API GetEnvironmentStrings now instead of the C runtime function.

In WM_COMMAND we are using another trick (have a look at the code):

We are using a trick here: in the C version szBuffer (pointer to char array) is used to let Windows put the selected text there. In Eiffel (as far as I know) it is not possible to use something like s.ptr, $s or s.area. One of the reasons is that a STRING can be resized and so the memory position (pointer) may not always be the same.

Thus we use a buffer with a BIT array

  Buf : BIT 32776 -- (MAXENV + 1)*8=4097*8=32776

to get a pointer to it.

'p := $buf'

doesn't work, so we use:

   'p.set_item($buf)'

'sendMessage' expects INTEGERS so I use 'p.to_integer' as argument. Finally we have to get the text out of this BIT buffer which is easy: 's.from_c(p)'

Chapter 8 : Head

If you try to get the selected text of a listbox you cannot do this in a direct way like this:

s  : STRING
r := api.sendMessage( hwndList, lbm.LB_GETTEXT, r, (s.to_c).to_integer)

Instead you have to go over a buffer:

p  : POINTER
s  : STRING
p.set_item($buf)
r := api.sendMessage(hwndList,
 lbm.LB_GETTEXT,
 r, p.to_integer)
s.from_c(p)
.
.
.
buf : BIT  65536 -- 8192*8

General Hints

Calling WIN32 API

In Visual Eiffel you call a Win32 routine with the external "WINAPI" form.
Example:

wStrokePath(hdc : INTEGER) : INTEGER is
    external "WINAPI"
    alias "StrokePath"
    end

This way you build an interface to the Win32 API routine. You can call the Eiffel routine wStrokePath which in turn calls the Win32 routine specified by the alias which is StrokePath here. Important: the routine name of alias (alias "StrokePath") is case sensitive. Alias "strokePath" would not work !

"What is this w in wStrokePath for ?" you will ask. It is used by many features of Visual Eiffels Winlib library. Most of the Win32 routines return an integer value which says whether the routine was successful or not.

This means that like in traditional programming every routine call should be embedded in an if then else clause (which is done rarely by most people). Visual Eiffel uses exceptions to avoid this. This has the disadvantage for the library designer/programmer to code two eiffel routines for each Win32 routine but the advantage is that the application programmer can use exceptions instead of if's. Here is an example of such a routine pair:

strokePath(hdc : INTEGER) is
    require
    correct_hDC    :  hDC /= NULL_
    do
    if wstrokePath(hDC) = FALSE_ then
    	error.raise ("WinAPI 'StrokePath' failed")
    	end
    end

wStrokePath(hdc : INTEGER) : INTEGER is
    external "WINAPI"
    alias "StrokePath"
    end

Now it's clear why the w. It stands for the interface to the Windows routine. The feature with the same name but without the w is the routine you should access as an application programmer from a VE program.

You should export the wroutine only to the current Class (this is not done at the moment for the examples provided but it should be). Sometimes it is necessary to return a value. Such a routine pair would look like this:

pathToRegion(hdc : INTEGER) : INTEGER is
    require
    correct_hDC    :  hDC /= NULL_
    do
    Result := wPathToRegion(hdc)
    if Result = FALSE_ then
        error.raise ("WinAPI 'PathToRegion' failed")
        end
    end

wPathToRegion(hdc : INTEGER) : INTEGER is
    external "WINAPI"
    alias "PathToRegion"
    end

ANSI and UNICODE strings

(Hint originally from Roger Browne)
Some routines take strings as arguments. This could be a filename for example. Windows uses two versions for such routines. One that takes ASCII Strings and one that takes UNICODE strings (also called wide characters).

The ASCII version always ends with an A. Visual Eiffel uses ASCII strings at the moment.
This routine is used in the Hello World example of chapter 2.

playsoundA(pszSound : POINTER; hModule : POINTER; fdwSound : BIT 32) : BOOLEAN is
    external "WINAPI"
    alias "PlaySoundA"
    end

Using special import libraries

(Hint originally from Roger Browne)
Only the most common import libraries are shipped with VE - advapi32.imp, comdlg32.imp, gdi32.imp and kernel32.imp. If you want others, e.g. winmm.lib for multimedia functions, you can copy it from your other development tools like VC++ or download them all in an 8MB package from

The .imp files are generated by Object Tools's lib tool and are actually the same as the .lib ones. If you have just the .lib file you can copy it to the Visual Eiffel bin directory and give it a .imp extension. In the project options insert the filename in the object files entry field. http://www.microsoft.com/msdownload/platformsdk/bkdenv.htm

Passing arrays of structures to a Win32 routine

(Hint originally from Sergei S.Ivanov)
Some routines expect an array of structures. The first example is SINEWAVE that needs to do it. It takes an array of WAPI_POINTs and draws a sin curve. At first it didn't work at all.

Then I remembered that Sergei S.Ivanov answered to a similar question concerning a COM component that expected a pointer to an array of data structures. SINWAVE isn't a COM component but it has the same problem. The solution is not to use an ARRAY [WAPI_POINT] but an ARRAY [expanded WAPI_POINT].

Passing Eiffel Strings and other parameters to a Win32 routine

Some Win32 routines expect a C-pointer as one of their arguments. Visual Eiffel provides several ways to get such a pointer from an Object:
to_c, to_external, to_ptr, ptr or the $ sign which do all the same. The many names exist mainly for historical reasons. Kernel classes conform to the ELKS standard and have to_c - from_c pair.

local
    s       : STRING
    a       : ARRAY[expanded WAPI_POINT]
    x, y    : INTEGER
    w       : WAPI_POINT
    rect    : WAPI_RECT
do
-- strings --
!!s.adapt("Hello World")
f(s.to_c) -- calling Win32 routine with to_c

-- arrays --
!!a.make(0, 99)
from x := 0 until i = 100 loop
    y := x*x
    w.set(x, y)
    a.put(w, x)
    x := x + 1
    end
drawCurve(hdc, a.to_c)                 -- to_c

-- structure --
    api.getclientrect(hWnd, $rect)     -- one version
    api.getclientrect(hWnd, rect.ptr)  -- other version

Interface classes

It is very important that those classes your program (or libraries you use) passes as arguments to WINAPI routines are defined as interface classes in the project options. What make interface classes different from normal classes ? Win32 expects the attributes of a data structure to be in a special order. The WINLIB classes like WAPI_RECT represent the according C-structs. VE must somehow guarantee that the order of the attributes of the Eiffel class is exactly in the order they are defined in the source code.

In very early versions of Visual Eiffel the layout of the class was alphabetic. Now it is in natural (declared) order of the attributes. So when you specify a class to be an interface class the compiler only checks if the order of the attributes can be preserved. If your class does not use multiple inheritance with attribute replication you can be sure that the attrubutes will be in the same order they are declared. From Version 2.6 upwards there is a way to specify all peculiarities on the cluster/library level (not only interface classes but additional .lib files and many other things (at least that's what Object Tools said. Don't ask me too much about this)).

What I don't like about VE here is that we have to define the interface classes on a project level. It would be better to define WAPI_RECT as an interface class on library level. Maybe there is a reason which I do not see at the moment. But when for example I use a third party library which uses WINLIB to build an application I cannot know which classes that library uses as interface classes. Thus the supplier of the library has to provide a documentation that says something like when you use feature f of our library please make sure you have defined the following classes as interface classes in your project: .....

From Version 2.6 upwards there is a way to specify all peculiarities on the cluster/library level (not only interface classes but additional .lib files and many other things). Obviously this is specified in an ASCII file named cluster.es. It seems to be in the format of INI files. Unfortunately I did not see any documentation about it yet.

Further Tips and Tricks

Checking bits

See also CHAP04.BEZIER
In a WM_MOUSEMOVE event we want to find out if the left mouse button is pressed or not. In C it is done in the following way:

 case WM_MOUSEMOVE:
     if (wParam & MK_LBUTTON)
     {
     ...

In a first approach in Eiffel it is done like this (WRONG !!!):

 local
 b    : BIT 32
 ...
 case WM_MOUSEMOVE:
  when wm.WM_MOUSEMOVE then
  b.from_integer(wParam)
  if b.item(
   mk_masks.MK_LBUTTON.to_integer) then
     ...

Here are the codes for mk_masks by the way:

class WAPI_MK_MASKS
------------------------------------------
feature -- Class data
------------------------------------------
MK_LBUTTON  : BIT 32 is     1B; -- 0x0001
MK_RBUTTON  : BIT 32 is    10B; -- 0x0002
MK_SHIFT    : BIT 32 is   100B; -- 0x0004
MK_CONTROL  : BIT 32 is  1000B; -- 0x0008
MK_MBUTTON  : BIT 32 is 10000B; -- 0x0010
------------------------------------------
end -- class WAPI_MK_MASKS

But be careful: do NOT confuse the bit pattern with the integer value! When we use c.to_integer with c as the BIT 32 constant we get the following (I start bit counting from 1 here):

cmeaning

00001B

bit 1

00010B

bit 2

00100B

bit 4 (not bit 3)

01000B

bit 8 (not bit 4)

10000B

bit 16 (not bit 5)

...

...

00011B

bit 3

...

...

With our example it is difficult to see that the method is wrong. Why ? Because in the case of MK_LBUTTON and MK_RBUTTON the bit pattern value and the integer value are the same per pure coincidence.

So to really check that a bit is set in an integer value we have to think about something better. Remember that in our windows programs we usually get an integer value through wParam, lParam etc.

Now we'd like to test if a certain bit in this value is set or not. The constants representing this bit (like MK_LBUTTON,...) are usually defined in C-header files using hex presentation which we converted to attributes of an Eiffel class (like WAPI_MK_MASKS). Routine (check_bit(aIntVal : INTEGER; aBitConst : BIT 32) in the following small test program takes our integer value (e.g. wParam) and a 32 bit constant (e.g. MK_LBUTTON) and reports if the corresponding bit is set in the integer value.

class MAIN
creation { ANY }
	make
feature { ANY }
-----------------
make is
	local
	i				: INTEGER
	do
	print("%N%N----- check if bit 1 is set -----%N")
	from i := 0 until i = 16 loop
		check_bit(i, bit_const_a)
		i := i + 1
		end

	print("%N%N----- check if bit 2 is set -----%N")
	from i := 0 until i = 16 loop
		check_bit(i, bit_const_b)
		i := i + 1
		end

	print("%N%N----- check if bit 3 is set -----%N")
	from i := 0 until i = 16 loop
		check_bit(i, bit_const_c)
		i := i + 1
		end
	print("%N%N")

	io.get_char
	end
-----------------
check_bit(aIntVal : INTEGER; aBitConst : BIT 32) is
	-- require
	-- valid_bit_const : only one bit must be set in aBitConst
	local
	bit_val	: BIT 32
	bt		: BIT 32
	do
	bt.from_integer(aIntVal)
	bit_val := bt and aBitConst
	print(aIntval); print(" = "); print(bt); print(" : ")
	print(" bit ")
	if bit_val /= aBitConst then
		print(" NOT")
		end
	print(" set%N")
	end
-----------------
bit_const_a	 	: BIT 32 is 001B
bit_const_b		: BIT 32 is 010B
bit_const_c		: BIT 32 is 100B
-----------------
end -- class MAIN

You can find the program under testprograms/bitcheck. After finding out how we can test our bit we can move the check_bit to class X_WINLIB_APP.

But we call it bit_of_int_set and let it return a BOOLEAN. I still have to think about how to implement the precondition valid_bit_const.

class WAPI_WINDOW_MESSAGES

Note that if you use VE prior to VE 3.0 B3 class WAPI_WINDOW_MESSAGES contains a bug. It contains the wrong values for several message constants concerning keys:

This problem has been corrected in the 3.0 Version. If you still have the old version there is a class XWAPI_WINDOW_MESSAGES in file xwapi_wm_c.e which I will remove in the next version. Have a look in the source code (e.g. chap05\keylook. You can just uncomment the appropriate lines of code to get the examples running with an 'old' VE.

	class WAPI_WINDOW_MESSAGES

   WM_KEYFIRST    : INTEGER is  256; -- 0x0100
   WM_KEYDOWN     : INTEGER is  257; -- 0x0100
                    -- !! 257 is WRONG, 256 is right.
   WM_KEYUP       : INTEGER is  258; -- 0x0101
   WM_CHAR        : INTEGER is  259; -- 0x0102
   WM_DEADCHAR    : INTEGER is  260; -- 0x0103
   WM_SYSKEYDOWN  : INTEGER is  261; -- 0x0104
   WM_SYSKEYUP    : INTEGER is  262; -- 0x0105
   WM_SYSCHAR     : INTEGER is  263; -- 0x0106
   WM_SYSDEADCHAR : INTEGER is  264; -- 0x0107
   WM_KEYLAST     : INTEGER is  265; -- 0x0108

Pointers

Pointers are used to pass a reference of/to a feature of an Eiffel class to the external world. In my Petzold examples windows are created quite often using routine wnd_proc as event handler routine for the main window. The address of wnd_proc has to be evaluated so that Windows knows to which routine it should send the occuring events.

Sometimes it is necessary to pass the address of a feature to a routine which expects an INTEGER:

WAPI_WINDOW_FUNTIONS.setWindowLong

for example expects 3 INTEGERs as arguments. The last one is the value and is of type INTEGER. You can use the function also for setting the event handler routine of the specified window. So the last argument should somehow contain the address of the new event handler.

h := api.setWindowLong(hwnd, gwl.GWL_WNDPROC, $routine)

doesn't work because the last argument has to be an INTEGER. This means we first have to convert the POINTER to an INTEGER.

h := api.setWindowLong(hwnd, gwl.GWL_WNDPROC, ($routine).to_integer)

unfortunately doesn't work either.

ptr := $scroll_proc	-- compiler error
h := api.setWindowLong(hwndScroll.item(i), gwl.GWL_WNDPROC, ptr.to_integer)

doesn't work either.

ptr.set_item($scroll_proc)
h := api.setWindowLong(hwndScroll.item(i), gwl.GWL_WNDPROC, ptr.to_integer)

finally works.

Basic Types in Windows

With the following small C-program you can find out the sizes of the most important types you have to deal with when you want to use the Win32 API.

#include "stdio.h"
#include "windows.h"

void main() {
	printf("Basic Types of MS-Windows\n");
	printf("INT        %d byte.\n", sizeof(INT));
	printf("WORD       %d byte.\n", sizeof(WORD));
	printf("DWORD      %d byte.\n", sizeof(DWORD));
	printf("LONG       %d byte.\n", sizeof(LONG));
	printf("LPSTR      %d byte.\n", sizeof(LPSTR));
	printf("LPBYTE     %d byte.\n", sizeof(LPBYTE));
	printf("HANDLE     %d byte.\n", sizeof(HANDLE));
	}

This will produce the following output:

Basic Types of MS-Windows
INT        4 byte.
WORD       2 byte.
DWORD      4 byte.
LONG       4 byte.
LPSTR      4 byte.
LPBYTE     4 byte.
HANDLE     4 byte.

You can find the definition of these basic types in windef.h. Here is an extract from it:

typedef unsigned long       DWORD;
typedef int                 BOOL;
typedef unsigned char       BYTE;
typedef unsigned short      WORD;
typedef float               FLOAT;
typedef FLOAT               *PFLOAT;
typedef BOOL near           *PBOOL;
typedef BOOL far            *LPBOOL;
typedef BYTE near           *PBYTE;
typedef BYTE far            *LPBYTE;
typedef int near            *PINT;
typedef int far             *LPINT;
typedef WORD near           *PWORD;
typedef WORD far            *LPWORD;
typedef long far            *LPLONG;
typedef DWORD near          *PDWORD;
typedef DWORD far           *LPDWORD;
typedef void far            *LPVOID;
typedef CONST void far      *LPCVOID;

typedef int                 INT;
typedef unsigned int        UINT;
typedef unsigned int        *PUINT;

Calling WIN32 routines which take structs as arguments

Example:
Once I tried to call WIN32 function GetVersionInfoEx to find out on which Windows platform my program is running. At the beginning I didn't realize that it is already implemented in WINLIB and tried to implement it myself which didn't seem to be very complicated. The special thing about it is that it uses a struct as argument which contains 5 int's and a char[128]. The int's aren't the problem but the char[128]. I tried to use a STRING and initialized it with (s.make(128). This approach didn't work. I also tried to use "expanded STRING" which didn't work either. Then I thought of using an ARRAY[CHAR] and use it's feature area. But area is not exported by ARRAY. As Alexander Kogtenkov from Object Tools told be that neither STRING nor ARRAY would work because they allocate the space in a separate place (one of the reasons is that they are resizeble).

Having a closer look into WINLIB I found the implementation of GetVersionInfoEx which uses the following class. Although this approach isn't very nice it works.

class WAPI_OSVERSIONINFO
...
feature -- Class data
-------------------------------------
   dwOSVersionInfoSize  : INTEGER;
   dwMajorVersion       : INTEGER;
   dwMinorVersion       : INTEGER;
   dwBuildNumber        : INTEGER;
   dwPlatformId         : INTEGER;
   szCSDVersion         : CHARACTER;
   szCSDVersion1        : CHARACTER;
   szCSDVersion2        : CHARACTER;
   szCSDVersion3        : CHARACTER;
   szCSDVersion4        : CHARACTER;
   szCSDVersion5        : CHARACTER;
   ...
   ...
   ...
   szCSDVersion125      : CHARACTER;
   szCSDVersion126      : CHARACTER;
   szCSDVersion127      : CHARACTER;
   ...
   ...
end -- class WAPI_OSVERSIONINFO

If there are many C structures which use a CHAR[128] it is better to declare a special class

expanded class CHARACTER_128
feature
	item0   : CHARACTER
	item1   : CHARACTER
	item2   : CHARACTER
   ...
   ...
   ...
	item127 : CHARACTER

This way our class can be defined as follows:

class WAPI_OSVERSIONINFO
...
feature -- Class data
-------------------------------------
 dwOSVersionInfoSize : INTEGER
 dwMajorVersion      : INTEGER
 dwMinorVersion      : INTEGER
 dwBuildNumber       : INTEGER
 dwPlatformId        : INTEGER
 szCSDVersion        : CHARACTER_128
end -- class WAPI_OSVERSIONINFO

use a class BIT
If we do not need to fetch the data (which is a very bad assumption because you never know what client classes need) we can use BIT 1024 (128*8=1024). Then our class has the following form:

class WAPI_OSVERSIONINFO
...
feature -- Class data
----------------------------------
   dwOSVersionInfoSize  : INTEGER;
   dwMajorVersion       : INTEGER;
   dwMinorVersion       : INTEGER;
   dwBuildNumber        : INTEGER;
   dwPlatformId         : INTEGER;
   szCSDVersion         : BIT 1024;
end -- class WAPI_OSVERSIONINFO

a combination of CHARACTER_128 and BIT 1024

expanded class CHARACTER_128
feature
	data   : BIT 1024
end -- class CHARACTER_128

class WAPI_OSVERSIONINFO
...
feature -- Class data
----------------------------------------
   dwOSVersionInfoSize  : INTEGER;
   dwMajorVersion       : INTEGER;
   dwMinorVersion       : INTEGER;
   dwBuildNumber        : INTEGER;
   dwPlatformId         : INTEGER;
   szCSDVersion         : CHARACTER_128;
end -- class WAPI_OSVERSIONINFO

Calling C routines from VE which return complex types

The types that require more than 8 bytes can be returned from C into Eiffel code using a WINAPI call and expanded classes (see CHARACTER_128).
Alexander Kogtenkov told me:

As to the user-defined types within the range 1-8 bytes (except the basic types) current (5.Nov 1998) VE uses a different convention for returning the result and therefore it requires

a wrapper. However VE 2.6 supports the results of user-defined types of 1 or 4 bytes and handles them like C.

Whatever user-defined types are.

Literature

  • [1] Charles Petzold: Programming Windows 95, Microsoft Press, ISBN: 1-55615-676-6
  • [2] Richard J.Simon: Windows 95 Win32 Programming API Bible, Waite Group Press, SAMS