ObjectWindows Library Coding Style

Back Up Next

The ObjectWindows Team
OWL NExt Teem

  1. Purpose

  2. This document provides a set of guidelines for anyone writing ObjectWindows Library (OWL) source code or examples. Since OWL’s source code and examples are public, every effort must be made to maintain a consistent look.

    This document is more specific to the current team’s style. It is not meant to replace the Borland Framework Team Guidelines, but to clarify certain gray areas within that document for OWL.

  3. Naming Conventions

    1. Identifiers
    2. In general, identifiers are mixed case. The only exception to this are macros, which are in UPPERCASE. Any identifier global variables, class data members, function names, and member function names begin with a capital letter. Parameters to functions and local variables begin with a lower case letter and are mixed case. OWL’s name for its classes, enums, and structs begins with an initial capital T followed by its description with word capitalization. Example:

          class _OWLCLASS TWindow : public TEventHandler {
              public:
                  DataMember1;
                  static DataMember2;
                  void MemberFunc1(int arg1, char arg2);
          };

      Try not to use _ to separate words unless it is in a macro, which is all UPPERCASE.

      Identifiers should be as descriptive as possible without being too long. If you cannot find one, perhaps the class or function is doing too much.

      Boolean data variables should be named when the condition in true. For example, a boolean variable that maintains the opened state of a file would be named IsOpened instead of IsClosed. Conditionals based on the variable would be easier to read. In this case, if (IsOpened) rather than if (!IsClosed) to test if the file is open. Also, consider using enums like if (FileState == open).

      1. Resource Identifiers
      2. For cross-platform compatibility, resources should be named with a number identifier rather than a named identifier. Windows supports both methods, but OS/2 only supports number identifiers.

        The following table lists the prefix for the various resource types:

        Resource Type Prefix
        Accelerator IDA_
        Bitmap or Dib IDB_
        Cursor IDC_
        Dialog IDD_
        Font IDF_
        Icon IDI_
        Menu IDM_
        String IDS_
        VersionInfo IDV_
        Help IDH_
        User-defined IDU_
        Commands CM_
      3. Miscellaneous Identifiers
        Type Prefix
        Child windows IDW_
        Gadgets IDG_
      4. Command Member Functions
      5. Command member functions are named CmXXXX where XXXX is the command. For example:

        void
        TMyWindow::CmFileOpen()
        {
        }

        The command enablers are named CeXXXX. For example:

        void
        TMyWindow::CeFileOpen(TCommandEnabler& enabler)
        {
        }

      6. Member Functions That Handle Child Notifications

      Member functions that handle child notifications use this prefix convention:

      Control Type Prefix
      Button Bn
      Combobox Cbn
      Edit En
      Listbox Lbn
      Common Controls Prefix
      Listview Lvn
      Header Hdn
      Treeview Tvn
      Tooltip Ttn
      Common dialog Cdn
      Toolbar Tbn
      Animation control Acn
      Updown Udn
      Tab control Tcn

       

    3. Types

    OWL provides a wrapper for various data types to make it more portable and easier to read. In some cases, rather than using macros like MAKELONG, OWL provides safer inline functions. Use OWL’s data types instead of Windows’s.

    For data that needs to be specific size, use int8, int16, and int32 instead of using short, int, and long which may not have the same size depending on target platform. Correspondingly use uint8, uint16, and uint32 for unsigned versions. Use int or long, when size is a suggestion.

    OWL also provides MkUint16(), MkUint32(), LoUint16(), HiUint16(), LoUint8(), and HiUint8() inline functions to extract and put together various integer data types. Use these instead of the macros MAKELONG, MAKELPARAM, HIWORD, and LOWORD.

    OWL provides a bool data type. Depending on the compiler, it may be the instrinsic C++ type or it will be emulated. In most cases, use bool instead of BOOL. Use true and false instead of TRUE and FALSE. Be sure to keep your usage consistent. Windows declare BOOL to be synonymous with int. The size of int changes when you switch from 16-bits to 32-bits (i.e. from 2 bytes to 4 bytes). If the compiler version of bool is used, the compiler will decide the most efficient size for bool.

    OWL provides wrappers for the four types in a callback: LRESULT, UINT (message), WPARAM, and LPARAM. Use TResult, TMsgId, TParam1, and TParam2 instead.

  4. Comments

  5. Comments provide insight to the code. Write them to explain why that piece of code exists and not what it is doing. The source code explains what. C++ style comments are used instead of C’s /* */ sequence. The first words in comments are always capitalized. Class comments and block comments should be complete sentences, while statement-block and single-line comments do not need to be.

    1. Class Comments
    2. Class comments are written in the header file of the class and look like this:

      //
      // class TLayoutWindow
      // ~~~~~ ~~~~~~~~~~~~~
      // TLayoutWindow provides a parent window that manages the
      // location and sizes of its children.
      //
      class TLayoutWindow : virtual public TWindow {
          // ... Other stuff ...   
          //
      };

      The comments describe what the class encapsulates and precedes the class declaration in the header file.

    3. Block comments
    4. Block comments precede the definition of functions. They can also be used to describe global variables. They are used whenever more than one line of comments is required. These style of comments are used outside the scope of any functions or class. The always line up in the first column of each line.

      OWL does not use C’s block style comments /* */. Instead of

      /*
      This is a block comment.
      Notice it has multiple lines.
      */

      Use this style:

      //
      // This is a block comment.
      // Notice it has multiple lines.
      //

    5. Statement-Blocks Comments
    6. Statement-blocks comments break up logical groups of statements and explain the purpose of each group. These comments are used inside of functions or class declarations and always indented by some number of spaces. They are preceded by a single line of whitespace if the statement above them is on the same level of indentation. They have this format:

      void
      SomeFunction()
      {
          // Iterate through all choices
          //
          for (int i = 0; i < 0; i++) {
              // Do stuff
              //
          }
      }

    7. Single-line Comments

    Single line comments are usually very short and belong at the end of the statement. For example,

    bool done; // Gone through all the files?

  6. Layout
  7. This section discusses how each of the following items should look, i.e. its spacing, its identation, and if applicable, its order of nested items.

    1. Copyright Notice
    2. The following copyright notices must appear at the top of each file (the number of dashes, -, have been truncated to fit the page, there should be 74 of them):

      //---------------------------------------------------------- (74 dashes)
      // ObjectWindows
      // Copyright (c) 1991, 1995 by Borland International, All Rights Reserved
      //
      // Description of what is contained within the file
      //---------------------------------------------------------- (74 dashes)

      The first year is the first published date of the file and the second year is the most recent date published, which should be updated every time there is a new release of the product.

      A description of what is contained within the file follows the copyright notice. It uses the block comments style mentioned above.

    3. Files

      1. Header Files (.h)
      2. Header files have a copyright notice at the top of the file. It is followed by a sentry guard. The sentry guard prevents the file from being included twice. The name of the sentry is typically named LIBRARY_FILENAME_H, so the header file looks something like

        //
        // Copyright notice
        // ...

        #if !defined(PRODUCT_FILENAME_H)
        #define PRODUCT_FILENAME_H

        //
        // Declare stuff here
        //

        #endif // PRODUCT_FILENAME_H

        Within the guards are global variable declarations and global function prototypes. They are then followed by class declarations and inline implemenations for each class at the end. Inline functions should never be defined class inline. They should be defined as inline member functions. Do

        class TClass1 {
            public:
                void Foo();
        };

        class TClass2 {
            public:
                void Foo();
        };

        inline void
        TClass1::Foo()
        {
            // Do stuff
            //
        }

        inline void
        TClass2::Foo()
        {
            // Do stuff
            //
        }

        instead of

        class TClass1 {
            public:
                void Foo()
                    {
                        // Do stuff
                        //
                    }
        };

        class TClass2 {
            public:
                void Foo()
                    {
                        // Do stuff
                        //
                    }
        };

      3. Resource header files (.rh)
      4. Resource header files must have a copyright notice at the top of the file. It should contain only #defines for the resource compiler. It does not need sentry guards since the #defines are always the same.

      5. Resource Files (.rc)
      6. Resource files must have a copyright notice at the top of the file. It should have #includes for any resource header it needs before defining any of the resource. Resources files should be self contained, for example, it should not need to refer to mybitmap.bmp. The resource file may contain other resource files.

      7. Source Files
        Source files must have a copyright notice at the top of the file. It should include any files it needs to compile. Be sure to check if the .cpp file compiles without precompiled headers. Global variable definitions as well as class-static data members are defined near the top of the file. They are followed by function definitions. Each function definition must be preceded by block comments.
    4. Whitespace, Indentation, and Braces

    5. Whitespace is very important for readability. Cramming too much code onto a page is usually not a good idea. In general, looking at OWL’s source code or examples, it should feel open with comments sprinkled throughout. Indentation plays another key role to quickly pick groups of statements, without actually reading the code. Braces are language punctuation that helps indentation make things clearer.

      1. Tabs and Indentation
      2. OWL does not use tab characters, it always use spaces. There are no control-z characters in the file to denote an end of file. Control-z is an anachronism from CP/M that has been obsolete for 13 years.

        Each level of indentation is 2 spaces.

      3. Line Breakage
      4. A statement in C++ ends with a semicolon (;). Each statement should be on its own line. The line is usually never longer than 80 characters. If the line is too long and needs to be separated, try to separate it as you would word-wrap a paragraph. But unlike a paragraph, the continuation should be indented one level or line up vertically to the logical group in the previous line. For example,

        void
        Foo(TArgType1 arg1, TArgType2 arg2, TArgType3 arg3)
        {
        }

         

      5. Keywords and Braces
      6. The following sections discuss many of the keywords in C++ and how they are formatted in OWL.

        1. Function Definition
        2. ReturnType TypeModifier
          FuncName(ArgType1 argName1, ArgType2 argName2, ...)
          {
          }

          For constructors with initialization list, use this format:

          ReturnType FunctionModifier
          ClassName::FuncName(TArgType1 argName1, TArgType2 argName2, ...)
          :
              Initialization(List)
          {
          }

          If the line is too long, break the line at the comma and indent the next line.

        3. Operators
        4. All binary and trinary operators must have a space on each side of the operator.

          r = sqrt(x * x + y * y);
          smaller = (a < b) ? a : b;

          If a group of assignment statements follow each other and they should be logically grouped, then the assignment operator should line up vertically:

          rect.left = 0;
          rect.top = 0;
          rect.right = 100;
          rect.bottom = 100;

          In addition the group should be preceded by a block comment.

        5. Control Flow
        6. The following section describes the various control-flow statements in C++ and how they are separated in OWL. Most control flow statements are preceded and followed by a blank line. A blank line separates the various cases in a switch statement. If the control flow statement is fairly long or contains multiple closing braces (}), it is advantageous to include a single line comment that says what the braces closes when the opening brace is more than screenful or pageful away. If it is too long, consider breaking it up into logical parts.

          do/while

          do {
              statement1;
              statement2;
          } while (condition);

          while

          while (condition) {
              statement1;
              statement2;
          }

          if/else/else if

          if (condition) {
              statement1;
              statement2;
          }
          else if (condition2) {
              statement3;
              statement4;
          }
          else {
              statement5;
              statement6;
          }

          for

          int j;
          for (j = 0; j < 3; j++) {
              step1;
              step2;
          }

          or

          for (int j = 0; j < 3; j++) {
              step1;
              step2;
          }

          The biggest difference between the two example for loops is the scope of the j. In the first example, j lives outside the scope of the for loop, whereas in the second example, j lives within the scope of the for. If the statement, j = 0, follows the closing brace of the for loops, it will always work in the first example, but not in the second.

          switch/case/break/delete

          switch (expression) {
              case choice1: {
                  choice1statement1;
                  choice2statement2;
                  break;
              } // Choice1

              // more cases
              //

              default: {
                  defaultstatement1;
                  defaultstatement2;
                  break;
              } // default
          }; // switch

          The braces around the case statements are not always needed. But if one of the statement inside the case is a definition of a variable, then you will need to start a new block with the braces.

        7. Exception Handling
        8. try {
              statement1;
              statement2;
          }
          catch (condition1) {
              condition1statement1;
              condition1statement2;
          } // condition1
          catch (condition2) {
              condition2statement1;
              condition2statement2;
          } // condition2

           

        9. Enums and Unions

            enum Type {
                Constant1 = 0,
                Constant2,
            };

         

      7. Preprocessor macros
      8. The indentation for preprocessor macros is a little different from code because of # being the first character in the line requirement. So instead of putting spaces at the front of the line to indent, put the spaces after the #, but make sure the indentation levels still match the code:

        #if defined(BI_PLAT_WIN16)
        # pragma xyz
        # if 1
        # pragma morestuff
        # endif
        #endif

        void
        foo()
        {
            if (condition)
                printf(...);
        }

         

        1. Length
        2. If the length of the preprocessor macro exceeds 80 characters, use the continuation character \ to break the line. Use the same concept of breaking macros as code.

          #define WONDERFULMACRO(x) some gobbledee gook that will space \
          that continues to the next line.

           

        3. Conditionals
        4. Use the #if !defined() operator to check for negative conditions and not #ifndef. For example,

      #if !defined(BI_PLAT_WIN32)
      # error Must be compiled for 32-bits
      #endif

    6. Class and Struct Declaration
    7. //
      // class ClassName
      // ~~~~~ ~~~~~~~~~
      class ClassModifier ClassName : public BaseClass {
          public:
              // Constructors and destructors
              //

          public_data:
              // Public data that may be hidden depending on OWL_STRICT_DATA
              //

          protected:

          protected_data:

          private:

          DECLARE_RESPONSE_TABLE(ClassName);

          friend class FriendClass;
          friend function();
      };

      Any friends of a class are declared at the bottom. It is indented one level from the class keyword as are public, public_data, protected, protected_data, private, and any class specific macros such as DECLARE_RESPONSE_TABLE or DECLARE_AUTOCLASS (OCF). Each section is separated by a blank line. Each item within a section is indented one level from the section heading they are in.

    8. Pointers and References
    9. Pointers and references belong with the types and not with the variable. Do this

      int* j;
      double& xAlias = x;

      and not

      int *j;
      double &xAlias = x;

    10. Multiple Declarations

    If you have multiple declarations involving pointers and references, it is better to have each as their own separate statement, rather than lumping them together with a comma (,). Do this:

    int* a;
    int* b;
    int* c;

    and not

    int* a,
       * b,
       * c;

    and definitely do not do this:

    int* a, * b, * c;

  8. General Coding
    1. Makefiles
    2. All examples in OWL must use makefile.gen. At the top of the makefile, it should clearly state which operating systems the example works with:

      SYSTEMS = WIN16 WIN32 CON32

      as well as which memory models the example supports:

      MODELS = S M L C H D X F

      The example must also have a corresponding .ide file. Additionally, there is a readme.txt in that same directory that describes the functionality of the example. The format of the readme.txt is

      Copyright Borland International

      [any text]

      Title: [Title of readme]

      [any text]

      Keywords: [List of keywords separated by ;]

      [if last character in preceding line is ;, continue list of keywords]

      [Text of readme.txt]

      [EOF]

       

    3. Supporting Various Platforms
    4. OWL compiles equally well for both 16-bits and 32-bits. All examples should do the same unless the example demonstrates features specifically to that operating platform. In which case, the source files should test for those conditions using this method:

      #include <owl/pch.h>
      #include <owl/defs.h> // guarantee loading of the needed macros
      #if !defined(condition)
      # error This example does not support the target platform.
      #endif

      Here is a list of platforms OWL knows about and the define you can check.

      Platform Define
      Windows (all) BI_PLAT_MSW
      16-bit Windows BI_PLAT_WIN16
      32-bit Windows BI_PLAT_WIN32
      IBM OS/2 BI_PLAT_OS2
      DOS BI_PLAT_DOS

      Within each platform, you can check for the various memory models (some do not apply):

      Model Define
      Tiny BI_MODEL_TINY
      Small BI_MODEL_SMALL
      Compact BI_MODEL_COMPACT
      Medium BI_MODEL_MEDIUM
      Large BI_MODEL_LARGE
      Huge BI_MODEL_HUGE

      Alternatively, you can check for various attributes of the memory model:

      Attribute Define
      Pointer is segmented BI_PTR_16_16
      Pointer is flat BI_PTR_0_32
      Pointers are near BI_DATA_NEAR
      Pointers are far BI_DATA_FAR
      Code is near BI_CODE_NEAR
      Code is far BI_CODE_FAR

      You can also check if the module being built is for a DLL target or an EXE target:

      Target Define
      EXE BI_APP_EXECUTABLE
      DLL BI_APP_DLL

      To know whether the operating platform supports multithreaded programming, check for the BI_MULTI_THREAD define.

    5. Headers
    6. Always use the forward slash to include files in subdirectories:

      #include <owl/window.h>

       

      1. Precompiled Headers
      2. Precompiled headers save a lot of time when used properly. But double-check to make sure each .cpp file can be compiled without precompiled headers. To use precompiled headers, add this to top of each .cpp file:

        #include <owl/pch.h>

        OWL NExt: suggested using "owlpch.h", VC version optimized for use this, (VC static library currently uses <owl/pch.h>), where "owlpch.h" - your local header file for precompiled headrs, this allows more flexibity.

      3. System Headers

      OWL typically will include headers in the right order. For example, it figures out whether you want OLE or not. Because it gets the header files, you should not need to explicitly include any of the Windows header files. But if you really want to, make sure you are including OWL’s headers first. Otherwise, there may be some conflicts.

      The order you should include files is OWL, OCF, BIDS, Windows, and finally the RTL.

      OWL NExt: The order it OCF, OWL Windows and RTL. (BIDS currently not used, and OCF derived from OWL.)

    7. Warnings
    8. The example should compile without any warnings when all warnings are turned on from the compiler.

    9. Diagnostics
    10. Diagnostic macros should be used liberally. They are only enabled when a diagnostic build is requested and can be controlled by the user by editing the OWL.INI file. TRACE, CHECK, and WARN also have option forms which allow a group name and a level to be specified. This provides very fine level control over which messages are generated.

      1. PRECONDITION (BoolExp), PRECONDITIONX (BoolExp, Message)
      2. This is used to prove that the function has been called correctly. Use it to validate any argument values which should not be encountered in normal usage. This also documents the assumptions that are made. So if you assume that an integer arg will never be 0, check it with a precondition. An exception will be thrown if BoolExp is false.

      3. CHECK (BoolExp), CHECKX (BoolExp, Message)
      4. This is used to prove that the function works correctly (as opposed to being called correctly). Use it like precondition, but not for arg validation. An exception will be thrown if BoolExp is false.

        A good place to use CHECK in in places where you find a comment like "// Should never get here"

      5. WARN (BoolExp, Message), WARNX(Group, BoolExp, Level, Message)
      6. This is used for things that are possible problems. If you know it’s a problem, then use CHECK because it will throw an exception where CHECK will only display the message.

      7. TRACE (Message), TRACEX(Group, Level, Message)
      8. VALIDPTR(Pointer), VALIDORNULLPTR(Pointer)
      9. VERIFY(BoolExp), VERIFYX(BoolExp,Message)
      10. COMPILE_ASSERT(Array)
        This guy generates a compiler error such as 'Array must have at least one element' if the boolean condition argument is false.
      11. TRACE_FUNCTION(FunctionName),  TRACE_FUNCTIONP(FunctionName,Parameter), TRACE_FUNCTIONX(Group,FunctionName),  TRACE_FUNCTIONXP(Group,FunctionName,Parameter),

      Trace macros are normally used as a debugging aid and may be placed anywhere. They should normally be removed before ship unless the add to the user’s understanding of the code. For example, a TRACE statement may be left in constructors if users have had difficulty understanding the order of construction.

    11. Catch Memory Leaks
    12. All examples should be run through some form of memory checker such as CodeGuard, Bounds Checker or the Debugging version of Windows. These tools help track memory and resource leaks and help debug OWL in general.

    13. C++ Usage
      1. Extern "C"
      2. If the header can be use from C, be sure to put the proper sentries around the prototypes:

        #if defined (__cplusplus)
        extern "C" {
        #endif

        //
        // Some function prototypes here
        //

        #if defined (__cplusplus)
        }
        #endif

      3. Scoping
      4. Use the scope resolution operator (::) to refer to global objects or functions.

      5. Inline functions
      6. Whenever possible, use inline functions instead of macros.

      7. Constants
      8. Avoid using hardcoded constants. Use the const keyword to create a descriptive alias for the constant. Instead of using NULL, use 0 instead.

      9. Goto
      10. Gotos should be avoided.

      11. Casts

