PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/18 TITLE : Borland Language News - Volume 1 Number 14 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 14 September 13, 1993 ---------------------------------------------------------------- OVERLOADING OPERATOR NEW & DELETE: Sometimes, the functionality of what happens when heapspace is either allocated or released needs to be extended. This need can either happen on a specific class-by-class basis, or perhaps the underlying global behaviour of operators new and delete is required to be modified. One of the valuable ex- tensible features of C++ is the ability for the programmer to customize the behaviour of operator new and delete. Both of these operators can be overloaded at class scope or at global scope. At class scope, operator new and delete can be modified to be: #include #include #include class foo { public: void* operator new(size_t); void operator delete(void*); }; void* foo::operator new(size_t n) { printf("foo::operator new()\n"); void *p = malloc(n); return p; } void foo::operator delete(void *p) { printf("foo::operator delete()\n"); free(p); } main() { foo *p = new foo; delete p; return 0; } PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/18 TITLE : Borland Language News - Volume 1 Number 14 Execution of this program works as expected, but interesting results will occur when function main() is changed to allo- cate an array as opposed to a single instance of class foo. main() { const int n = 4; foo *p = new foo[n]; delete [] p; return 0; } In this case, no output is generated at all. The overloaded versions of operator new and delete are not executed. Sec- tion 5.3.3 of the ARM states that allocation of arrays will use global operator new (::new) as opposed to the local op- erator new defined in class foo. This can be seen to be true when both global operators new and delete are overloaded as in the following: #include #include #include void* operator new(size_t n) { printf("global operator new()\n"); void *p = malloc(n); return p; } void operator delete(void *p) { printf("global operator delete()\n"); free(p); } class foo { public: void* operator new(size_t); void operator delete(void*); }; void* foo::operator new(size_t n) { printf("foo::operator new()\n"); void *p = malloc(n); return p; PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/18 TITLE : Borland Language News - Volume 1 Number 14 } void foo::operator delete(void *p) { printf("foo::operator delete()\n"); free(p); } main() { const int n = 4; foo *p = new foo[n]; delete [] p; return 0; } All possibilities of heapspace allocation can be tested by extending the calls to operator new and delete in function main() to a variety of scenarios. Below is a small sample of what can be done. main() { const int n = 4; foo *p = new foo[n]; char *q = new char[n]; int *r = new int; foo *s = new foo; delete [] p; delete [] q; delete r; delete s; return 0; } Note that printf() has been used throughout in the overload- ing of operator new instead of taking advantage of the streams library. This is nessitated since cout (class o- stream_withassign) uses global operator new internally. One issue not considered in this discussion thus far is what happens when heap exhaustion occurs. By default, when the heapspace request cannot be honored, the function pointer _new_handler is checked to see if it points to a function or is set to 0. If _new_handler points to a valid function (which can be set by calling set_new_handler()), the func- tion is executed; if _new_handler is 0, then new returns PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/18 TITLE : Borland Language News - Volume 1 Number 14 the value 0. The following code illustrates how _new_handler can be used with the default version of oper- ator new: #include #include #include void empty() { cerr << "heapspace exhausted" << endl; exit(1); } void main() { set_new_handler(&empty); while (1) { const int n = 4096; // 4k long *p = new long[n]; } } This can be simplistically integrated into a custom ver- sion of global operator new as in: #include #include #include #include #include void* operator new(size_t n) { printf("global operator new\n"); void *p; if ((p = malloc(n)) == 0 && _new_handler != 0) (*_new_handler)(); return p; } void empty() { cerr << "heapspace exhausted" << endl; exit(1); } void main() { set_new_handler(&empty); PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/18 TITLE : Borland Language News - Volume 1 Number 14 while (1) { const int n = 4096; // 4k long *p = new long[n]; } } HOT ISSUES The header file defs.h from Rogue Wave Tools.h++ contains these lines: // The following is for your convenience and can be // removed if it conflicts with some other package: #define Boolean RWBoolean If you are using Tools.h++ with Turbo Vision, it will lead to conflicts since TV has the following definition for Boolean: enum Boolean { TRUE, FALSE }; Commenting out the #define in defs.h from Tools.h++ resolves the conflict. In the case where this was initially encountered, a virtual function being redefined in a derived class was seen by the compiler to have a different signature. Thus, instead of be- ing a redefinition, it hid the base class function. Then a call to (baseClass*)->virtualFunction got the base class function instead of the intended derived class' version. DOS 1. When using findfirst with the FA_DIREC attribute, I get regular DOS files in addition to the directories. How do I get just the directories? Using the FA_DIREC attribute returns both directories and all normal files. If a listing of only directories is desired, the attribute returned in the ffblk will need to be compared to FA_DIREC. ie. PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/18 TITLE : Borland Language News - Volume 1 Number 14 #include #include #include main() { struct ffblk fb; int done; printf ("Directories found:\n"); done = findfirst ("*.*", &fb, FA_DIREC); while (!done) { if (fb.ff_attrib & FA_DIREC) printf ("%s\n", fb.ff_name); done = findnext (&fb); } return 0; } OS/2 1. With the 16-bit versions of the SQL library/DLL there are 4 issues that need to be addressed to use the DLL from a 32- bit Borland C++ for OS/2 applications. a. Define all pointers passed to the functions in the SQL library to be __far16. The SQL Database functions must also be defined as __cdecl in addition to the __far16 definition. b. Define all typedefs and #defines that are using 'int' to be 'short'. In the 16-bit OS/2 an int is 16-bits where in the 32-bit OS/2 an int is 32-bits. The short in 32-bit OS/2 is 16-bits and thus is the same size as a 16-bit OS/2 'int'. c. When using the 16-bit version of the SQL Database DLL, to functions may be called that require a parameter which is a 16-bit function. Two such functions in the SQL DLL library are dberrhandler() and dbmsghandler(). These function require a single parameter which is 16-bit function. Borland C++ for OS/2 will not generate 16-bit functions to satisfy a 16-bit function parameter to dberrhandler() or dbmsghandler(). A workaround would be to use a third-party compiler that generates 16-bit PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/18 TITLE : Borland Language News - Volume 1 Number 14 functions and write two extra functions in a DLL. The two functions can then be exported and passed in to the SQL API calls. d. Any functions from the SQL Library that take a variable number of arguments cannot be called with variable argu- ment lists. The function dbfcmd() cannot be called with variable argument lists. The current version of Borland C++ for OS/2 will not support variable argument lists as parameters to the 16-bit functions. The dbfcmd() func- tion can be called with the two defined variables(the first two variables) but calling the function with any further arguments will cause unpredictable results from the SQL DLL. When attempting to use the function dbfcmd() which takes a variable argument list the fol- lowing compiler errors may result: 'Multiple declar- ations for "dbfcmd$32" in module xxx.c' and 'Multiple declarations for "dbfcmd$16" in modules xxx.c'. The dbfcmd part of the symbol is the actual _far16 variable argument list function being called in the source code. WINDOWS 1. How do I detect a keypress within a modal dialog? It is often necessary to detect when a particular key has been pressed while a dialog is active. For example, a pro- grammer may decide to provide 'HELP' when the user presses the F1 key; in another case, a coder reported that users requested that the Enter key be used to move around the controls (instead of the standard Windows behaviour which activates the default push button when the Enter key is pressed). For modeless dialogs it's relatively easy to load an accelerator table and call the TranslateAccelera- tor() function from the GetMessage/PeekMessage loop and receive WM_COMMAND messages for specific keys. Modal dia- log boxes, on the other hand, do not allow for accelerators. This article provides a method to detect keys while a modal dialog is active via a message filter. Message filters are hooks except that they are task-spe- cific. Hence, they do not need to be in a DLL. The filter is installed with a call to the SetWindowsHookEx() API, as PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/18 TITLE : Borland Language News - Volume 1 Number 14 illustrated in the example below: lpHkProc = MakeProcInstance( ( FARPROC )MsgFilterProc, hInstance ); if ( lpHkProc ) { hHook = SetWindowsHookEx( WH_MSGFILTER, ( HOOKPROC )lpHkProc, hInstance, GetCurrentTask() ); ... Once the filter is installed, the dialog can then be cre- ated. The message procedure (MsgFlterProc() in the example above) will now be called whenever the dialog retrieves a message (it will also be called whenever a message box or menu retrieves a message). This allows us to detect when a WM_KEYDOWN is retrieved and take special action. For exam- ple, the MsgFilterProc() of our example, catches the WM_KEYDOWN with VK_F1 and VK_RETURN (i.e. when the F1 and CR keys are pressed). The MsgFilterProc() posts a user-defined message when one of those events is detected and then re- turns TRUE, indicating that this event has been handled and should not be processed any further. Upon receiving the posted user-defined message, our dialog displays a "HELP" message and simulates control-tabbing for the F1 and CarriageReturn keys respectively. Once the dialog terminates, the filter must be removed. The following code illustrates what is needed for a dialog which detects both the F1 and ENTER keys. /* -----------[ Sample Application Code ] ---------- */ #include #include // ================================================== // // // // -------------------------------------------------- // LRESULT CALLBACK _export MsgFilterProc( int nCode, WPARAM wParam, LPARAM lParam ); PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/18 TITLE : Borland Language News - Volume 1 Number 14 // ================================================== // // // // -------------------------------------------------- // static HHOOK hHook = 0; static FARPROC lpHkProc = NULL; // ================================================== // // // // -------------------------------------------------- // BOOL SetMsgHook( HINSTANCE hInstance ) { assert( hHook == 0 ); assert( lpHkProc == NULL ); lpHkProc = MakeProcInstance((FARPROC)MsgFilterProc, hInstance ); if ( lpHkProc ) { hHook = SetWindowsHookEx( WH_MSGFILTER, ( HOOKPROC )lpHkProc, hInstance, GetCurrentTask() ); if ( !hHook ) { FreeProcInstance( lpHkProc ); lpHkProc = NULL; } } return( hHook != NULL ); } // ================================================== // // // // -------------------------------------------------- // BOOL RemoveMsgHook() { assert( hHook ); assert( lpHkProc ); return( UnhookWindowsHookEx( hHook ) ); } // ================================================== // // // // -------------------------------------------------- // PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/18 TITLE : Borland Language News - Volume 1 Number 14 const UINT WM_HELP = ( WM_USER*2 ); const UINT WM_ENTER = ( WM_USER*2 + 1 ); HWND hwndDialog = NULL; // ================================================== // // // // -------------------------------------------------- // LRESULT CALLBACK _export MsgFilterProc( int nCode, WPARAM wParam, LPARAM lParam ) { if ( nCode != MSGF_DIALOGBOX ) return CallNextHookEx( hHook, nCode, wParam, lParam ); MSG *pMsg= ( MSG* )lParam; if ( pMsg->message == WM_KEYDOWN ) { if ( pMsg->wParam == VK_F1 ) { PostMessage( hwndDialog, WM_HELP, 0, 0 ); return TRUE; // We handled it... } if ( pMsg->wParam == VK_RETURN && GetParent( pMsg->hwnd ) == hwndDialog ) { PostMessage( hwndDialog, WM_ENTER, 0, 0 ); return TRUE; // We handled it... } } return CallNextHookEx( hHook, nCode, wParam, lParam ); } // ================================================== // // // // -------------------------------------------------- // BOOL CALLBACK _export MainDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ); PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/18 TITLE : Borland Language News - Volume 1 Number 14 // ================================================== // // // // -------------------------------------------------- // int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { SetMsgHook( hInstance ); FARPROC lpDlgProc; lpDlgProc = MakeProcInstance( ( FARPROC )MainDlgProc, hInstance ); DialogBox( hInstance, MAKEINTRESOURCE( 1000 ), NULL, ( DLGPROC )lpDlgProc ); if ( lpDlgProc ) FreeProcInstance( lpDlgProc ); RemoveMsgHook(); return( 0 ); } // ================================================== // // // // -------------------------------------------------- // BOOL CALLBACK _export MainDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { switch ( message ) { case WM_INITDIALOG: hwndDialog = hDlg; return TRUE; case WM_HELP: { HWND hwnd = GetFocus(); PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/18 TITLE : Borland Language News - Volume 1 Number 14 if ( hwnd && GetParent( hwnd )==hDlg ) { char buffer[80]; int nId = GetDlgCtrlID( hwnd ); wsprintf( buffer, "Ctrl Id (%d)", nId ); MessageBox( hDlg, buffer, "Help Requested", MB_OK ); } } break; case WM_ENTER: PostMessage( hDlg, WM_NEXTDLGCTL, 0, 0 ); break; case WM_COMMAND: switch( wParam ) { case IDOK: case IDCANCEL: EndDialog( hDlg, wParam ); default: return( FALSE ); } default: return( FALSE ); } return( TRUE ); } /* HOOKDIAL.RC ******\ #include 1000 DIALOG 33, 19, 239, 108 STYLE DS_ABSALIGN | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CAPTION "Main Dialog" BEGIN PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 13/18 TITLE : Borland Language News - Volume 1 Number 14 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 181, 26, 30, 14 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 181, 74, 29, 14 CONTROL "&Enabled", 101, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 22, 17, 42, 12 EDITTEXT 102, 70, 21, 85, 17, ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP CONTROL "", 103, "EDIT", ES_LEFT | ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 70, 55, 85, 28 END \* HOOKDIAL.RC ******/ TURBO VISION 1. Turbo Vision with the Borland C++ and Application Frameworks 3.1 package seems very slow, much slower that the previous version, in fact. Can this be fixed? The problem is twofold: one, snow checking was inadvertent- ly turned on by default and two, the new operator that is in TV.LIB has debugging code compiled in with it. Fortun- ately, the solution to these problems is very simple. Since Borland C++ and Application Frameworks customer, comes with source code, this is what needs to be done to fix this prob- lem: a. To fix the snow checking problem, go to the TVISION\SOURCE directory of the product and edit the file TSCREEN.CPP. Change line 42 from -- Boolean near TScreen::checkSnow = True; to -- Boolean near TScreen::checkSnow = False; PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 14/18 TITLE : Borland Language News - Volume 1 Number 14 Recompile the module with the following command line. Adjustments may need to be made to the include directory path for the compiler to find TV.H. (See the Borland C++ 3.1 _User's Guide_ p. 172 and pp. 174-177 for fur- ther details.) bcc -c -P -O1 -ml tscreen.cpp tlib /0 ..\lib\tv.lib -+tscreen.obj This will compile TSCREEN.CPP and add it to the Turbo Vision Library for later use. If only the Integrated Development (IDE) environment was installed, these li- braries can be rebuilt by setting the following options and recompiling TSCREEN.CPP. Once the module has been compiled, execute the TLIB command shown above. Options | Compiler | Optimizations: -- set to smallest code. Options | Compiler | Code Generation: -- set for large memory model. Options | Directories: -- set the correct include paths. b. To remove the debugging code from the new operator, re- compile the source for that module. Recompile NEW.CPP with the following command line. As in (a) above, the include paths may need to be altered. bcc -c -P -O1 -ml -DNDEBUG new.cpp tlib /0 ..\lib\tv.lib -+new.obj The -DNDEBUG defines the NDEBUG symbol and will cause compilation of NEW.CPP without the extra debugging code. If only the IDE was installed, set the options listed above, and also set the following. Once the module has been compiled, execute the TLIB command shown above. Options | Compiler | Code Generation | Defines: -- add NDEBUG to this line. Upon performing these two steps, the Turbo Vision li- PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 15/18 TITLE : Borland Language News - Volume 1 Number 14 brary should again be performing at the speed it had in previous versions. PARADOX ENGINE 1. Paradox Engine functions do not display any error messages, but their state (valid/invalid) can be obtained. These re- turn values should be checked upon every Engine function call. The following is an example of implementing this error-checking in C: #include #include // add #include for Database Framework // applications. #define PXErr(parm) PXError( __FILE__, __LINE__, \ #parm, parm ) int GlobalPXErr; PXCODE PXError( char *module, int line, char *function, int retval ) { if ( retval == PXSUCCESS ) { GlobalPXErr = PXSUCCESS; return retval; } printf( "module: %s, function %s, line %d - error# %d -> %s\n", module, function, line, retval, PXErrMsg( retval ) ); // Change PXErrMsg to PXOopErrMsg for Database // Framework applications. GlobalPXErr = retval; return retval; } main() { PXErr( PXInit() ); PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 16/18 TITLE : Borland Language News - Volume 1 Number 14 PXErr( PXExit() ); return 0; } PASCAL 1. How can I access environment variables within my Turbo Pascal application? The following accesses the environment present in the program's PSP. Program Test; {$M 10240,0,0} Uses Dos; type String80 = string[80]; var Spec: string; Function AccessEnvironment( Search : string80 ): string; var I, Seg: integer; TempS : string[80]; begin Seg:=MemW[PrefixSeg:$2C]; Temps:=''; For I:=1 to Length(Search) do Temps:=Temps+#0; I:=0; repeat TempS:=Copy(Temps, 2, 255) + chr(mem[seg:i]); inc(I); until TempS = Search; TempS:=''; Repeat Temps:=Temps + Chr(mem[seg:i]); inc(I); Until Mem[seg:i] = 0; AccessEnvironment:=Temps; end; PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 17/18 TITLE : Borland Language News - Volume 1 Number 14 begin Spec := accessenvironment( 'COMSPEC=' ); writeln( Spec ); Spec := accessenvironment( 'PATH=' ); writeln( Spec ); end. QUIZ Last week's quiz/exercise asked for a class 'foo' which given the code -- foo i = 0, j = 0; j = i + 15 + 2 + 1; meant that 'i' would be 'or'ed with bit 15 and bit 2 and bit 1. One possible solution would be -- #include class foo { unsigned v; public: foo() { } foo(const unsigned i) { v = i; } unsigned value() { return v; } unsigned value() const { return v; } foo operator+(const int i) { return v | 1 << i; } }; inline ostream& operator<<(ostream &out, const foo &f) { return out << "v = " << hex << f.value(); } main() { foo i = 0, j; // no need to initialize 'j' j = i + 15 + 2 + 1; cout << j << endl; return 0; } For this week's quiz/exercise and in the spirit of the lead article in this issue of the newsletter, overload operators PRODUCT : Borland Languages NUMBER : 8513 VERSION : All OS : All DATE : October 25, 1993 PAGE : 18/18 TITLE : Borland Language News - Volume 1 Number 14 new and delete to use only statically allocated memory -- ie. use a static array defined at compile-time as the 'new' heap. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1550 Using buffers with I/O string streams. 1552 Turbo Vision example of determining cursor position. 1553 How to override freeItem for Collections. 1554 Turbo Vision context sensitive help example. 1555 Example of a restricted Turbo Vision InputLine. 1556 How to trace into a run-time library function. 1557 Creating red error dialog boxes in Turbo Vision. 1558 Saving/restoring the mouse state in Turbo Vision. 1559 Creating a pointer to BIOS while in protected mode. 1561 How to boot clean. These documents can be found on either LIB-2 of BCPPDOS or BCPPWIN on CompuServe, Borland's DLBBS at (408)431-5096, TechFAX at (800)822-4269, or OAS at (408)431-5250. DISCLAIMER: You have the right to use this technical information subject to the terms of the No-Nonsense License Statement that you received with the Borland product to which this information pertains.