Interactive
Software Engineering
Windows Eiffel Library

[ISE Home] Home ] Release Notes ] Technology Papers ] Installation Notes ] About Eiffel ]


An overview of WEL, the Windows Eiffel Library


NOTICE: This is a working document. It is being made public to illustrate our thinking and directions of development, as well as some of the benefits of the Eiffel way of doing things, but no part of its contents may be construed as implying any specific commitment on the part of ISE. The design of WEL as documented here may still change.

The document and the software it describes are copyright ISE, 1999. All trademarks cited are the property of their respective owners.

The example discussed below was chosen as a tested for WEL. The C and C++/MFC versions were written later on, for purpose of comparison; they represent the result of our best efforts to implement the example in these approaches, and should not be construed as implying any inherent property of any existing commercial product.

Additional notes: If you want to have some practical example on how you can use WEL, we encourage you to follow the WEL tutorial.


This note introduces some of the concepts of the new Windows Eiffel Library (WEL), which has been designed to make Windows programming easier, more reliable, more convenient, and more powerful. WEL is the Windows-specific layer of EiffelVision, the portable Eiffel graphics library.

Introduction: more than an encapsulation

The most obvious definition of WEL is that it is an encapsulation of Windows primitives, making it possible for users of Graphical Eiffel for Windows to have direct access to the Windows graphical API.

But this is only part of the truth. What you will see through the discussion below is that for someone whose primary interest is Windows programming (rather than Eiffel per se), WEL provides considerable benefits of its own: the ability to work in a much simpler, move convenient and safer way than if you were using the Windows primitives directly, for example from C.

This advantage comes from the abstraction facilities of Eiffel, which make it possible to encapsulate many low-level details so that developers can concentrate on the functionality, not implementation requirements.

So even for specific and OS-dependent tasks such as using a particular graphical API, the benefits of the Eiffel method and of reusable Eiffel components will show up quickly.

Rather than providing an extensive description of WEL, this note will illustrate the above points through a commented example, which will contrast the C, MFC (C++) and Eiffel ways of achieving the same needs.

A small example application

The examnple application writes the x and y position in the window where the user has clicked. Also, the user must be able to clear the window by pushing a button. Here is the display:

 

This application needs to catch two kinds of action:

  • The mouse click in the window.
  • The `Clear' button activation.

Doing it in C

In C Windows programming, we need to register a class to create the application's main window (WNDCLASS structure and RegisterClass function). We have also to write the application's main loop to get and dispatch the messages (functions GetMessage, DispatchMessage). All the messages received by a window are sent to a window procedure (WndProc function) which must be written by the developper to catch the user's events (WM_LBUTTONDOWN and WM_COMMAND).

Here is the C program to perform the above:

#define STRICT
#include &ltwindows.h>
#include &ltstring.h>
#include &ltstdio.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

HINSTANCE CurrenthInstance;

#define ID_BUTTON_CLEAR 1

#pragma argsused
int PASCAL WinMain (HINSTANCE hInstance,
	HINSTANCE hPrevInstance, LPSTR lpszCmdLine,
	int nCmdShow)
{
	WNDCLASS wClass;
	MSG msg;
	HWND hWnd;

	CurrenthInstance = hInstance;

	if (!hPrevInstance)
	{
		wClass.style = CS_HREDRAW | CS_VREDRAW;
		wClass.lpfnWndProc = WndProc;
		wClass.cbClsExtra = 0;
		wClass.cbWndExtra = 0;
		wClass.hInstance = hInstance;
		wClass.hIcon = LoadIcon (hInstance, IDI_APPLICATION);
		wClass.hCursor = LoadCursor (NULL, IDC_ARROW);
		wClass.hbrBackground = GetStockObject (WHITE_BRUSH);
		wClass.lpszMenuName = NULL;
		wClass.lpszClassName = "TestClass";

		if (!RegisterClass (&wClass))
			return FALSE;
	}

	/* Make the main window */
	hWnd = CreateWindow ("TestClass", "Test",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow (hWnd, nCmdShow);
	UpdateWindow (hWnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg);
		DispatchMessage (&msg);
	}
	return 0;
}

LRESULT CALLBACK WndProc (HWND hWnd, UINT Message,
	WPARAM wParam, LPARAM lParam)
{
	int x, y;
	char buffer[20];
	HDC dc;

	switch (Message)
	{
		case WM_CREATE:
			/* Make a button */
			CreateWindow ("BUTTON", "Clear",
				WS_VISIBLE | WS_CHILD,
				1, 1, 70, 40, hWnd,
				ID_BUTTON_CLEAR,
				CurrenthInstance, NULL);
			break;

		case WM_LBUTTONDOWN:
			/* Write x and y */
			x = LOWORD (lParam);
			y = HIWORD (lParam);
			sprintf (buffer, "(%i, %i)", x, y);
			dc = GetDC (hWnd);
			TextOut (dc, x, y, buffer, strlen (buffer));
			ReleaseDC (hWnd, dc);
			break;

		case WM_COMMAND:
			/* Clear the window */
			if (wParam == ID_BUTTON_CLEAR)
				InvalidateRect (hWnd, NULL, TRUE);
			break;

		case WM_DESTROY:
			PostQuitMessage (0);
			break;

		default:
			return DefWindowProc (hWnd, Message,
				wParam, lParam);
	}
	return 0;
}

Doing it in C++/MFC

The C++ version (using Microsoft Foundation Class library) is more abstract than the C version, but still leaves much to be desired in its encapsulation of Windows, in particular for such aspects as window styles and the window creation process.

Class CMyWindow inherits from CFrameWnd to create a window and a button. To process the messages, some macros must be called to map a C++ method into a Windows message (AFX_MESSAGE). The method InitInstance from CWinApp must be defined to create the application's main window and show it. Programmers still have to contend with a lot of low-level details. This version is slightly longer than the Eiffel-WEL version which follows.

#include &ltafxwin.h>
#include &ltstring.h>
#include &ltstdio.h>

#define ID_BUTTON_CLEAR 1

class CMyWindow: public CFrameWnd
{
public:
	CMyWindow ();

	//{{AFX_MSG (CMyWindow)
	afx_msg void OnClear ();
	afx_msg void OnLButtonDown (UINT nFlags, CPoint point);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP (CMyWindow, CFrameWnd)
	//{{AFX_MSG_MAP (CMyWindow)
	ON_WM_LBUTTONDOWN ()
	ON_COMMAND (ID_BUTTON_CLEAR, OnClear)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP ()

CMyWindow::CMyWindow ()
{
	// Make the main window and a button
	CRect rect (1, 1, 70, 40);
	CButton * button;

	Create (NULL, "Test", WS_OVERLAPPEDWINDOW,
		rectDefault, NULL, NULL);
	button = new CButton;
	button->Create ("Clear", WS_VISIBLE | WS_CHILD,
		rect, this, ID_BUTTON_CLEAR);
}

void CMyWindow::OnClear ()
{
	// Clear the window
	Invalidate (TRUE);
}

void CMyWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
	// Write x and y mouse position
	CDC * dc;
	char buffer [20];

	MessageBeep (0);
	dc = GetDC ();
	sprintf (buffer, "(%i, %i)", point.x, point.y);
	dc->TextOut (point.x, point.y, buffer, strlen (buffer));
	ReleaseDC (dc);

	CFrameWnd::OnLButtonDown(nFlags, point);
}

// Define an application class derived from CWinApp
class CMyApp: public CWinApp
{
public:
	virtual BOOL InitInstance ();
};

// Construct the CMyApp's m_pMainWnd data member
BOOL CMyApp::InitInstance ()
{
	m_pMainWnd = new CMyWindow ();
	m_pMainWnd->ShowWindow (m_nCmdShow);
	m_pMainWnd->UpdateWindow ();
	return TRUE;
}

CMyApp MyApp;

The Eiffel version

Using WEL, the Eiffel version is a lot simpler, clearer - and easier to write.

It consists of two simple classes, MY_APPLICATION and MY_MAIN_WINDOW.

Class MY_APPLICATION inherits from APPLICATION, a WEL class which "knows" how and where to dispatch the messages. All MY_APPLICATION needs to do is to define the application's main window (routine `init_main_window'). Everything else is taken care of automatically by the predefined mechanisms of the reusable library class APPLICATION from WEL.

