Programming Windows 95 and Windows NTHistoryApril 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 AbstractThis 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) IntroductionBecause 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 classesThe 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 ExamplesI 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 WorldSee introduction about playing a wav file. It uses the Win32 API PlaySoundA to achieve this. Chapter 3 : Sysmets 2In 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 3no comment yet. Chapter 4 : BezierOne 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 : WhatsizeHere 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 : Endjoinin 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 : ScrambleIt 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 : EMF 2Saves the enhanced metafile to a file on disk using createEnhMetaFile and closeEnhMetaFile. Chapter 4 : EZFontnot a real example. It is used in example Justify 1 Chapter 4 : Justify 1The 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 : SysmetsThere are only a few changes to Sysmets of chapter 3. The changes support the keyboard as input. Chapter 6 : Checker 2Same as Checker 1 but with keyboard support (arrow keys, space bar). Chapter 6 : Checker 3Same 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 2No comments yet. Chapter 7 : Beeper 1Overrides is_app_startable to try to create the timers before the messageloop starts. Chapter 7 : Beeper 2No comments yet. Chapter 7 : DigclockThe 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 : AnaclockLike 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 8 : BtnLookThere are two ways Petzold gets the handle to the Application Instance. Both are done in wm_create:
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 : OwnerDrwFor 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 1The 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 : PopPad1This 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 : EnvironI 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
'p := $buf' Chapter 8 : HeadIf you try to get the selected text of a listbox you cannot do this in a direct way like this: s : STRING 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 HintsCalling WIN32 API
In Visual Eiffel you call a Win32 routine with the external "WINAPI" form. 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)
The ASCII version always ends with an A.
Visual Eiffel uses ASCII strings at the moment.
playsoundA(pszSound : POINTER; hModule : POINTER; fdwSound : BIT 32) : BOOLEAN is external "WINAPI" alias "PlaySoundA" end Using special import libraries
(Hint originally from Roger Browne)
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) 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: 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 classesIt 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 TricksChecking bits
See also CHAP04.BEZIER 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 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):
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_MESSAGESNote 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 PointersPointers 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:
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 WindowsWith 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: 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 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
a wrapper. However VE 2.6 supports the results of user-defined types of 1 or 4 bytes and
handles them like C.
|