|
SilverPlus Development Reference |
Table
of Contents
SilverPlus Application Development
Table of Exhibits |
| Introduction
SilverPlus is the environment used for developing Windows applications that run under SilverScreen. Such applications are implemented as DLLs loaded under control of a SilverC program. SilverPlus applications use the SilverScreen API, which is the common set of functions used to develop application programs that use SilverScreen Solid Modeling technologies. This manual contains information about developing SilverPlus DLLs. We also assume some familiarity with the C/C++ languages and SilverScreen operation, plus some understanding of 3D concepts. For more complete coverage of SilverScreen, see the SilverScreen Reference Manual. This document describes the SilverPlus development system, which is a method used to build applications that utilize the SilverScreen Solid Modeler. Important type styles and conventions used in this document:
|
|
SilverPlus Application Development
Under the DOS version of SilverScreen, the only way to link to native code for purposes of execution speed was to use the REX file interface. Under Windows, a similar approach available is available by using an executable library known as a DLL, short for (Dynamic Link Library). A DLL is a collection of resources such as code, data, graphical elements etc. For the purposes of extending SilverC, a DLL must have executable code in it. Unlike DOS, a DLL is useful not only for accessing native code for speed, but also the ability to incorporate native Windows user interface elements such as menus, dialog boxes and toolbars into your application. SilverScreen does not directly load DLLs for 3rd party use; the 3rd party developer must write a SilverC application to manage the loading and unloading of DLLs, and also to direct flow of execution into and out of the DLL. A SilverC program does this by using the REX interface: rex_load, rex_unload and rex_exec.
Exhibit 1. Execution of a SilverPlus DLL There are many tools that may be used to build DLLs, however, we recommend using Microsofts Visual C++ package (MSVC++), as that is what we use to build SilverScreen. By using MSVC++ and the MFC DLL libraries, you can cut down on the amount of code that is linked into your DLL. We believe that other packages may also be used, some of which use licensed versions of MFC, but we have only ever tested with MSVC++. If you are using DLLs to extend SilverScreen functionality, and you use either the MFC library or the C runtime library, it is essential that you use the identical DLLs that SilverScreen is using. If you do not, then it is very likely that SilverScreen and your app will crash. This includes the cases where you are running under a release version of SilverScreen or a debug version. The SilverScreen Developers Kit is designed to be used with version 6.0 of the Microsoft Visual C++ compiler. SilverScreen makes use of the following MSVC DLLs:
A DLL in the Windows environment is a collection of resources including executable code, graphical elements like dialog templates and menus, and data, such as strings. The acronym DLL stands for Dynamic Link Library, and it indicates that that the library is loaded dynamically by an executing program. A SilverC application may use a DLL to perform calculations at native code speed or to access Windows functionality not provided by the SilverC runtime library. We here describe two ways to build DLLs for use by SilverScreen. One way is to build a C DLL, specifying your own DllMain DLL entry function. The other is to make a C++ DLL, with a CWinApp derived class that serves as the base of the application (the DllMain function is embedded implicitly in this construct). Both methods require that there be an exported function named either SilverCExec or _SilverCExec that SilverScreen will use to communicate with the DLL. If you choose to use a C DLL, then you will need to provide a DllMain function. The DllMain function is a requirement for Win32 DLLs. The DllMain function is called just after your DLL is loaded, and again just prior to its removal. DllMain is also called when the SilverScreen process creates or destroys a thread. DllMain is used to initialize the DLL; in particular, the C run-time library and some static C++ objects are initialized then, also, an important piece of data -- the instance (or module) handle for the DLL -- is passed to DllMain when the DLL is being loaded. The instance handle is necessary for loading other resources from the DLL module. Note that under some uses of the MFC for DLLs, the DllMain function is hidden inside the MFC mechanisms. See the MFC example. The prototype for DllMain should match the following:
where hInstance is the DLL module handle, and where dwReason is the reason for the DllMain call. dwReason may be one of the following
lpReserved reserved by Windows Exhibit 2 contains a simple skeleton for a C DLL, including a SilverCExec function. If you choose to use an MFC DLL to extend SilverScreen, then you may use the AppWizard in Microsoft Visual C++ to provide the skeleton. We recommend that you use specify a regular DLL using the MFC library implemented as a shared DLL. The procedure for starting such a DLL is found in Building SilverC DLLs Using the MFC Wizard. Note: This section assumes that the compiler being used is Microsofts Visual C++. The SilverCExec (or equivalently, _SilverCExec) function is required by the SilverC DLL loading and unloading code. It is used to pass the SSREX pointer sc down to the DLL (SSREX is a structure containing function pointers corresponding to members of the SilverC library), so that SilverC functions may be used by the DLL. It is also used by a SilverC program to call into the DLL, thus serving as the gateway between SilverC and the DLL. The prototype for SilverCExec should match the following:
That is:
Where: op a user-defined operation code; with the exception of anything less than the value of REX_USER_BASE, which is reserved for SilverScreen library initialization data is dependent on the operation code; in the case where op is equal to REX_FIRST_LOAD, this will be a pointer to an SSREX structure. The alignment for members of structures passed via data (including the SSREX) structure must be 4 bytes. Exhibit 2. Sample Skeleton for a C DLL A SilverScreen DLL is loaded by calling the SilverC library function rex_load. Internally, rex_load operates by calling the Win32 function LoadLibrary. If the DLL is found, LoadLibrary attempts to load it, and in doing so, calls the DLL entry function (DllMain in our case). In addition, if it is a C++ DLL, then all global objects are initialized (i.e., the constructors of global objects are invoked, including that of the CWinApp derived main class). At his point in execution, the SilverCExec function has not been called, so you may not yet call SilverC functions (i.e., not in the DLL_PROCESS_ATTACH section of DllMain, or in the constructor for your CWinApp derived class). Provided all goes well, the library is successfully loaded, and then SilverScreen attempts to locate the SilverCExec (or _SilverCExec) function in the library (by using GetProcAddress). If neither SilverCExec nor _SilverCExec is found, then the load fails, and rex_load returns 0. Otherwise, the DLL was previously loaded, but not unloaded, then SilverScreen calls SilverCExec with an op value of REX_FIRST_LOAD. At this point, the SSREX pointer will be passed as well, and the DLL should store this pointer in a variable so that the DLL can call SilverC functions. Following this, SilverScreen calls SilverCExec with a value of REX_LOADING. rex_load then returns a handle which should then be used in subsequent calls to rex_call and rex_unload. The __stdcall calling convention The __stdcall calling convention is used to call Win32 API functions. The called function cleans up the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype. The following list shows the implementation of this calling convention.
The /Gz compiler options specifies __stdcall for all functions not explicitly declared with a different calling convention. The __cdecl calling convention The __cdecl calling convention is used for the SilverCExec function, and also for SilverC library functions, which are called via pointers passed to the DLL. The calling function cleans up the stack Functions that use this calling convention require a function prototype. The following list shows the implementation of this calling convention.
The /Gd compiler options specifies __cdecl for all functions not explicitly declared with a different calling convention. Note that although for __cdecl functions the C compiler generates a symbol that is prefixed with _, the linker, when exporting such a symbol from the DLL, strips the _ from the exported name. You may use a function with a name other name than DllMain by using the /ENTRY: linker option; to keep things simple, we retain the DllMain convention. The Silver Engine is built using the _AFXDLL architecture, that is, it uses the MFC and C runtime DLL's, rather than statically linking them. The SilverC extension DLL may also use the MFC as well in a DLL, thus saving code. Beware... the standard run-time library calls in a SilverC extension library are actually implemented via call through a function pointer in the SSREX structure passed at initialization time. Building SilverC DLLs Using the MFC Wizard (using Microsoft Visual C++ version 6.0) Files Used in the Project Silver directory
Execution directory
Project directory
Generating the DLL Skeleton The following procedure may be used to build a DLL that may be used by a SilverC program.
c:\msdev\include,c:\msdev\mfc\include,c:\silver,c:\silver\ex Using the REX Interface with Windows DLLs
The REX interface was designed to allow a Silver C program to load binary code files under DOS, REX being the name for Relocatable EXecutable file format. The REX functions are overloaded under Windows for use with DLLs, to much the same effect. The REX functions will only load REX files under DOS and DLL files under Windows, and not vice-versa. They are documented in the SilverScreen API Reference manual manual. An important usage of 3rd-party DLLs is to store your own resources (dialog templates, menus, toolbars and the like) for your own UI. A requirement of using the MFC resource handling mechanisms to load resources from the DLL is that the default resource handle for the application be set to the instance of the DLL. The call to set the default resource handle is the MFC function AfxSetResourceHandle, passing it the instance handle of your resource DLL. The instance handle is used to uniquely identify an executable module (.EXE or .DLL), and is set when your DLL is loaded. If you have an MFC style DLL, then it is part of the CWinApp-derived class that is the base of your DLL. Alternatively, if you have an "old-style" DLL, the instance handle is passed as the first argument to DLLMain. Most often, you can set the default resource handle early in your DLL initialization code, and forget about it. For purposes of loading its own resources, SilverScreen will set the default resource handle as necessary, and then restore it when finished. This is always true in the case of executed commands (a la ss_command), however, it is possible that we have missed cases where the resource handle is not set and restored properly, most likely in cases of SilverC function calls. If you run into a situation where you suspect that this is the case (i.e., a resource in your DLL is not loaded properly), then please let us know. Note that SilverScreen will restore the proper instance handle when your program exits, so it is safe to leave it unrestored in your DLL. Originally, we recommended wrapping each and every call to load resources with code to set and restore the default resource handle, but that is not necessary now, and was awkward to do in certain cases where MFC needed to load resources from the DLL without explicitly notifying the program. By leaving the resource handle set to that of the DLL, things like tooltips can be retrieved properly by MFC. DLLs built for SilverScreen may be debugged using the Visual C++ debugger. You must build your DLL for debug, and use the debug version of the SilverScreen engine (\silver\debug\engine.exe). Set breakpoints in your code, as usual, and set the Executable program to engine.exe (pathed appropriately) by selecting the Project Settings dialog, Debug tab, General category; you should also set the working directory here, as well. In your DLL, when you are setting up command IDs for menu items, toolbar buttons and the like, the range ID_FIRST_DEVELOPER_ID through ID_LAST_DEVELOPER_ID (defined in silver.h) should be used. If you don't use IDs in this range, then MFC will think that there is no handler for them, and will disable them, probably not what you want. SilverScreen now has two ways of dealing with developer command IDs. In the first case (the default), SilverScreen will automatically inform MFC that there are handlers for these commands, and thus they will be enabled when you use them. In the second case, SilverScreen can be told to pass the address of a CCmdUi object for the command ID down to the DLL, and the DLL can determine what is to be done. This behavior is conditioned on the CV variable CV_PASSTHRU_CMDUI_ENABLE. When set to TRUE, SilverScreen will, in its command UI handler for developer's ids, call SendMessage with a message value of WM_CMDUI_ENABLE, a wParam value of the command ID, and lParam is the address of the CCmdUi object passed to SilverScreen. This message may then be intercepted in your subclassing window function, and the CCmdUi pointer used to call Enable, SetCheck or othe member functions. For example, in your initialization code, you would call:
Then, in your mainframe subclassing code, you would add the something like following code:
In Windows, the technique of window subclassing is used to intercept and perhaps filter Windows messages before they arrive at a particular window. When you subclass a Windows window, you are placing a new window procedure at the head of a chain window procedures for that window. In SilverScreen, we have designed our internal architecture so that the main frame and view windows may be subclassed safely by the DLL developer. Since Windows menus and toolbars generate WM_COMMANDs, and the only way to get your hands on what WM_COMMANDs are generated is to receive them in a window procedure, you will very likely wish do this subclassing. The MFDLL project included in the developer's engine distribution provides an example of how to go about subclassing both the main frame and the view windows. Exhibits 4 though 6 show how this subclassing is effected in SilverScreen. SilverScreen and MFC SilverScreen is built as an MFC document/view application, using SDI (Single Document Interface). This means that there SilverScreen contains a main frame window implemented by a class derived from CFrameWnd, and a single view window derived from CView. The main frame window contains the usual assortment of user interface items, including a main menu, toolbars and status bar. The view window is where SilverScreen drawings are displayed. Exhibit 3 shows the gross structure of SilverScreen's architecture. Exhibit 3. SilverScreen frame window/view window architecture In subclassing SilverScreen, the windows that we are interested in are the main frame windows and the view window. In order to subclass them, we must have their window handles. The main frame window handle may be obtained by calling the MFC function AfxFrameWnd to obtain a CFrameWnd pointer, and then calling the CFrameWnd's member function GetSafeHwnd. The return value is the main frame window handle. The view window handle maybe obtained by calling the CFrameWnd member function GetViewWindow to obtain a CView pointer, and then calling its GetSafeWnd member function. The following piece of code demonstrates the technique.
SilverScreen Input Processing Most MFC applications use the default MFC message processing mechanisms to allow messages to be routed to appropriate handler classes. However SilverScreen has a number of sitations where it is necessary to use a modal-style inchar function to control input. This same inchar is the same as is used in the SilverC library.The following diagrams illustrate the relationship between a standard MFC application (Exhibit 4), SilverScreen in standalone usage (Exhibit 5), and SilverScreen with a subclassing DLL (Exhibit 6). Note that the subclassing typically pertains to that of SilverScreens main frame and view windows. Exhibit 4. Normal MFC Event Flow Exhibit 4 shows the default flow of events through a normal MFC application. The MFC application MessagePump routing causes application events to be dispatched to the MFC window procedure, and from there they are routed out to the appropriate MFC class objects. Exhibit 5. SilverScreen Event Flow Exhibit 5 shows how the situation exists in SilverScreen when running in standalone mode. Because SilverScreen needs to implement modal-style inchar routing, both for its internal use, and for use in SilverC programs, the normal flow of messages is changed by subclassing the main frame and the view windows, passing the messages through the SilverScreen window procedure. When inchar is called, the SilverScreen window procedure picks out messages that are appropriate for returning via inchar, and sends them off that way. Other messages are passed through to MFC as normal. When inchar is not called, then message flow proceeds as before. Note that in this situation, when inchar is called, some messages are passed to MFC as well as returned via inchar, while others are not passed down to MFC. Exhibit 6. SilverScreen Event Flow with DLL Subclassing Exhibit 6 shows the situation when the DLL developer subclasses the main frame and view windows. The DLL window procedure is inserted in the subclass chain before the SilverScreen window procedure. Any messages that pass through the DLL window procedure may be filtered off by not calling the next handler in the chain; or modified by changing the parameters passed to the next message handler, or ignored altogether. One of the facilities available when subclassing is the ability to pass a message through to be returned via inchar in a SilverC program. Because the DLL window procedure is at the head of the event handling chain, it may fabricate messages and then pass them along to the SilverScreen window procedure. If the message is WM_FEED_CHAR (from silver.h) then the wParam associated with it is passed though to inchar. This is demonstrated in MFDLL, where the IDM_FILE_EXIT command is changed into a WM_FEED_CHAR message with wparam of ESCAPE. The wparam of a WM_FEED_CHAR message is passed through to inchar without translation, so in effect an ESCAPE keystroke is returned to the SilverC program that called inchar. You need not use a predefined keystroke or mouse event with WM_FEED_CHAR, any value will be returned uninterpreted. The DLL Developer Problem Solver Displaying Menu Text on the Status Line In a normal Windows application, the text associated with a menu item is displayed on the status line. In a DLL, if you have put up your own menu, the link between the menu and the status line is broken. The solution is to intercept the WM_MENUSELECT message in your subclassed window function. The wParam parameter specifies the command ID desired. Fetch the appropriate text for that command ID, and display it on the status line by using qmessage. Handling CmdUI for Developer IDs For the range of command IDs between ID_FIRST_DEVELOPER_ID and ID_LAST_DEVELOPER_ID, the default SilverScreen behavior is to enable them automatically. If you desire a different behavior, then handle the WM_CMDUI_ENABLE (0x402) message in your subclassed window procedure. The wParam parameter is the command ID, and the lParam parameter is the address of a CCmdUi object. The CCmdUi object may then be used to call Enable, or SetCheck, or whatever. This is more fully described in the section A Note on Command IDs above. Changing the Mouser Cursor Shape In a DLL, there are two ways that you can change the shape of the mouse cursor. One way is to set the view window's class cursor to the handle of the cursor you want to use. The other is to set the view window's class cursor field to NULL, and trap the WM_MOUSEMOVE command in your subclassing window procedure, and calling SetCursor with the handle of your desired cursor there. An example of the first approach: static HCURSOR view_cursor=0; // to save old view An example of the second approach. First, set the view window's class cursor to NULL: static HCURSOR view_cursor=0; // to save old view Then, in the subclassing window procedure: static HCURSOR myCursor = 0; Remember. If you change the view window's class cursor, you must set it back before returning to SilverScreen. Calling C++ from C Occasionally you will find yourself in a position, particularly if you are porting older C code, where it would be convenient, if not necessary, to call a C++ routine from C code. A legal and portable way to do this is to define a C++ function with a C interface (note that this must be a global function, and not a member function). Such a function is defined in a .cpp file (C++ source) and declared using the extern "C" mechanism. This makes the function appear to be C-callable, but inside the function, you may make use of an C++ constructs that you wish. The function may only take C-style parameters (no classes, class pointers, references, etc.). For example, suppose that you wish to call C++ function cpp_func from a .C file (C source). Then the technique is to define the function in a C++ file, but declare it in such a way that it has "C" interface. The following is an example: HEADER.H Contains the declaration for cpp_func. Note how the extern "C" mechanism is wrapped by ifdefs that allow the extern "C" to be seen by the C++ compiler (since __cplusplus is only defined in a C++ compiler), but not by a C compiler (where it not a legal C construct). #if defined(__cplusplus) CPPFILE.CPP By including HEADER.H, the cpp_func function is known to the C++ compiler to have a C interface.
CFILE.C Using HEADER.H, the cpp_func is declared for use. #include "header.h" |
| Sample Projects When you install the SilverPlus development kit, a number of sample projects are installed. In the following descriptions, the term <silver> refers to the directory where SilverScreen is installed. The SCDLL project is intended to demonstrate simple invocation of dialogs using a DLL. The DllMain version of the DLL is used. The SilverC driver file for the SCDLL project is DLLTEST.C. Installed into: <silver>\Samples\SilverPlus\SCDLL The MFDLL project is intended to demonstrate various techniques useful in DLL development:
The SilverC driver file for the MFDLL project is MFDLL.C. Installed into: <silver>\Samples\SilverPlus\MFDLL The ScriptWin project is intended to demonstrate use of the script window API, for displaying SilverScreen drawings in arbitrary windows. The SilverC driver program for the ScriptWin project is ScriptWin.c Installed into: <silver>\Samples\SilverPlus\Scriptwin The Preview project is intended to demonstrate a print preview interface implemented in a DLL. Installed into: <silver>\Samples\SilverPlus\Preview This is not really a project, but is rather set of code templates that you can use on which to base your applications. Installed into: <silver>\\DLLStuff |
| Copyright Copyright 1997-1999 by Schroff Development Corporation, Shawnee-Mission, Kansas, United States of America. All rights reserved. Disclaimer of All Warranties and Liability Schroff Development Corporation makes no warranties, either express or implied, with respect to this document or with respect to the hardware and software described in this document. Schroff Development Corporation does not guarantee, warrant or make any representation regarding the use of, or the results of the use of the information in terms of correctness, accuracy, reliability, or performance. Schroff Development Corporation assumes no liability for any direct, indirect, or consequential, special or exemplary damages, regardless of its having been advised of the possibility of such damage. |