//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1991, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Internal window object instance proc creation & maintenance.
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/window.h>
#include <owl/private/memory.h>
#include <stddef.h>
#if !defined(__CYGWIN__) && !defined(WINELIB)
# include <dos.h>
#endif
#if defined(BI_MULTI_THREAD_RTL)
# include <owl/thread.h>
#endif
#if defined(__BORLANDC__)
# pragma option -w-amp // Disable "Superfluous & with function"
# pragma option -w-ccc // Disable "Condition is always true/false"
# pragma option -w-inl // Disable "Function containing 'statment' is not expanded inline"
#endif
namespace owl {
//------------------------------------------------------------------------------
//
// Diagnostics
//
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlWin);
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlThunk, 0, 2);
//
/// Returns the internal version number. See "owl/version.h".
//
_OWLFUNC(uint32)
OWLGetVersion()
{
return OWLInternalVersion;
}
//
// Returns a reference to the global OWL module object.
//
_OWLFUNC(TModule&)
OWLGetModule()
{
return GetGlobalModule();
}
//----------------------------------------------------------------------------
/// \cond NoSuppressDoxygenWarning
//
// This internal singleton class encapsulates the global "creation window" pointer.
// The creation window is the window currently being constructed/initialized.
// A thread-safe implementation is used for multi-threaded builds.
//
class TCreationWindow
{
TCreationWindow() {} // Private to prohibit instantiation.
#if defined(BI_MULTI_THREAD_RTL)
//
// Thread-safe encapsulation of a creation window pointer.
//
struct TInstance : public TLocalObject
{
TInstance() : Window(0) {}
TWindow* Window;
};
#endif
public:
//
// Get a reference to the pointer to the creation window.
// Returns the pointer to the current window being created in this thread.
//
static TWindow*& GetInstance();
};
TWindow*& TCreationWindow::GetInstance()
{
#if defined(BI_MULTI_THREAD_RTL)
//
// Note that while this lazy initialization avoids problems with global initialization order,
// the initial call of this function is not thread-safe (pre C++11). As a work-around, we
// ensure this function is called during program start-up (single-thread, safe).
// See InitCreationWindowInstance below.
//
// The work-around can be removed when C++11 compliant compilers are mandated.
//
// Also note that the singleton construction may throw an exception. Since there is no way to
// continue without this singleton, we make no attempt to handle it here. We assume that
// the exception will terminate the program, or if it is handled, that subsequent calls will
// be retried still within a single thread.
//
static TTlsContainer<TInstance> container;
return container.Get().Window;
#else
static TWindow* creationWindow = 0;
return creationWindow;
#endif
}
namespace
{
//
// Ensure singleton initialization at start-up (single-threaded, safe).
//
TWindow*& InitCreationWindowInstance = TCreationWindow::GetInstance();
}
/// \endcond
//
/// Accessor of the "creation window", the global pointer to the current window being created.
/// Internal function.
//
_OWLFUNC(TWindow*)
GetCreationWindow()
{
return TCreationWindow::GetInstance();
}
//
/// Set the "creation window", the global pointer to the current window being created.
/// Internal function.
//
_OWLFUNC(void)
SetCreationWindow(TWindow* w)
{
// Theoretically, there should always be only one non-zero
// "creation window" pointer. i.e. we never want to have a case where
// we're holding on to a pointer waiting to be thunked, and this function
// is invoked with yet another valid pointer (Otherwise, we'll fail to
// thunk a HWND<->TWindow* pair.
//
PRECONDITION(GetCreationWindow() == 0 || w == 0);
TRACEX(OwlWin, 1, _T("SetCreationWindow: Old(") << (void*)GetCreationWindow() <<\
_T("), New(") << (void*)w << _T(')'));
TCreationWindow::GetInstance() = w;
}
//
/// Global GetWindowPtr() message ID used for registered message.
//
_OWLDATA(uint) GetWindowPtrMsgId = 0;
//----------------------------------------------------------------------------
//
// Returns the correct default window proc, depending on build options.
// Internal function.
//
WNDPROC
GetDefWindowProc()
{
// For Win32, find the default window proc in the right DLL for this build.
# if defined(UNICODE)
# define DEFWINDOWPROC "DefWindowProcW"
# else
# define DEFWINDOWPROC "DefWindowProcA"
# endif
#define DEFWINDOWPROC_MODULE _T("USER32")
HMODULE module = GetModuleHandle(DEFWINDOWPROC_MODULE);
return reinterpret_cast<WNDPROC>(GetProcAddress(module, DEFWINDOWPROC));
}
//
/// Callback process for hooking TWindow to native window.
///
/// Initial WndProc called when an OWL window is first created.
/// Subclasses the window function by installing the window proc then calls the
/// window proc to get this first message to the window.
//
LRESULT CALLBACK
TWindow::InitWndProc(HWND hWnd, UINT msg, WPARAM param1, LPARAM param2)
{
// Get the creation window (i.e. the current TWindow being initialized at this point).
// If there's no creation window, i.e. we'll be aliasing a resource, then we
// can't do anything now. Create() will take care of it later.
//
TWindow* w = GetCreationWindow();
if (!w)
return ::DefWindowProc(hWnd, msg, param1, param2);
// Reset the creation window so that it is not inadvertently used again.
//
SetCreationWindow(0);
// Initialize the window.
// Assign a default window proc before we subclass,
// otherwise we would subclass InitWndProc which would be bad.
//
w->SetHandle(hWnd);
w->SetWindowProc(GetDefWindowProc());
w->SubclassWindowFunction(); // Install the instance window proc.
// Call the instance window proc.
//
WNDPROC wndproc = w->GetWindowProc();
return (*wndproc)(hWnd, msg, param1, param2);
}
//----------------------------------------------------------------------------
//
// Implementation of the instance window proc interface using lookup
//
#if defined(BI_NOTHUNK)
//
/// The name of the Window Property used to associate a HWND and a TWindow object.
//
const char OwlObjectProperty [] = "OWL_TWINDOW"; // NB! Should be an ANSI string, not UNICODE.
//
/// Returns a common instance proc for all windows.
//
WNDPROC
TWindow::CreateInstanceProc()
{
struct TLocal
{
//
// This common window procedure is used instead of thunks. It forwards the message to the
// owning TWindow instance using lookup.
//
static LRESULT CALLBACK CommonWndProc(HWND hWnd, UINT msg, WPARAM p1, LPARAM p2)
{
// Lookup the window pointer.
//
HANDLE hObj = ::GetPropA(hWnd, OwlObjectProperty);
WARNX(OwlThunk, !hObj, 0, _T("CommonWndProc: Handle lookup failed! GetLastError: ") << ::GetLastError());
TWindow* w = reinterpret_cast<TWindow*>(hObj);
CHECK(w);
// Dispatch the message to the window.
//
return w->ReceiveMessage(hWnd, msg, p1, p2);
}
};
return &TLocal::CommonWndProc;
}
//
/// Creates an association between the window and its handle.
//
void
TWindow::InitInstanceProc()
{
PRECONDITION(GetHandle());
BOOL r = ::SetPropA(GetHandle(), OwlObjectProperty, reinterpret_cast<HANDLE>(this));
WARNX(OwlThunk, !r, 0, _T("InitInstanceProc: Handle registration failed! GetLastError: ") << ::GetLastError());
CHECK(r);
}
//
/// Removes the association between the window and its handle.
//
void
TWindow::FreeInstanceProc()
{
HANDLE h = ::RemovePropA(GetHandle(), OwlObjectProperty);
WARNX(OwlThunk, !h, 0, _T("FreeInstanceProc: Handle removal failed! GetLastError: ") << ::GetLastError());
}
#else // nothunk/thunk
//----------------------------------------------------------------------------
/// \cond NoSuppressDoxygenWarning
//
// Diagnostics macros for the allocator
// We keep track of the current and maximum number of thunks allocated.
//
#if defined(__WARN)
#define THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES\
unsigned Count;\
unsigned Maximum;
#define THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION\
, Count(0), Maximum(0)
#define THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(i)\
{\
if ((Count += i) > Maximum)\
{\
Maximum = Count;\
WARNX(OwlThunk, true, 2, _T("New thunk count record: ") << Maximum);\
}\
CHECKX(Count >= 0, _T("Negative thunk count!"));\
};
#else // no diagnostics
#define THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES
#define THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION
#define THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(i)
#endif
//
// This singelton class provides a memory heap for thunks.
// The implementation is compatible with DEP (Data Execution Prevention).
//
class TThunkAllocator
{
public:
//
// Provides access to the singleton allocator.
//
static TThunkAllocator& GetInstance()
{
//
// Note that while this lazy initialization avoids problems with global initialization order,
// the initial call of this function is not thread-safe (pre C++11). As a work-around, we
// ensure this function is called during program start-up (single-thread, safe).
// See InitThunkAllocatorInstance below.
//
// The work-around can be removed when C++11 compliant compilers are mandated.
//
// Also note that allocator construction may throw an exception. Since there is no way to
// continue without a thunk allocator, we make no attempt to handle it here. We assume that
// the exception will terminate the program, or if it is handled, that subsequent calls will
// be retried still within a single thread.
//
static TThunkAllocator TheAllocator; // initial call (construction) not thread-safe pre-C++11
return TheAllocator;
}
//
// Destructor - destroys the heap.
//
~TThunkAllocator()
{
const auto ok = Handle != nullptr;
WARN(!ok, _T("TThunkAllocator::~TThunkAllocator: Terminating due to failed precondition."));
if (!ok) std::terminate();
BOOL r = ::HeapDestroy(Handle);
WARNX(OwlThunk, !r, 0, _T("HeapDestroy failed! GetLastError: ") << ::GetLastError());
TRACEX(OwlThunk, 1, _T("TThunkAllocator destructed @") << Handle);
}
//
// Allocates memory from the private heap.
//
LPVOID Allocate(size_t size)
{
PRECONDITION(Handle);
LPVOID p = ::HeapAlloc(Handle, 0, size);
WARNX(OwlThunk, !p, 0, _T("HeapAlloc failed!"));
if (!p) TXOutOfMemory().Throw();
THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(1);
return p;
}
//
// Frees memory previously allocated from the private heap.
//
void Free(LPVOID p)
{
PRECONDITION(Handle);
BOOL r = ::HeapFree(Handle, 0, p);
WARNX(OwlThunk, !r, 0, _T("HeapFree failed! GetLastError: ") << ::GetLastError());
THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(-1);
}
private:
HANDLE Handle; // Handle for the private heap (returned by HeapCreate)
THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES
//
// Heap creation parameters
//
enum
{
Flags = 0x00040000, // = HEAP_CREATE_ENABLE_EXECUTE, // Allow thunks to run.
InitialSize = 32768, // Room for 1024 32-byte thunks.
MaxSize = 0, // Set no limits, i.e. dynamic growth. (Any benefits to limiting it?)
};
//
// Private constructor - creates the heap.
//
TThunkAllocator()
: Handle(::HeapCreate(Flags, InitialSize, MaxSize))
THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION
{
WARNX(OwlThunk, !Handle, 0, _T("HeapCreate failed! GetLastError: ") << ::GetLastError());
if (!Handle) TXOutOfMemory().Throw();
TRACEX(OwlThunk, 1, _T("TThunkAllocator constructed @") << Handle);
}
//
// Prohibit copying (i.e. not implemented)
//
TThunkAllocator(const TThunkAllocator&);
TThunkAllocator& operator=(const TThunkAllocator&);
};
namespace
{
//
// Ensure singleton initialization at start-up (single-threaded, safe).
//
TThunkAllocator& InitThunkAllocatorInstance = TThunkAllocator::GetInstance();
}
//
// This class template defines a thunk that contains an embedded TWindow pointer
// and dispatch function address.
//
template
<
const uint8* Initializer, size_t Size,
void* (*GetMessageReceiverAddress)(), int DispatchAddressIndex, int InstanceAddressIndex
>
struct TThunkT
{
struct TFactory
{
struct TCode {uint8 bytes[Size];};
TCode CodeTemplate;
//
// Links the code template to the dispatch function.
//
TFactory()
: CodeTemplate(*reinterpret_cast<TCode*>(reinterpret_cast<void*>(const_cast<uint8*>(Initializer))))
{*reinterpret_cast<void**>(&CodeTemplate.bytes[DispatchAddressIndex]) = (*GetMessageReceiverAddress)();}
};
typename TFactory::TCode Code;
static const TFactory Factory;
//
// Permanently links this thunk to the given TWindow instance.
//
TThunkT(TWindow* w)
: Code(Factory.CodeTemplate)
{*reinterpret_cast<void**>(reinterpret_cast<void*>(&Code.bytes[InstanceAddressIndex])) = w;}
//
// Class-local new operator - allocates virtual memory with DEP support.
//
void* operator new(size_t n)
{
LPVOID p = TThunkAllocator::GetInstance().Allocate (n);
CHECK(p);
TRACEX(OwlThunk, 1, _T("TThunk allocated @") << p);
return p;
}
//
// Class-local delete operator. Frees virtual memory.
//
void operator delete(void* p, size_t)
{
WARNX(OwlThunk, !p, 0, _T("TThunk::delete called with null pointer."));
if (!p) return;
TThunkAllocator::GetInstance().Free(p);
TRACEX(OwlThunk, 1, _T("TThunk deallocated @") << p);
}
};
//
// Static thunk factory
//
template <const uint8* i, size_t s, void* (*gpa)(), int dai, int iai>
const typename TThunkT<i, s, gpa, dai, iai>::TFactory
TThunkT<i, s, gpa, dai, iai>::Factory;
//
// Thunk equivalent to the following function:
//
// LRESULT CALLBACK
// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
// {
// TWindow* w = WindowInstance; // hard-coded instance
// return DispatchWindowMessage(w, msg, param1, param2);
// }
//
extern const uint8 ThunkInitializerForBorland32Bit[] =
{
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, 00000000h; instance address
0x89, 0x44, 0x24, 0x04, // mov [esp+4], eax; replace first argument
0x68, 0x00, 0x00, 0x00, 0x00, // push 00000000h; dispatch address
0xC3 // ret
};
TResult CALLBACK DispatchWindowMessage(TWindow* w, TMsgId msg, TParam1 param1, TParam2 param2)
{return w->ReceiveMessage(w->GetHandle(), msg, param1, param2);}
void* GetDispatchHelper()
{return reinterpret_cast<void*>(&DispatchWindowMessage);}
typedef TThunkT
<
ThunkInitializerForBorland32Bit, sizeof ThunkInitializerForBorland32Bit,
&GetDispatchHelper, 10, 1
>
TThunkForBorland32Bit;
//
// Thunk equivalent to the following function:
//
// LRESULT CALLBACK
// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
// {
// TWindow* w = WindowInstance; // hard-coded instance
// return w->ReceiveMessage(hwnd, msg, param1, param2);
// }
//
extern const uint8 ThunkInitializerForMicrosoft32Bit[] =
{
0xB9, 0x00, 0x00, 0x00, 0x00, // mov ecx, 00000000h; instance address
0x68, 0x00, 0x00, 0x00, 0x00, // push 00000000h; dispatch address
0xC3 // ret
};
//
// Provides access to the private member function in TWindow,
// to which we want to dispatch messages.
// Note: We assume that a member function pointer consists of an ordinary
// function pointer followed by extra stuff. We also assume that the
// member function is non-virtual.
//
void* GetMessageReceiverMemberFunctionAddress()
{
typedef TResult (TWindow::*TSignature)(HWND, TMsgId, TParam1, TParam2);
TSignature f = &TWindow::ReceiveMessage;
return *reinterpret_cast<void**>(&f);
}
typedef TThunkT
<
ThunkInitializerForMicrosoft32Bit, sizeof ThunkInitializerForMicrosoft32Bit,
&GetMessageReceiverMemberFunctionAddress, 6, 1
>
TThunkForMicrosoft32Bit;
//
// Thunk equivalent to the following function:
//
// LRESULT CALLBACK
// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
// {
// TWindow* w = WindowInstance; // hard-coded instance
// return DispatchWindowMessage(w, msg, param1, param2);
// }
//
extern const uint8 ThunkInitializerForMicrosoft64Bit[] =
{
0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, 0000000000000000h; dispatch address
0x51, // push rcx
0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, 0000000000000000h; instance address
0xC3 // ret
};
typedef TThunkT
<
ThunkInitializerForMicrosoft64Bit, sizeof ThunkInitializerForMicrosoft64Bit,
&GetDispatchHelper, 2, 13
>
TThunkForMicrosoft64Bit;
//
// The following section selects the thunk types for the active platform.
//
#if defined(_M_AMD64)
typedef TThunkForMicrosoft64Bit TThunkForMicrosoft;
typedef TThunkForMicrosoft64Bit TThunkForBorland; //To be tested
#elif defined(_M_IX86)
typedef TThunkForBorland32Bit TThunkForBorland;
typedef TThunkForMicrosoft32Bit TThunkForMicrosoft;
typedef TThunkForBorland32Bit TThunkForGnu;
#else
#error OWLNext: Unable to generate thunks for this platform.
#endif
//
// The following section selects the thunk type for the compiler.
//
#if defined(__BORLANDC__)
typedef TThunkForBorland TThunk;
#elif defined(_MSC_VER)
typedef TThunkForMicrosoft TThunk;
#elif defined(__GNUC__)
typedef TThunkForGnu TThunk;
#else
#error OWLNext: Unable to generate thunks for this compiler.
#endif
/// \endcond
//
/// Creates a thunk for this TWindow instance.
//
WNDPROC
TWindow::CreateInstanceProc()
{
return reinterpret_cast<WNDPROC>(new TThunk(this));
}
//
/// Nothing to do.
//
void
TWindow::InitInstanceProc()
{
}
//
/// Releases the thunk for this TWindow instance.
//
void
TWindow::FreeInstanceProc()
{
delete reinterpret_cast<TThunk*>(GetInstanceProc());
}
#endif // nothunk/thunk
} // OWL namespace
↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.