Do not use explicit casts anywhere. They are error prone. Instead, use the C++ new-style casts :

dynamic_cast<>()
static_cast<>()
const_cast<>()
reinterpret_cast<>()

or the macros:

TYPESAFE_DOWNCAST()
STATIC_CAST()
CONST_CAST()
REINTERPRET_CAST()

It is better to use the macros because on other compilers where the C++ new-style casts are not defined, the macros emulate the right behaviour.

      1. New and Delete
      2. All memory allocation should use new and delete instead of malloc and free.

      3. Class declaration

A class should have at the following member functions: default constructor, copy constructor, destructor (possibly virtual), assignment operator, and the comparison operator. For example, if you have a class called AClass, these are the declarations of those member functions:

class AClass {
    public:
                AClass();
                AClass(const AClass&);
        virtual ~AClass();
        AClass& operator = (const AClass&);
        int operator == (const AClass&) const;

    protected:

    private:
};

Some of the member functions may not apply in certain situations, but you should declare them and not define them to prevent the compiler from generating a default ones. If you do accidentally use them, you will get an undefined symbol from the linker. You may also change the visibility of those member functions to prevent others from using them accidentally. For example, moving the default constructor to the private section to prevent anybody from using it in either of these manners:

AClass instance;

or

AClass* instancePtr = new AClass;

