Developed by: John Walker, JSW Technology
Distributed by: Aonix
Copyright John Walker & JSW Technology 1997-1999.
All Rights Reserved.
The information contained herein is subject to change without notice.
ObjectAda is a trademark of Aonix
Microsoft, Visual C++, Win32 and Windows NT are trademarks of Microsoft, Inc.
Last updated: 2 March 1999
This page will be used to post ongoing work on more samples for the benefit of existing and potential customers. These samples are derived from the examples in Programming Windows 95 with MFC by Jeff Prosise and published by Microsoft Press. This book is recommended reading for understanding MFC. Issues relating to AFC and the mapping to Ada are discussed in the AFC User Guide.
It is intended to replace Scribble steps 0 to 5 with the full implementation of Scribble step 6 and substitute the Prosise examples as the tutorial for learning MFC and AFC.
Go back to Ada Foundation Classes
Go back to start of JSW Technology Home Page
These samples require AFC Version 1.0 which is included in:
The samples can be downloaded using prosise2.zip (57 KB) into objectada\afc.
The sources can be extracted directly into the samples directories set up by the product installation. For example, assuming that ObjectAda is installed in c:\program files\aonix\objectada then the files can be extracted as follows:
>c: >cd \"program files"\aonix\objectada\afc >..\bin\unzip prosise2.zipNote that unzip.exe is supplied with ObjectAda in objectada\bin.
That should have installed the following samples directories:
objectada\afc\samples\prosise objectada\afc\samples\prosise\hello objectada\afc\samples\prosise\gdidemo1 objectada\afc\samples\prosise\gdidemo2 objectada\afc\samples\prosise\gdidemo3 objectada\afc\samples\prosise\gdidemo4 objectada\afc\samples\prosise\mousecap objectada\afc\samples\prosise\tictac objectada\afc\samples\prosise\visualkb
This section provides a step by step guide to setting up a project for the sample Hello. Similar steps are required to set up projects for the other samples.
Create a project using ObjectAda File New Project. Set Project name to Hello. Choose a directory for the project, for example:
C:\Program Files\Aonix\ObjectAda\AFC\Samples\Prosise\Hello
Use Project Files to add all the Ada sources from the directories:
prosise
prosise\hello
Use Project Setting Search Add to add one of the AFC libraries from the following:
..\afc\binding\nafxcwd\lib
..\afc\binding\nafxcw\lib
..\afc\binding\mfc42\lib
..\afc\binding\mfc42d\lib
Use Project Targets Win32 (Intel) Release to build without debug options.
Select Project Compile All Files.
Use Project Settings Build to set the Main unit to Hello, use Project Settings Link to set the Application type to Windows and check Remove uncalled code.
Use Project Build to build the application and Project Execute to run it.
The linker reports an unresolved external symbol when the child package VC.Strings is compiled with debug options. This is because VC.Strings is a child of the library package VC in the bindings and VC has been compiled without Ada debug options. Future releases of the bindings should be compiled with Ada debug options. A workaround for this problem is to use Project Settings Link to pass /FORCE to the linker. However the "proper" solution is to compile VC.Strings without debug options as follows.Use Project Settings General to Allow selection of compiler options before running compiler.
Use Project Compile to compile strings.ads and strings.adb unchecking Codeview and Ada debug in Compiler Options.
Use Project Build to compile the remaining sources and build the application.
Samples built using the MFC library NAFXCWD may complain about missing DLLs URLMON.dll and SHLWAPI.dll when run under Windows NT 4.0. They run correctly with these DLLs installed.
The following DLLs should be installed in order to run executables on Windows 95:
OpenGL.dll
Glu32.dll
Note that executables built using the library NAFXCWD also require URLMON.dll and SHLWAPI.dll when run under Windows 95 but then fail as described in section 1.2 of the User Guide. It is recommended that NAFXCWD not be used for executables that are to be run under Windows 95.
Note that error messages relating to missing DLLs are displayed when running Windows applications directly but not when invoked using Project Execute in the ObjectAda IDE.
It is recommended that the Ada program structure be designed to mirror the structure of C++ applications based on MFC to take advantage of published examples and tutorials based on C++. This seems to lead to an elegance and ease of understanding in Ada which is strangely less apparent in C++.
The principal structure involves mapping the application object, MyApp, and WinMain on to the Ada main subprogram and mapping each class derived from the Ada Foundation Classes on to an Ada package as described below.
Hello is such a simple application that Prosise has chosen to use just two source files, hello.h and hello.cpp. In Ada the same source file structure has been adopted as is recommended for all applications. The simplicity of the application enables this structure to be emphasised.
The source files for the Ada version of sample Hello are as follows:
prosise\afcwmain.ada Ada version of WinMain (AFCWinMain) prosise\strings.ads Ada package VC.Strings prosise\strings.adb prosise\hello\hello.ada Ada main subprogram Hello prosise\hello\cmyapp.ads Ada version of class CMyApp prosise\hello\cmyapp.adb prosise\hello\cmainwnd.ads Ada version of class CMainWindow prosise\hello\cmainwnd.adb
The source files for AFCWinMain and VC.Strings are grouped separately because they are potential candidates for inclusion as AFC helpers in a future release of the Ada Foundation Classes. They are used by all the Prosise samples (implemented so far).
The sources specific to the sample Hello include the main subprogram and a specification and body for each derived class package in the application.
The sources are described below.
The application object, MyApp, is declared in the Ada main subprogram, Hello. This is equivalent to the global declaration of MyApp in C++ except that, in Ada, the constructor and destructor must be called explicitly in the main subprogram where the calls are implicit in C++.
In C++ the MFC source file AppModule.cpp is included in an EXE application. This initializes the module state and provides the MFC implementation of WinMain.
AppModule cannot be included in an ObjectAda application because it would override the Ada implementation of WinMain. So, the initialization of the module state is implemented explicitly in the main subprogram and the implementation of the MFC version of WinMain is implemented explicitly by the separate subprogram, AFCWinMain, which is called by the main subprogram.
The parameterless procedure AFCWinMain implements the equivalent of the MFC version of WinMain. This is a potential candidate for inclusion in the Ada Foundation Classes as an AFC helper.
The WinMain arguments are not available directly in the Ada main subprogram and so they are reconstructed in AFCWinMain.
It is not necessary to make the application object visible to AFCWinMain because it can be obtained from the MFC framework using AfxGetApp. This returns a CWinApp pointer to the application object.
The function calls in AFCWinMain to CWinApp functions are virtual and therefore the function overrides in the package CMyApp will be called. In Hello only InitInstance is overriden, the other functions use the default implementations provided by the MFC framework.
AFCWinMain illustrates the use of VC.Strings.To_AddressOfChar to convert an Ada null terminated string to a C++ string pointer, which the AFC binding maps to VC.AddressOfChar. Note that AFC does not take account of const when mapping C++ arguments to Ada so there is no write protection when Ada constant strings are used as actual parameters to MFC functions.
The package VC.Strings is a potential candidate for inclusion in the Ada Foundation Classes as an AFC Helper. The samples are being used as a guide to establish the functionality required.
function To_AddressOfChar (S : String) return AddressOfChar;
Returns the address of the first character of the string typed as VC.AddressOfChar. This can be used where MFC requires char *.
Hello includes two derived classes, CMyApp and CMainWindow. These are implemented by the Ada class packages CMyApp and CMainWindow.
The complex dependencies between MFC classes require class record types to be grouped together in packages such as AFC. The AFC bindings include a class package for each class which includes a subtype, called Object, for the class type and a subtype, called Pointer, for a pointer to the class type.
The dependencies between derived classes in applications seem to be much simpler and, so far, it has been possible to include their type declarations in the application class packages. The naming convention used in the binding is carried through to the samples and enables a standard approach to the handling of the all pervasive C++ pointers and references. The class package includes the class record type, Object, and a pointer to the class type, Pointer.
The application class packages follow the structure generated by DACTool except that the class record type, Object, is private and unused code is not included. It is not clear that in the general case, supported by DACTool, it would be desirable for the class record type to be private.
The class packages also support the convention used in AFC to provide conversion functions from the class pointer to base class pointers, when they are required by the application. These conversions are safe because of the structure of the class record, which is required for compatibility with C++.
The class CMyApp is derived from CWinApp. Note that the structure of the class record, Object, must remain compatible with CWinApp so that CMyApp objects can be offered as parameters to the member functions of the base classes.
The component Base is introduced into all Ada class records. This protects the application code from any changes to the base class in future releases of MFC.
CMyApp implements overrides for the virtual functions InitInstance and ScalarDeletingDestructor. The Ada specification and calling conventions for these functions must match the C++ specification exactly because these functions may be called by the MFC C++ framework through the VFT. This can be ensured by using DACTool to generate the derived class package which is then customised for the particular application.
Note the use of the conversion function, CMainWindow.To_CWnd, in InitInstance to convert from CMainWindow.Pointer to CWnd.Pointer.
The virtual destructor override, ScalarDeletingDestructor, raises Program_Error because it should not be used by the framework. CMyApp.Destructor is called directly by the main subprogram, Hello.
The constructor invokes the constructor for its base class. This yields a pointer to the VFT for the base class in Base.VFT.
If this is the first invocation of the constructor, indicated by null values in BaseVFT and VFT, then the VFT is created and initialised. By default it is the same as the base class. Any function overrides are patched in explicitly using the VFT offsets provided by AFC. Pointers to the VFT for the base class and the derived class are stored in BaseVFT and VFT for later use in constructors and destructors.
The destructor should execute any destruction code specific to the derived class, including calling the destructors for class members, if any. Then the VFT is restored to that of the base class before calling the destructor for the base class. Note that nondispatching calls should be used to the destructors for class members and the base class.
The constructors and destructors are not called implicitly in Ada as in C++. They should be called explicitly when objects of the derived class are created or destroyed.
Constructors cannot be virtual and therefore the constructors declared in Ada will not be called from C++.
Destructors can be virtual. However the VFT entry for a virtual destructor should point at a scalar deleting destructor and not the destructor itself. A scalar deleting destructor takes a parameter in addition to "this", which is a flag of type unsigned int. In C++ this flag is set to zero for a virtual destructor call and one for a call of the delete operator for the class. For further information see the Microsoft Article Q128805.
The samples are designed to avoid calls to scalar deleting destructors, which are implemented to raise Program_Error.
The class CMainWindow is derived from CFrameWnd. It's implementation in the class package CMainWindow follows the same principles described for CMyApp.
CMainWindow provides two conversions from its class pointer to base class pointers. To_CWnd was used in CMyApp.InitInstance. To_CFrameWnd is used by CMainWindow.Constructor and CMainWindow.Destructor.
CMainWindow is created in the Ada heap by CMyApp.InitInstance. The default implementation provided in the MFC framework for PostNcDestroy uses the C++ delete operator which calls the virtual destructor and then removes the object from the heap. CMainWindow.PostNcDestroy overrides the default implementation making an explicit call on the destructor. Unchecked_Deallocation could be used to Free the object from the Ada heap but since PostNcDestroy is called as part of the program exit sequence this can be left for the exit from the main Ada subprogram.
CMainWindow introduces the use of message handlers and the AFC Helper package AFXMSG.
The Ada specification and calling convention for the message handler OnPaint must match exactly the C++ specification because it is called by the MFC framework through the message map. This can be ensured by using the strongly typed version of the access subprogram AFXMSG.ON_WM_PAINT when constructing MessageEntries. However this approach requires that the This pointer for the message handler, OnPaint, has type CCmdTarget.Pointer which corresponds to a base class of CMainWnd.Pointer. Unchecked_Conversion is required to convert the base pointer to the required derived class pointers.
An alternative approach is to use the less strongly typed version of AFXMSG.ON_WM_PAINT which uses System.Address as the pointer to the message handler. This puts the onus on the programmer to ensure the correct specification of OnPaint which can now be declared as:
procedure OnPaint (This : Pointer); pragma Export (thiscall, OnPaint, Link_Name => "CMainWindow.OnPaint");
Now the required class pointers can be acquired without Unchecked_Conversion using the class conversion functions as follows:
To_CFrameWnd (This) To_CWnd (This)
The application Hello uses the first approach. Note that the conversion functions are declared locally within the OnPaint handler to restrict their use and to prevent the hiding of the conversion functions in the specification.
CMainWindow.OnPaint in application GDIDemo1 uses the alternative approach.
MessageMap is constructed using the AFC Helper MakeMessagemap. Note that the actual structure of AFX_MSGMAP is different for the statically linked and DLL versions of MFC. This difference is handled by MakeMessagemap (see 2.3).
The implementation of MessageEntries, MessageMap and GetMessageMap is common to all message maps. The only changes required are the size of MessageEntries and associated calls on the functions in AFXMSG, the base class used for the actual parameters in the call to MakeMessageMap and the link name for GetMessageMap, which must be unique to the application.
The application, GDIDemo1, can be found in directory prosise\gdidemo1. It also uses AFCWinMain and VC.Strings as described for Hello.
The implementation of GDIDemo1 builds on the principles illustrated in Hello.
The class, CMainWindow, extends its base class, CFrameWnd, with members m_cxChar and m_cyChar.
The message handler, CMainWindow.OnPaint, illustrates the alternative approach described above for message handlers (see CMainWindow). Having used this alternative specification for OnPaint the alternative implementation of AFXMSG.ON_WM_PAINT must be used in the initialization of MessageEntries. GDIDemo2 reverts back to the original approach.
The following Prosise samples are also included in this release. They continue the principles described above.
prosise\gdidemo2 prosise\gdidemo3 prosise\tictac prosise\mousecap prosise\gdidemo4 prosise\visualkb