Category : C++ Source Code
Archive   : C_ALL.ZIP
Filename : TI1017.ASC

 
Output of file : TI1017.ASC contained in archive : C_ALL.ZIP







PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 1/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




This document covers issues related to creating OWL DLLs (v3.1)
namely, using OWL31.DLL with Windows v3.0, allocating Local
memory for TModule objects and restoring the Window Procedure of
an 'Aliased Window'.

1. OWL31.DLL may crash upon unloading when running under
Windows 3.0. This situation is related to problems with the WEP
function under Windows v3.0. Under Windows v3.0 the WEP()
function cannot contain Windows API calls, particularly KERNEL
functions. More specifically, one cannot ( should not ) call a
KERNEL function like GlobalFree. The WEP function of OWL31.DLL,
( located in the source file MODULE.CPP ) contains a call to the
delete operator, which translates into a call to the KERNEL
function GlobalFree() . This may cause a crash under Windows
3.0. The work around is to comment out that line of code, and
rebuild OWL31.DLL. After rebuilding OWL31.DLL, it will be placed
in the OWL\LIB directory. Replace the old OWL31.DLL which is
located in the BORLANDC\BIN directory.

( NOTE: For more information regarding the above mentioned
limitation of the WEP function and Windows v3.0 see the Microsoft
Document 'Windows Exit Procedure for a Dynamic-Link Library
Q66360' available from the Microsoft Software Language ( MSL )
forum on CompuServe ).

2. The DLLHELLO example that ships with OWL in BC 2.0 and 3.0
is a good demonstration of creating the required TModule object
an OWL DLL needs, and of using an Object Window alias. It works
as a good model for many DLL's that use OWL, but falls short in a
couple of specific situations.

One of these situations occurs when the DLL acts as a server for
several different EXE's. A second is when the window, that is
being aliased, persists after the DLL has been unloaded.

The first situation refers to a case where two or more EXE's link
to the same DLL and will be running simultaneously. The
traditional DLLHELLO example poses a problem for this scenario
because of one fact about DLLs: they do not own the memory which
they allocate. That is, if a DLL calls GlobalAlloc, the memory
is owned by the EXE task which called the DLL. When the EXE is















PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 2/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




terminated, the memory it allocated is freed; any subsequent
references to the freed memory will cause a general protection
fault (GP fault).

So consider this scenario: EXE A and EXE B both link to dllhello.
EXE A loads up. Along with it, DLLHELLO is loaded. The memory
allocation it does in LibMain is considered, by Windows, to be
owned by EXE A.

It allocates a TModule in LibMain as follows:

DLLHelloLib = new TModule("DLLHello", hInstance, lpCmdLine);

The TModule constructor also does the following:

lpCmdLine = _fstrdup(ACmdLine? ACmdLine: "");

So, _fstrdup will allocate the required memory to copy the
command line string.

To Windows, the allocated memory has been accessed on behalf of
EXE A.

Now suppose EXE B gets run. Since DLLHELLO is already loaded,
LibMain does not get called. Then, EXE A terminates. The
allocated memory for the TModule and the command line is freed.
The next time EXE B uses the DLL's function CreateDLLWindow,
there will be a GP fault since that function uses the pointer to
the TModule, that was freed when EXE A terminated.

There are two solutions to this problem. Memory for the DLL (
the TModule and the saved command line string ) should be
allocated from the DLL's local heap. Or, global memory can be
allocated as shared memory. Shared memory allocated in a DLL is
not freed until the DLL is unloaded, as it is considered the
owner.

The example provided below implements the first solution
mentioned above. This is done by overloading the new operator
for a class derived from TModule.

3. Another shortcoming of DLLHELLO is related to aliasing a
Window which persists after the DLL is unloaded. The key things













PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 3/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




the CreateDllWindow function does are the following:

AParentAlias = DLLHelloLib->GetParentObject(ParentHWnd);
TheWindow = new TWindow( AParentAlias, "Hello from a DLL!",
DLLHelloLib );

The GetParentObject returns a TWindow alias around ParentHWnd
(which is an HWND). The function actually subclasses the Window.
That is, it installs its own WndProc in ParentHWnd via the
following code in the TWindow( HWND, PTModule ) constructor:

DefaultProc = (WNDPROC) SetWindowLong(AnHWindow, GWL_WNDPROC,
(DWORD)GetInstance());

This means that any messages which the parent window receives now
goes through the code in DLLHELLO. If DLLHELLO is unloaded after
a call to CreateDllWindow, and the window it was aliasing still
exists, then there will most likely be a GP fault the next time a
message comes for that window. This is because the message will
be routed to the WndProc in DLLHELLO which no longer exists.

The simplest way to work around this is to restore the old
WndProc right after creating the Object Window Alias. We will do
that in the following example. The disadvantage is that we
cannot trap messages to the parent anymore. This situation does
not affect the DLLHELLO example, but a different application
might want to derive something from TWindow and use it as an
alias for an existing window.

If one needs to subclass an aliased window, and that window is
going to exist after the aliasing window goes away (for example
the window is the main window in Object Vision), then one must
ensure that the old WndProc is restored. Keep in mind though,
that at the point when you wish to restore the old 'WndProc',
someone else may be subclassing you. That is messages for the
window are being routed to some other module that is in turn
calling you.

When looking at ways to restore the old WndProc, you cannot just
delete the object Window Alias, because our destructor
does not restore the old WndProc. This is true up to and
including BC++ 3.1. This may change in the future.














PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1

OS : WIN
DATE : September 28, 1992 PAGE : 4/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