In the class MY_MAIN_WINDOW, an heir of FRAME_WINDOW, we redefine two routines:

  • We redefine on_wm_command_control so that it will write the current cursor position.
  • We redefine on_wm_left_button_down so that it clears the window.
That's it.

Here is the first class:

	indexing
		description: "A small example of WEL programming, %
					%with a main window, where we can detect %
					%user clicks, and let users clear the window."
	class
		MY_APPLICATION
	
	inherit
		APPLICATION
	
	creation
		make
	
	feature
	
		init_main_window is
				-- Create the main window.
			do
				!MY_MAIN_WINDOW! main_window.make
			end
	
	end -- class MY_APPLICATION
Here is the second class:
	class
		MY_MAIN_WINDOW
	
	inherit
		FRAME_WINDOW
			rename
				make_top as frame_make_top,
				make as frame_make
			redefine
				on_wm_command_control,
				on_wm_left_button_down
			end
	
	creation
		make
	
	feature {NONE} -- Initialization
	
		make is
				-- Create the main window and a button.
			local
				button: BUTTON
			do
				frame_make_top ("Test");
				create button.make (Current, "Clear",
					1, 1, 70, 40, Id_button_clear)
			end
	
	feature {NONE} -- Behaviors
	
		on_wm_command_control (id_control: INTEGER; window: WINDOW) is
				-- Clear the window.
			do
				if id_control = Id_button_clear then
					invalidate
				end
			end
	
		on_wm_left_button_down (keys, x_pos, y_pos: INTEGER) is
				-- Write the values of `x_pos' and `y_pos'
			do
				position.wipe_out;
				position.extend ('(');
				position.append_integer (x_pos);
				position.append (", ");
				position.append_integer (y_pos);
				position.extend (')');
				dc.get;
				dc.text_out (x_pos, y_pos, position);
				dc.release
			end
	
	feature {NONE} -- Implementation
	
		position: STRING is
				-- The string that will contain the position indication
			once
				create Result.make (20)
			ensure
				result_not_void: Result /= Void
			end
	
		dc: DC is
				-- Device context used to write the position
			once
				create Result.make_by_window (Current)
			ensure
				result_not_void: Result /= Void
			end
	
		Id_button_clear: INTEGER is unique
	
	end -- class MY_MAIN_WINDOW

Even for such a basic and relatively low-level application, the code examples speak for themselves.

Note that even though the classes of the example merely rely on the WEL library mechanisms, they can make some good use of assertions. The library classes themselves use assertions more extensively for coherence of design, readability and safety.