Default constructor

AClass::AClass()
{
}

Copy constructor

AClass::AClass(const AClass& otherInstance)
{
    *this = otherInstance;
}

Destructor

AClass::~AClass()
{
}

Assignment operator

AClass&
AClass::operator = (const AClass& otherInstance)
{
    // copy as necessary
    //
    return *this;
}

Comparison operator

int
AClass::operator == (const AClass& otherInstance)
{
    if (compareIsEqual)
        return true;
    return false;
}

    1. Locality of Reference for Local Variables
    2. In C, variables can only be defined at the beginning of the block. In C++, a variable definition is considered as another statement, and therefore can be anywhere within the block. So keep the local variable definitions close to where they are used. Do the following

    3. No Redundant Parentheses
    4. In conditional expressions, remove any redundant parentheses. Use the C++ precedence rules guide the behavior, rather than using redundant parentheses. Use

      if (c == '\n' || c == '\t')

      rather than,

      if ((c == '\n') || (c == '\t'))

       

    5. Function Preferences
    6. For portability, use the standard C library functions over the operating system’s API. For example, use strcpy() rather than lstrcpy() unless there is a need to use the operating system’s. When using the operating system API, it should explain why it was chosen.

    7. Optimizing Memory Usage
      1. Global Variables
      2. The number of global variables should be kept to a minimum. If a global variable is needed, try to make it a global pointer to an object, rather than a global instance. Then at run-time, dynamically allocate the object and free it.

      3. Stack