One could look at adding it to the destructor of a derived
TWindow object, or the WEP code of the DLL. WEP is called at a
sensitive point in Windows, and it adviseable to do as little as
possible there.

The following is a modified version of DLLHELLO.CPP which
allocates the TModule object in shared memory and restores the
WndProc of the Windows it aliases. If you define DEBUG when
compiling it, it includes code to make MessageBeeps when the Dll
is loaded and unloaded. It also includes an exported function
to de-reference the command line. This code can be useful for
testing purposes.



#include
#include
#include
#include
#include
#include "dllhello.h"

_CLASSDEF( TDLLModule )

// class TDLLModule forces the memory for instances of the class
// to be allocated in the Dll's local heap.
// It provides an operator new to do a local alloc for instances
// of the class, and a static function to do a local alloc for //
the command line string it will save.

class TDLLModule : public TModule
{
public:
TDLLModule( LPSTR , HMODULE , LPSTR );
~TDLLModule();
void *operator new ( unsigned );
static void * GetLocalMem( unsigned );
static void DeleteLocalMem( void *);
void operator delete (void *);
};

void * TDLLModule::GetLocalMem( unsigned n )
{













PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 5/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




// when memory is allocated as fixed from LocalAlloc, the
// handle returned may be used as a pointer

return (void *)( void near * )LocalAlloc( LPTR , n );
}


void *TDLLModule::operator new( unsigned n )
{
return GetLocalMem( n );
}

void TDLLModule::DeleteLocalMem( void * p )
{
// p is presumably a far pointer, the offset is what we
// want to free
LocalFree( ( HLOCAL ) FP_OFF( p ) );
}

void TDLLModule::operator delete( void * p )
{
DeleteLocalMem( p );
}


TDLLModule::TDLLModule( LPSTR n , HMODULE hm , LPSTR cm )
: TModule( n , hm , cm )
{
// free the memory which the TModule constructor allocated
// save the command line string. That memory is owned by
// the EXE.

farfree( lpCmdLine );

// now save the command line string in local memory.
if ( cm )
{
lpCmdLine = (LPSTR)GetLocalMem( strlen( cm ) + 1 );
if ( lpCmdLine )
{
strcpy( lpCmdLine , cm );
}
else Status = EM_INVALIDMODULE;













PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 6/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




} else {
lpCmdLine = (LPSTR)GetLocalMem( 1 );
if ( lpCmdLine )
{
*lpCmdLine = 0;
} else {
Status = EM_INVALIDMODULE;
}
}
}


TDLLModule::~TDLLModule( )
{
if ( HIWORD( lpCmdLine ) )
DeleteLocalMem( lpCmdLine );

// set lpCmdLine to NULL so that the ~TModule won't try to
// free it.
lpCmdLine = NULL;
}


PTDLLModule DLLHelloLib;

#ifdef DEBUG

// Test function to make sure that the command line string
// can dereferenced. Run two instances of a program linking
// to DLLHELLO, close the first and have the second call
// this function.
// Most often the command line string will be null.

extern "C" void far _export ShowCommandLine()
{
MessageBox( GetFocus() , DLLHelloLib->lpCmdLine , "The Dll
Command Line", MB_OK );
}

#endif

BOOL far _export CreateDLLWindow(HWND ParentHWnd)
{













PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 7/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




PTWindowsObject AParentAlias;
PTWindow TheWindow;

// save the old WndProc for this window
WNDPROC wp = (WNDPROC)GetWindowLong( ParentHWnd ,
GWL_WNDPROC );

AParentAlias = DLLHelloLib->GetParentObject(ParentHWnd);
TheWindow = new TWindow(AParentAlias, "Hello from a
DLL!", DLLHelloLib);
TheWindow->Attr.Style |= WS_POPUPWINDOW | WS_CAPTION |
WS_THICKFRAME | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX;
TheWindow->Attr.X = 100; TheWindow->Attr.Y = 100;
TheWindow->Attr.W = 300; TheWindow->Attr.H = 300;
PTWindow MadeWindow =(PTWindow)DLLHelloLib->MakeWindow(
TheWindow );

// restore the old WndProc for the main window
SetWindowLong( ParentHWnd , GWL_WNDPROC , (DWORD) wp );

return ( MadeWindow == TheWindow );
}

int FAR PASCAL LibMain(HINSTANCE hInstance, WORD /*wDataSeg*/,
WORD /* cbHeapSize */, LPSTR lpCmdLine)
{
int TheStatus;

#ifdef DEBUG
// Make beeps so we know when the Dll is loaded, helps debug
// loading and unloading problems
for ( int i = 0; i < 10 ; i ++ )
MessageBeep( 0 );
#endif

DLLHelloLib = new TDLLModule("DLLHello", hInstance,
lpCmdLine);
TheStatus = DLLHelloLib->Status;
if ( TheStatus != 0 )
{
delete DLLHelloLib;
DLLHelloLib = NULL;













PRODUCT : Borland C++ NUMBER : 1017
VERSION : 3.1
OS : WIN
DATE : September 28, 1992 PAGE : 8/8

TITLE : Creating Windows DLLs using v3.1 of Object Windows.




}
return (TheStatus == 0);
}

extern "C" int FAR PASCAL WEP ( int /*bSystemExit*/ )
{
#ifdef DEBUG
// Make beeps so we know when we've been unloaded, helps to
// debug WEP code not being called problems, and loading and
// unloading problems
for ( int i = 0; i < 10 ; i ++ )
MessageBeep( 0 );
#endif

delete DLLHelloLib;
return 1;
}


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.




























  3 Responses to “Category : C++ Source Code
Archive   : C_ALL.ZIP
Filename : TI1017.ASC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/