Using Microsoft Foundation Classes with Borland C++ 3.1 Prepared by: Language Business Unit Borland International 1800 Green Hills Road PO Box 660001 Scotts Valley, CA 95067-0001 Table of Contents Table of Contents i Overview 1 Changes to MFC 2 Changes to SRC\VALIDADD.CPP 2 Changes to SRC\DOSIO_.H 3 Changes to SRC\OLEPTR_.H 3 Changes to SRC\FILE.CPP 4 Changes to SRC\WINDOW.CPP 4 Changes to SRC\WINDLGS.CPP 5 Changes to SRC\OBJECT.CPP 6 Changes to the Build Process 7 Preparing for the Build Process 7 Changes to SRC\MAKEFILE 7 Debug Information and TASM 11 Memory Model Issues 12 Building with 386^Max 12 Building the Example Programs 13 Changes to the Build Process 13 Building An Example: ABOUT2 13 Changes to SAMPLES|ABOUT2\MAKEFILE 13 Changes to SAMPLES\ABOUT2\ABOUT2.H 14 Changes to Other Example Programs 14 Changes to SAMPLES\CTRLTEST\WCLSTEST.CPP 14 Changes to SAMPLES\CTRLTEST\PAREDIT2.CPP 15 Changes to SAMPLES\CTRLTEST\FEATPEN.CPP 15 Changes to SAMPLES\CTRLTEST\CUSTLIST.CPP 15 Overview This document describes how to build and use Microsoft Foundation Class (MFC) programs with Borland C++ 3.1. This document is divided into several sections. The first describes changes to make to the MFC source files; the second, changes and suggestions for the build process; and the final section explores compiling an example providing with Microsoft C/C++ 7.0. The changes described in this document are necessary for several reasons. Some relate to non-standard syntax that Microsoft uses in their C++ compiler, while others relate to differences in assembly language extensions and makefile conventions. If you are using MFC for your Windows programs, you will find many advantages in using Borland C++ 3.1 instead of Microsoft C/C++ 7.0: * Borland provides far faster development times, with its lightning fast compiles. * Borland provides a Windows hosted IDE. * Borland provides more sophisticated Windows tools, such as Resource Workshop, WinSight, and WinSpector. * Borland provides a far more robust, compliant implementation of C++. * Borland provides templates and other advanced C++ features. * Borland provides automatic, safe precompiled headers and a smart project manager. If you are interested in a more detailed technical comparison of Borland C++ 3.1 and Microsoft C/C++ 7.0, be sure to ask for our technical whitepaper: "Borland C++ 3.1 & Application Frameworks v. Microsoft C/C++ 7.0, An Expose". Changes to MFC This section describes the various changes you will need to make to the MFC source code. Note that the line numbers mentioned are based on the version of MFC in the first shipping packages of Microsoft C/C++ 7.0. The actual line numbers are subject to change should Microsoft provide interim versions of MFC. Note further that any line number references assume that you have made all previously mentioned changes to the particular file. Changes to SRC\VALIDADD.CPP 1. Add the following lines at the top of the file: #ifdef __TURBOC__ #pragma inline #endif 2. Because "asm" in not a valid keyword within an existing inline asm statement in Borland C++ 3.1, you need to change the _asm _emit's to db's in the following manner: #ifdef __TURBOC__ db 0x66 #else _asm _emit 0x66 #endif 3. Because Borland C++ 3.1 doesn't accept inline asm labels, you need to make the following labels into C++ labels: skip_write_test skip_range_test done For example, instead of leaving the label skip_write_test in an inline asm block as illustrated below, jnz done skip_write_test: close the inline asm block, then open another after the label. jnz done } skip_write_test: _asm { The label done needs a semicolon after it as follows: } skip_range_test: _asm { inc word ptr bValid } done: ; } else 4. The comparison: cmp bReadWrite, 0 needs a type override. You need to change it to cmp word ptr bReadWrite, 0 There are 2 occurrences of this statement in the file. Also, the increment: inc bValid needs a type override. You need to change it to inc word ptr bValid There are also 2 occurrences of this statement in SRC\VALIDADD.CPP. 5. Borland C++ 3.1 doesn't allow assembler-style comments in inline assembler, so you should change all such comments to C++ style comments. Changes to SRC\DOSIO_.H 1. Change line 33 from: #define DOSCALL call DOS3Call to #define DOSCALL call far ptr DOS3Call Changes to SRC\OLEPTR_.H 1. Because Borland C++ 3.1 does not allow Microsoft-style based pointers, you should change line 17 from: ASSERT(_AFX_FP_SEG(lp) == _segname("_DATA")); to #ifndef __TURBOC__ ASSERT(_AFX_FP_SEG(lp) == _segname("_DATA")); #endif Changes to SRC\FILE.CPP 1. Add the following lines at the top of the file: #ifdef __TURBOC__ #pragma inline #endif 2. Replace line 68 with the lines: } __seek_err: _asm { 3. Replace line 87 with the lines: } __rename_err: _asm { 4. Replace line 106 with the lines: } __remove_err: _asm { 5. Replace line 129 with the lines: } __lock_err1: _asm { Changes to SRC\WINDOW.CPP The function FindMessageEntry has inline assembler statements which Borland C++ 3.1 does not recognize. You should replace this function with the following one, which is compatible with both MSC/C++ 7.0 and Borland C++ 3.1. static CMessageEntry FAR* NEAR FindMessageEntry(CMessageEntry FAR* lpEntry, UINT nMsg, UINT nID) { _asm { LES BX,lpEntry MOV AX,nMsg MOV DX,nID } __loop: _asm { MOV CX,WORD PTR ES:[BX+4] // nSig (0 => end) JCXZ __failed CMP AX,WORD PTR ES:[BX] // nMessage JE __found_1 } __next: #ifdef __TURBOC__ static const int CMESSAGEENTRYSIZE = sizeof(CMessageEntry); _asm { ADD BX,CMESSAGEENTRYSIZE JMP __loop } #else _asm { ADD BX,SIZE CMessageEntry JMP __loop } #endif __found_1: _asm { CMP DX,WORD PTR ES:[BX+2] // nID JNE __next // found a match MOV WORD PTR lpEntry,BX MOV WORD PTR lpEntry+2,ES JMP __end } __failed: _asm { XOR AX,AX MOV WORD PTR lpEntry,AX MOV WORD PTR lpEntry+2,AX } __end: return lpEntry; } Changes to SRC\WINDLGS.CPP 1. Line 705 contains a syntax error that MSC/C++ 7.0 does not detect. You need to change line 705 from: 700: BOOL 701: CFindReplaceDialog::Create(BOOL bFindDialogOnly, 702: LPCSTR lpszFindWhat, 703: LPCSTR lpszReplaceWith /* = NULL */, 704: DWORD dwFlags /* = FR_DOWN */, 705: CWnd* pParentWnd ) : CDialog((UINT)0, pParentWnd) 706: { ^ the syntax error is here to 700: BOOL 701: CFindReplaceDialog::Create(BOOL bFindDialogOnly, 702: LPCSTR lpszFindWhat, 703: LPCSTR lpszReplaceWith /* = NULL */, 704: DWORD dwFlags /* = FR_DOWN */, 705: CWnd* pParentWnd ) // : CDialog((UINT)0, pParentWnd) 706: { Changes to SRC\OBJECT.CPP 1. The function AfxNewHandler on line 179 is prototyped incorrectly to be used as a new handler. You should replace it with: void cdecl AfxNewHandler(void) { // AFX memory allocation will never return "NULL" it will always throw // a memory exception instead AfxThrowMemoryException(); } Changes to the Build Process This section describes changes you will need to make to your build process in order to build MFC applications. It covers changes to the makefile as well as particulars relating to memory models and DPMI servers. Preparing for the Build Process When building the MFC class library with Borland C++ 3.1, you must use Borland's MAKE utility. When you invoke MAKE, you need to specify all the switches you would normally use with NMAKE and append these two options: MAKE ...normal options... -D__TURBOC__ -N Since MSC/C++ 7.0 requires the INCLUDE and LIB environment variables to be set, while Borland C++ 3.1 does not, you will need to create a TURBOC.CFG file that defines these paths. Also, you will need to define some macros to control the building of the MFC class library. For example, if you installed Borland C++ 3.1 in C:\BORLANDC and MSC/C++ 7.0 in C:\C700, your TURBOC.CFG file would look like this: -Ic:\borlandc\include ; Standard headers -Ic:\c700\mfc\include ; AFX headers -Lc:\borlandc\lib ; Standard libraries -Lc:\c700\mfc\lib ; MFC class libs -D__MSC ; Tells std headers to allow MS bugs -DBASED_CODE ; Don't use handle-based pointers -D_asm=asm ; MS uses _asm, Borland uses asm -D_stat=stat The default optimization options for compiling MFC with C/C++ 7.0 are /Oselg. You can use these options with Borland C++ 3.1, or you can compile with -O2 -po for fastest code and -O1 -po for smallest code. Changes to SRC\MAKEFILE Note: all line number references that follow assume that you have made all previously mentioned changes to the file. 1. Replace lines 47-48 with the lines: !ifdef __TURBOC__ CPP=bcc CC=bcc !else CPP=cl CC=cl !endif 2. Replace line 84 with the lines: !ifdef __TURBOC__ DEBUGOPTS=/Od /N !else DEBOPTS=/Odr /f !endif 3. Replace line 96 with the lines: !ifdef __TURBOC__ DEBOPTS=$(DEBOPTS) /v !else DEBOPTS=$(DEBOPTS) /Zi !endif 4. Replace line 105 with the lines: !ifdef __TURBOC__ CVEXTRA=/v !else CVEXTRA=/Zi !endif 5. Replace lines 112-128 with the lines: !if "$(MODEL)"=="s" || "$(MODEL)"=="S" || "$(MODEL)"=="" !ifdef __TURBOC__ CL_MODEL=/ms /D_M_I86SM !else CL_MODEL=/AS !endif !else !if "$(MODEL)"=="m" || "$(MODEL)"=="M" !ifdef __TURBOC__ CL_MODEL=/mm /D_M_I86MM !else CL_MODEL=/AM !endif !else !if "$(MODEL)"=="c" || "$(MODEL)"=="C" !ifdef __TURBOC__ CL_MODEL=/mc /D_M_I86CM !else CL_MODEL=/AC !endif !else !if "$(MODEL)"=="l" || "$(MODEL)"=="L" !ifdef __TURBOC__ CL_MODEL=/ml /D_M_I86LM !else CL_MODEL=/AL !endif !else !error MODEL must be one of S, M, C, L. !endif !endif !endif !endif 6. Replace lines 155-157 with the lines: !ifdef __TURBOC__ TARGOPTS=/WSE /OW /2 MKWIN=1 EXPFLAG=/WSE !else TARGOPTS=/GA /GEs /G2 MKWIN=1 EXPFLAG=/GEe !endif 7. Replace line 184 with the lines: !ifdef __TURBOC__ CL_OPT=/w /a- $(DEBOPTS) $(TARGOPTS) $(OPT) !else CL_OPT=/W3 /Zp $(DEBOPTS) $(TARGOPTS) $(OPT) !endif 8. Replace lines 196-199 with the lines: !ifdef __TURBOC__ TARGDEFS=/D_WINDOWS TARGOPTS=/WDE /2 /D_WINDLL !else CL_MODEL=$(CL_MODEL)w TARGDEFS=/D_WINDOWS TARGOPTS=/GD /G2 # /GD will define _WINDLL !endif 9. Replace line 252 with the lines: !ifdef __TURBOC__ CPPFLAGS=$(CPPFLAGS) /H=$(PCH_FILE) !else CPPFLAGS=$(CPPFLAGS) /Yu /Fp$(PCH_FILE) !endif 10. Replace lines 264-271 with the lines: !ifdef __TURBOC__ .cpp{$D}.obj: $(CPP) @<< $(CPPFLAGS) /c /o$@ $< << .c{$D}.obj: $(CC) @<< $(CFLAGS) /c /o$@ $< << !else .cpp{$D}.obj: $(CPP) @<< $(CPPFLAGS) /c /Fo$@ $< << .c{$D}.obj: $(CC) @<< $(CFLAGS) /c /Fo$@ $< << !endif 11. Replace lines 310-312 with the lines: !ifdef __TURBOC__ $(CPP) @<< /c /H=$(PCH_FILE) $(CL_STANDARD) $(CL_MODEL) $(CL_OPT) $(DEFS) $(CVEXTRA) /c /o$D\object.obj object.cpp << !else $(CPP) @<< /c /Yc /Fp$(PCH_FILE) $(CL_STANDARD) $(CL_MODEL) $(CL_OPT) $(DEFS) $(CVEXTRA) /c /Fo$D\object.obj object.cpp << !endif 12. Replace lines 325-338 with the lines: !ifdef __TURBOC__ !if "$(CODEVIEW)"=="2" $D\memory.obj : memory.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /o$D\memory.obj memory.cpp << !ifdef MKWIN $D\winmain.obj : winmain.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /o$D\winmain.obj winmain.cpp << $D\window.obj : window.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /o$D\window.obj window.cpp << $D\winapp.obj : winapp.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /o$D\winapp.obj winapp.cpp << !endif !endif !else !if "$(CODEVIEW)"=="2" $D\memory.obj : memory.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /Fo$D\memory.obj memory.cpp << !ifdef MKWIN $D\winmain.obj : winmain.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /Fo$D\winmain.obj winmain.cpp << $D\window.obj : window.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /Fo$D\window.obj window.cpp << $D\winapp.obj : winapp.cpp $(CPP) @<< $(CPPFLAGS) $(CVEXTRA) /c /Fo$D\winapp.obj winapp.cpp << !endif !endif !endif 13. Replace lines 378-380 with the following lines: !ifdef __TURBOC__ $(CPP) @<< $(CPPFLAGS) $(EXPFLAG) /c /o$D\version.obj version.cpp << !else $(CPP) @<< $(CPPFLAGS) $(EXPFLAG) /c /Fo$D\version.obj version.cpp << !endif 14. Replace lines 394-400 with the following lines: !ifdef __TURBOC__ tlib /P128 @<< ..\lib\$(GOAL) & -+$(OBJS:.obj =.obj -+) << !else lib /PAGESIZE:128 @<< ..\lib\$(GOAL) y $(OBJS) nul ; << !endif 15. Replace all occurences of $D with $(D) Debug Information and TASM The debug information in some files that contain inline assembler statements may cause TASM to run out of memory. You can: 1. Try to give TASM more memory. 2. Compile the file outside of the MAKE procedure. 3. Try using MAKER -S to free more real-mode memory for TASM during the MAKE process. 4. Change SRC\MAKEFILE to spawn TASMX for the offending files. For example, to compile and assemble SRC\FILE.CPP, add the following line to the end of SRC\MAKEFILE: $D\file.obj : file.cpp $(CPP) $(CFLAGS) -S -n$(D) file.cpp @tasmx /ml /t $(D)\file.asm,$(D)\file.obj Memory Model Issues In medium model, the MFC class library source code relies on a non-standard extension to member function modifiers. You cannot compile the MFC class library in medium model with any compiler that does not support this non-standard extension. In small or compact model, some of the example programs will not link with Borland C++ 3.1. This is because more of the runtime library is pulled in by the link step. To run these examples, use a larger memory model. Building with 386^Max If you are running the version of 386^Max that comes with MSC/C++ 7.0, version 6.01d, you may get errors or hangs from 386max's DPMI server while building the MFC class library or example files. If this happens, insert the following line in the 386MAX.PRO file in the directory where 386MAX is installled: NODPMI This will allow Borland C++ 3.1 to use its own DPMI server and will prevent these problems. Building the Example Programs This section discusses building the MFC sample programs using Borland C++ 3.1. You will need to make changes to your build process, and in some cases fix syntax errors in the sample programs. Changes to the Build Process To build the sample applications, you will need to copy the TURBOC.CFG from the MFC\SRC directory. In addition, because the resource compiler does not explicitly path WINDOWS.H and other header files, you must change the implicit rule in your BUILTINS.MAK file so that MAKE will include the directory where the headers reside. For example, if you installed Borland C++ 3.1 in C:\BORLANDC and MSC/C++ 7.0 in C:\C700, you would change the .rc.res rule to: .rc.res: $(RC) $(RFLAGS) /I\borlandc\include;\c700\mfc\include /r $& Building An Example: ABOUT2 The makefiles for the sample programs follow the same pattern. To build a sample with Borland C++ 3.1, you will need to change each makefile. The steps for doing this follow, using ABOUT2 as an example. In addition to changing the makefile, you will have to change some of the source files. Note that the examples are all small model examples. If you have built the large model libary, you must change the makefile to use that library. If you have problems linking the examples in small model with Borland C++ 3.1, rebuild the library and the example in large model. Begin by copying MFC\SRC\TURBOC.CFG into the SAMPLES\ABOUT2 directory. Remember, you must repeat this step for each example program that you build. Changes to SAMPLES|ABOUT2\MAKEFILE 1. Replace lines 25-36 with the lines: !ifdef __TURBOC__ CPPFLAGS= /DWINVER=0x0310 /ms /w /a- /WE /OW /2 LINKFLAGS=/Twe /L\borlandc\lib;\c700\mfc\lib !if "$(DEBUG)"=="1" CPPFLAGS=/D_DEBUG $(CPPFLAGS) /Od /v /N LINKFLAGS=$(LINKFLAGS) /v LIBS=safxcwd cws !else CPPFLAGS=$(CPPFLAGS) /Oselg LINKFLAGS=$(LINKFLAGS) LIBS=safxcw cws !endif !else CPPFLAGS= /DWINVER=0x0300 /AS /W3 /Zp /GA /GEs /G2 LINKFLAGS=/NOD /ONERROR:NOEXE !if "$(DEBUG)"=="1" CPPFLAGS=/D_DEBUG $(CPPFLAGS) /Od /f /Zi LINKFLAGS=$(LINKFLAGS) /COD LIBS=safxcwd libw slibcew !else CPPFLAGS=$(CPPFLAGS) /Oselg /Gs LINKFLAGS=$(LINKFLAGS) LIBS=safxcw libw slibcew !endif !endif 2. Replace line 55 with the lines: !ifdef __TURBOC__ tlink $(LINKFLAGS) c0ws about2, about2, NUL, $(LIBS), about2.def; !else link $(LINKFLAGS) about2, about2, NUL, $(LIBS), about2.def; !endif Changes to SAMPLES\ABOUT2\ABOUT2.H 1. Line 92 has a syntax error. DECLARE_MESSAGE_MAP() cannot be followed by a semicolon. You should change it to: 90: // Message map defined in the .cpp file. 91: // 92: DECLARE_MESSAGE_MAP() 93: private: 94: short m_nCurrentColor; 95: short m_nCurrentFigure; 96: }; Changes to Other Example Programs The following describes changes you will need to make to some of the other sample programs provided with C/C++ 7.0. Changes to SAMPLES\CTRLTEST\WCLSTEST.CPP 1. Line 26 has a syntax error. DECLARE_MESSAGE_MAP() cannot be followed by a semicolon. You should change it to: 25: void OnIllegalChar(); 26: DECLARE_MESSAGE_MAP() 27: }; Changes to SAMPLES\CTRLTEST\PAREDIT2.CPP 1. Line 41 has a syntax error. DECLARE_MESSAGE_MAP() cannot be followed by a semicolon. You should change it to: 40: static LONG FAR PASCAL __export WndProcHook(HWND, UINT, UINT, LONG); 41: DECLARE_MESSAGE_MAP() Changes to SAMPLES\CTRLTEST\FEATPEN.CPP 1. Lines 38 and 97 have syntax errors. DECLARE_MESSAGE_MAP() cannot be followed by a semicolon. You should change these lines to: 37: void OnConfigure(); 38: DECLARE_MESSAGE_MAP() 39: }; 96: void OnChooseInkColor(); 97: DECLARE_MESSAGE_MAP() 98:}; Changes to SAMPLES\CTRLTEST\CUSTLIST.CPP 1. Line 121 has a syntax error. DECLARE_MESSAGE_MAP() cannot be followed by a semicolon. You should change it to: 120: void OnOK(); 121: DECLARE_MESSAGE_MAP() 122:}; 15