Local variables consume stack space. Keep local variables to a minimum as well. If you need to have a local array or a local object that is pretty large, then use the same pointer technique for global variables.

Another technique to use for local variables is using smart pointers. With smart pointers, you get all the benefits of pointers, but you can forget about explicitly deleting the object. For example, if you have this type of code:

void
func()
{
    TSomeObject object;
    // Do stuff with object
    //
}

Change it to using pointers and references:

void
func()
{
    TSomeObject* objectPtr = new TSomeObject;
    TSomeObject& object = *objectPtr; // reference for compatability
   
    // Do stuff with object
    //
    delete objectPtr;
}

And use this form for smart pointers:

#include <owl/pointer.h>

void
func()
{
    TPointer<TSomeObject> objectPtr = new TSomeObject;
    TSomeObject& object = *objectPtr;
   
    // Do stuff with object
    //
    // No need to call delete
    //
}

A big benefit from using smart pointers is whenever the smart pointer goes out of scope, regardless if the function returned or an exception is throw, it automatically deletes the object. This is not true for the case of using only pointers because an explicit delete is required.

If you have an array, use TAPointer to get a smart pointer for the array.

  1. General OWL Usage
    1. Frame Windows
    2. In general, do not derive from frame windows such as TFrameWindow, TMDIFrame, or TDecoratedFrame. All user-specific windows should be derived from TWindow and inserted as a client to one of the frames. If there are messages needed to be caught at the frame level, then it could be a problem with the frame not fowarding the message to the client (i.e. a bug in OWL).

      If there are messages that specifically need to be caught at the application level, do not put them in the response table for the main window, put them in the derived TApplication class.

    3. OwlMain
    4. Use OwlMain. Do not create your own WinMain for your applications.

      Use OwlMain for the entry point of your DLLs as well. It will be called with the right arguments on startup of the DLL.

    5. InitMainWindow
    6. In TApplication::InitMainWindow, do not use

      MainWindow = new TFrameWindow(...);

      Instead use

      SetMainWindow(new TFrameWindow(...));

       

    7. Resources

Resources can be named with a number or a string identifier. Use the number identifier, rather than the string.

 

Revised: October 01, 2000.


Copyright © 1998-2001 Yura Bidus. All rights reserved.