OWLNext    7.0
Borland's Object Windows Library for the modern age
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
owl.cpp
Go to the documentation of this file.
1//----------------------------------------------------------------------------
2// ObjectWindows
3// Copyright (c) 1991, 1996 by Borland International, All Rights Reserved
4//
5/// \file
6/// Internal window object instance proc creation & maintenance.
7//----------------------------------------------------------------------------
8
9#include <owl/pch.h>
10#include <owl/window.h>
11#include <owl/private/memory.h>
12#include <stddef.h>
13
14#if !defined(__CYGWIN__) && !defined(WINELIB)
15# include <dos.h>
16#endif
17
18#if defined(BI_MULTI_THREAD_RTL)
19# include <owl/thread.h>
20#endif
21
22#if defined(__BORLANDC__)
23# pragma option -w-amp // Disable "Superfluous & with function"
24# pragma option -w-ccc // Disable "Condition is always true/false"
25# pragma option -w-inl // Disable "Function containing 'statment' is not expanded inline"
26#endif
27
28namespace owl {
29
30//------------------------------------------------------------------------------
31
32//
33// Diagnostics
34//
38
39//
40// Returns the internal version number. See "owl/version.h".
41//
44{
45 return OWLInternalVersion;
46}
47
48//
49// Returns a reference to the global OWL module object.
50//
51_OWLFUNC(TModule&)
53{
54 return GetGlobalModule();
55}
56
57//----------------------------------------------------------------------------
58
59//
60// This internal singleton class encapsulates the global "creation window" pointer.
61// The creation window is the window currently being constructed/initialized.
62// A thread-safe implementation is used for multi-threaded builds.
63//
64class TCreationWindow
65{
66 TCreationWindow() {} // Private to prohibit instantiation.
67
68#if defined(BI_MULTI_THREAD_RTL)
69
70 //
71 // Thread-safe encapsulation of a creation window pointer.
72 //
73 struct TInstance : public TLocalObject
74 {
75 TInstance() : Window(nullptr) {}
76 TWindow* Window;
77 };
78
79#endif
80
81public:
82 //
83 // Get a reference to the pointer to the creation window.
84 // Returns the pointer to the current window being created in this thread.
85 //
86 static TWindow*& GetInstance();
87};
88
89TWindow*& TCreationWindow::GetInstance()
90{
91#if defined(BI_MULTI_THREAD_RTL)
92
93 //
94 // Note that while this lazy initialization avoids problems with global initialization order,
95 // the initial call of this function is not thread-safe (pre C++11). As a work-around, we
96 // ensure this function is called during program start-up (single-thread, safe).
97 // See InitCreationWindowInstance below.
98 //
99 // The work-around can be removed when C++11 compliant compilers are mandated.
100 //
101 // Also note that the singleton construction may throw an exception. Since there is no way to
102 // continue without this singleton, we make no attempt to handle it here. We assume that
103 // the exception will terminate the program, or if it is handled, that subsequent calls will
104 // be retried still within a single thread.
105 //
107 return container.Get().Window;
108
109#else
110 static TWindow* creationWindow = 0;
111 return creationWindow;
112#endif
113}
114
115namespace
116{
117 //
118 // Ensure singleton initialization at start-up (single-threaded, safe).
119 //
120 TWindow*& InitCreationWindowInstance = TCreationWindow::GetInstance();
121}
122
123//
124// Accessor of the "creation window", the global pointer to the current window being created.
125// Internal function.
126//
127_OWLFUNC(TWindow*)
129{
130 return TCreationWindow::GetInstance();
131}
132
133//
134// Set the "creation window", the global pointer to the current window being created.
135// Internal function.
136//
137_OWLFUNC(void)
139{
140 // Theoretically, there should always be only one non-zero
141 // "creation window" pointer. i.e. we never want to have a case where
142 // we're holding on to a pointer waiting to be thunked, and this function
143 // is invoked with yet another valid pointer (Otherwise, we'll fail to
144 // thunk a HWND<->TWindow* pair.
145 //
146 PRECONDITION(GetCreationWindow() == 0 || w == 0);
147 TRACEX(OwlWin, 1, _T("SetCreationWindow: Old(") << (void*)GetCreationWindow() <<\
148 _T("), New(") << (void*)w << _T(')'));
149 TCreationWindow::GetInstance() = w;
150}
151
152//
153// Global GetWindowPtr() message ID used for registered message
154//
156
157
158//----------------------------------------------------------------------------
159
160//
161// Returns the correct default window proc, depending on build options.
162// Internal function.
163//
166{
167 // For Win32, find the default window proc in the right DLL for this build.
168
169# if defined(UNICODE)
170# define DEFWINDOWPROC "DefWindowProcW"
171# else
172# define DEFWINDOWPROC "DefWindowProcA"
173# endif
174
175#define DEFWINDOWPROC_MODULE _T("USER32")
176
177 HMODULE module = GetModuleHandle(DEFWINDOWPROC_MODULE);
178 return reinterpret_cast<WNDPROC>(GetProcAddress(module, DEFWINDOWPROC));
179}
180
181//
182/// Callback process for hooking TWindow to native window.
183///
184/// Initial WndProc called when an OWL window is first created.
185/// Subclasses the window function by installing the window proc then calls the
186/// window proc to get this first message to the window.
187//
190{
191 // Get the creation window (i.e. the current TWindow being initialized at this point).
192 // If there's no creation window, i.e. we'll be aliasing a resource, then we
193 // can't do anything now. Create() will take care of it later.
194 //
196 if (!w)
197 return ::DefWindowProc(hWnd, msg, param1, param2);
198
199 // Reset the creation window so that it is not inadvertently used again.
200 //
201 SetCreationWindow(nullptr);
202
203 // Initialize the window.
204 // Assign a default window proc before we subclass,
205 // otherwise we would subclass InitWndProc which would be bad.
206 //
207 w->SetHandle(hWnd);
208 w->SetWindowProc(GetDefWindowProc());
209 w->SubclassWindowFunction(); // Install the instance window proc.
210
211 // Call the instance window proc.
212 //
213 WNDPROC wndproc = w->GetWindowProc();
214 return (*wndproc)(hWnd, msg, param1, param2);
215}
216
217//----------------------------------------------------------------------------
218
219//
220// Implementation of the instance window proc interface using lookup
221//
222#if defined(BI_NOTHUNK)
223
224//
225/// The name of the Window Property used to associate a HWND and a TWindow object.
226//
227const char OwlObjectProperty [] = "OWL_TWINDOW"; // NB! Should be an ANSI string, not UNICODE.
228
229//
230/// Returns a common instance proc for all windows.
231//
233TWindow::CreateInstanceProc()
234{
235 struct TLocal
236 {
237 //
238 // This common window procedure is used instead of thunks. It forwards the message to the
239 // owning TWindow instance using lookup.
240 //
242 {
243 // Lookup the window pointer.
244 //
246 WARNX(OwlThunk, !hObj, 0, _T("CommonWndProc: Handle lookup failed! GetLastError: ") << ::GetLastError());
247 TWindow* w = reinterpret_cast<TWindow*>(hObj);
248 CHECK(w);
249
250 // Dispatch the message to the window.
251 //
252 return w->ReceiveMessage(hWnd, msg, p1, p2);
253 }
254 };
255 return &TLocal::CommonWndProc;
256}
257
258//
259/// Creates an association between the window and its handle.
260//
261void
262TWindow::InitInstanceProc()
263{
265 BOOL r = ::SetPropA(GetHandle(), OwlObjectProperty, reinterpret_cast<HANDLE>(this));
266 WARNX(OwlThunk, !r, 0, _T("InitInstanceProc: Handle registration failed! GetLastError: ") << ::GetLastError());
267 CHECK(r);
268}
269
270//
271/// Removes the association between the window and its handle.
272//
273void
274TWindow::FreeInstanceProc()
275{
277 WARNX(OwlThunk, !h, 0, _T("FreeInstanceProc: Handle removal failed! GetLastError: ") << ::GetLastError());
278}
279
280#else // nothunk/thunk
281
282//----------------------------------------------------------------------------
283
284//
285// Diagnostics macros for the allocator
286// We keep track of the current and maximum number of thunks allocated.
287//
288#if defined(__WARN)
289
290#define THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES\
291 unsigned Count;\
292 unsigned Maximum;
293
294#define THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION\
295 , Count(0), Maximum(0)
296
297#define THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(i)\
298 {\
299 if ((Count += i) > Maximum)\
300 {\
301 Maximum = Count;\
302 WARNX(OwlThunk, true, 2, _T("New thunk count record: ") << Maximum);\
303 }\
304 CHECKX(Count >= 0, _T("Negative thunk count!"));\
305 };
306
307#else // no diagnostics
308
309#define THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES
310#define THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION
311#define THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(i)
312
313#endif
314
315
316//
317// This singelton class provides a memory heap for thunks.
318// The implementation is compatible with DEP (Data Execution Prevention).
319//
320class TThunkAllocator
321{
322public:
323 //
324 // Provides access to the singleton allocator.
325 //
326 static TThunkAllocator& GetInstance()
327 {
328 //
329 // Note that while this lazy initialization avoids problems with global initialization order,
330 // the initial call of this function is not thread-safe (pre C++11). As a work-around, we
331 // ensure this function is called during program start-up (single-thread, safe).
332 // See InitThunkAllocatorInstance below.
333 //
334 // The work-around can be removed when C++11 compliant compilers are mandated.
335 //
336 // Also note that allocator construction may throw an exception. Since there is no way to
337 // continue without a thunk allocator, we make no attempt to handle it here. We assume that
338 // the exception will terminate the program, or if it is handled, that subsequent calls will
339 // be retried still within a single thread.
340 //
341 static TThunkAllocator TheAllocator; // initial call (construction) not thread-safe pre-C++11
342 return TheAllocator;
343 }
344
345 //
346 // Destructor - destroys the heap.
347 //
348 ~TThunkAllocator()
349 {
350 const auto ok = Handle != nullptr;
351 WARN(!ok, _T("TThunkAllocator::~TThunkAllocator: Terminating due to failed precondition."));
352 if (!ok) std::terminate();
353
354 [[maybe_unused]] BOOL r = ::HeapDestroy(Handle);
355 WARNX(OwlThunk, !r, 0, _T("HeapDestroy failed! GetLastError: ") << ::GetLastError());
356 TRACEX(OwlThunk, 1, _T("TThunkAllocator destructed @") << Handle);
357 }
358
359 //
360 // Allocates memory from the private heap.
361 //
362 LPVOID Allocate(size_t size)
363 {
364 PRECONDITION(Handle);
365 LPVOID p = ::HeapAlloc(Handle, 0, size);
366 WARNX(OwlThunk, !p, 0, _T("HeapAlloc failed!"));
367 if (!p) TXOutOfMemory().Throw();
369 return p;
370 }
371
372 //
373 // Frees memory previously allocated from the private heap.
374 //
375 void Free(LPVOID p)
376 {
377 PRECONDITION(Handle);
378 [[maybe_unused]] BOOL r = ::HeapFree(Handle, 0, p);
379 WARNX(OwlThunk, !r, 0, _T("HeapFree failed! GetLastError: ") << ::GetLastError());
381 }
382
383private:
384 HANDLE Handle; // Handle for the private heap (returned by HeapCreate)
386
387 //
388 // Heap creation parameters
389 //
390 enum
391 {
392 Flags = 0x00040000, // = HEAP_CREATE_ENABLE_EXECUTE, // Allow thunks to run.
393 InitialSize = 32768, // Room for 1024 32-byte thunks.
394 MaxSize = 0, // Set no limits, i.e. dynamic growth. (Any benefits to limiting it?)
395 };
396
397 //
398 // Private constructor - creates the heap.
399 //
400 TThunkAllocator()
401 : Handle(::HeapCreate(Flags, InitialSize, MaxSize))
403 {
404 WARNX(OwlThunk, !Handle, 0, _T("HeapCreate failed! GetLastError: ") << ::GetLastError());
405 if (!Handle) TXOutOfMemory().Throw();
406 TRACEX(OwlThunk, 1, _T("TThunkAllocator constructed @") << Handle);
407 }
408
409 //
410 // Prohibit copying (i.e. not implemented)
411 //
412 TThunkAllocator(const TThunkAllocator&);
413 TThunkAllocator& operator=(const TThunkAllocator&);
414};
415
416namespace
417{
418 //
419 // Ensure singleton initialization at start-up (single-threaded, safe).
420 //
421 TThunkAllocator& InitThunkAllocatorInstance = TThunkAllocator::GetInstance();
422}
423
424//
425// This class template defines a thunk that contains an embedded TWindow pointer
426// and dispatch function address.
427//
428template
429<
430 const uint8* Initializer, size_t Size,
431 void* (*GetMessageReceiverAddress)(), int DispatchAddressIndex, int InstanceAddressIndex
432>
433struct TThunkT
434{
435 struct TFactory
436 {
437 struct TCode {uint8 bytes[Size];};
438 TCode CodeTemplate;
439
440 //
441 // Links the code template to the dispatch function.
442 //
443 TFactory()
444 : CodeTemplate(*reinterpret_cast<TCode*>(reinterpret_cast<void*>(const_cast<uint8*>(Initializer))))
445 {*reinterpret_cast<void**>(&CodeTemplate.bytes[DispatchAddressIndex]) = (*GetMessageReceiverAddress)();}
446 };
447
448 typename TFactory::TCode Code;
449 static const TFactory Factory;
450
451 //
452 // Permanently links this thunk to the given TWindow instance.
453 //
454 TThunkT(TWindow* w)
455 : Code(Factory.CodeTemplate)
456 {*reinterpret_cast<void**>(reinterpret_cast<void*>(&Code.bytes[InstanceAddressIndex])) = w;}
457
458 //
459 // Class-local new operator - allocates virtual memory with DEP support.
460 //
461 void* operator new(size_t n)
462 {
463 LPVOID p = TThunkAllocator::GetInstance().Allocate (n);
464 CHECK(p);
465 TRACEX(OwlThunk, 1, _T("TThunk allocated @") << p);
466 return p;
467 }
468
469 //
470 // Class-local delete operator. Frees virtual memory.
471 //
472 void operator delete(void* p, size_t)
473 {
474 WARNX(OwlThunk, !p, 0, _T("TThunk::delete called with null pointer."));
475 if (!p) return;
476 TThunkAllocator::GetInstance().Free(p);
477 TRACEX(OwlThunk, 1, _T("TThunk deallocated @") << p);
478 }
479};
480
481//
482// Static thunk factory
483//
484template <const uint8* i, size_t s, void* (*gpa)(), int dai, int iai>
485const typename TThunkT<i, s, gpa, dai, iai>::TFactory
486TThunkT<i, s, gpa, dai, iai>::Factory;
487
488//
489// Thunk equivalent to the following function:
490//
491// LRESULT CALLBACK
492// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
493// {
494// TWindow* w = WindowInstance; // hard-coded instance
495// return DispatchWindowMessage(w, msg, param1, param2);
496// }
497//
499{
500 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, 00000000h; instance address
501 0x89, 0x44, 0x24, 0x04, // mov [esp+4], eax; replace first argument
502 0x68, 0x00, 0x00, 0x00, 0x00, // push 00000000h; dispatch address
503 0xC3 // ret
504};
505
507{return w->ReceiveMessage(w->GetHandle(), msg, param1, param2);}
508
510{return reinterpret_cast<void*>(&DispatchWindowMessage);}
511
512typedef TThunkT
513<
515 &GetDispatchHelper, 10, 1
516>
518
519//
520// Thunk equivalent to the following function:
521//
522// LRESULT CALLBACK
523// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
524// {
525// TWindow* w = WindowInstance; // hard-coded instance
526// return w->ReceiveMessage(hwnd, msg, param1, param2);
527// }
528//
530{
531 0xB9, 0x00, 0x00, 0x00, 0x00, // mov ecx, 00000000h; instance address
532 0x68, 0x00, 0x00, 0x00, 0x00, // push 00000000h; dispatch address
533 0xC3 // ret
534};
535
536//
537// Provides access to the private member function in TWindow,
538// to which we want to dispatch messages.
539// Note: We assume that a member function pointer consists of an ordinary
540// function pointer followed by extra stuff. We also assume that the
541// member function is non-virtual.
542//
544{
546 TSignature f = &TWindow::ReceiveMessage;
547 return *reinterpret_cast<void**>(&f);
548}
549
550typedef TThunkT
551<
554>
556
557//
558// Thunk equivalent to the following function:
559//
560// LRESULT CALLBACK
561// WndProcThunk(HWND, UINT msg, WPARAM param1, LPARAM param2)
562// {
563// TWindow* w = WindowInstance; // hard-coded instance
564// return DispatchWindowMessage(w, msg, param1, param2);
565// }
566//
568{
569 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, 0000000000000000h; dispatch address
570 0x51, // push rcx
571 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, 0000000000000000h; instance address
572 0xC3 // ret
573};
574
575typedef TThunkT
576<
578 &GetDispatchHelper, 2, 13
579>
581
582//
583// The following section selects the thunk types for the active platform.
584//
585#if defined(_M_AMD64)
587typedef TThunkForMicrosoft64Bit TThunkForBorland; //To be tested
588#elif defined(_M_IX86)
592#else
593#error OWLNext: Unable to generate thunks for this platform.
594#endif
595
596//
597// The following section selects the thunk type for the compiler.
598//
599#if defined(__BORLANDC__)
600typedef TThunkForBorland TThunk;
601#elif defined(_MSC_VER)
602typedef TThunkForMicrosoft TThunk;
603#elif defined(__GNUC__)
604typedef TThunkForGnu TThunk;
605#else
606#error OWLNext: Unable to generate thunks for this compiler.
607#endif
608
609//
610// Creates a thunk for this TWindow instance.
611//
613TWindow::CreateInstanceProc()
614{
615 return reinterpret_cast<WNDPROC>(new TThunk(this));
616}
617
618//
619// Nothing to do.
620//
621void
622TWindow::InitInstanceProc()
623{
624}
625
626//
627// Releases the thunk for this TWindow instance.
628//
629void
630TWindow::FreeInstanceProc()
631{
632 delete reinterpret_cast<TThunk*>(GetInstanceProc());
633}
634
635#endif // nothunk/thunk
636
637} // OWL namespace
638
#define CHECK(condition)
Definition checks.h:239
#define WARNX(group, condition, level, message)
Definition checks.h:277
#define WARN(condition, message)
Definition checks.h:273
#define PRECONDITION(condition)
Definition checks.h:227
#define DIAG_DECLARE_GROUP(group)
Definition checks.h:404
#define TRACEX(group, level, message)
Definition checks.h:263
#define DIAG_DEFINE_GROUP_INIT(f, g, e, l)
Definition checks.h:429
TWindow, derived from TEventHandler and TStreamableBase, provides window-specific behavior and encaps...
Definition window.h:414
static LRESULT CALLBACK InitWndProc(HWND, UINT msg, WPARAM, LPARAM)
Callback process for hooking TWindow to native window.
Definition owl.cpp:189
HWND GetHandle() const
Returns the handle of the window.
Definition window.h:2020
#define _T(x)
Definition cygwin.h:51
Reliable platform independent header for common memory and string functions.
Object Windows Library (OWLNext Core)
Definition animctrl.h:22
TModule & OWLGetModule()
Returns a reference to the global OWL module object. When linking dynamically to OWLNext,...
Definition owl.cpp:52
UINT TMsgId
Message ID type.
Definition dispatch.h:53
WNDPROC GetDefWindowProc()
Definition owl.cpp:165
void * GetDispatchHelper()
Definition owl.cpp:509
TThunkT< ThunkInitializerForMicrosoft32Bit, sizeof ThunkInitializerForMicrosoft32Bit, &GetMessageReceiverMemberFunctionAddress, 6, 1 > TThunkForMicrosoft32Bit
Definition owl.cpp:555
void SetCreationWindow(TWindow *w)
Definition owl.cpp:138
unsigned char uint8
Definition number.h:32
unsigned long uint32
Definition number.h:34
const uint8 ThunkInitializerForMicrosoft32Bit[]
LPARAM TParam2
Second parameter type.
Definition dispatch.h:55
WPARAM TParam1
First parameter type.
Definition dispatch.h:54
OWL_DIAGINFO
Definition animctrl.cpp:14
LRESULT TResult
Result type.
Definition dispatch.h:52
TThunkT< ThunkInitializerForBorland32Bit, sizeof ThunkInitializerForBorland32Bit, &GetDispatchHelper, 10, 1 > TThunkForBorland32Bit
Definition owl.cpp:517
unsigned int uint
Definition number.h:25
TModule & GetGlobalModule()
Definition global.cpp:48
TResult CALLBACK DispatchWindowMessage(TWindow *w, TMsgId msg, TParam1 param1, TParam2 param2)
Definition owl.cpp:506
uint32 OWLGetVersion()
Get version of OWL at runtime.
Definition owl.cpp:43
const uint8 ThunkInitializerForMicrosoft64Bit[]
const uint8 ThunkInitializerForBorland32Bit[]
void * GetMessageReceiverMemberFunctionAddress()
Definition owl.cpp:543
TThunkT< ThunkInitializerForMicrosoft64Bit, sizeof ThunkInitializerForMicrosoft64Bit, &GetDispatchHelper, 2, 13 > TThunkForMicrosoft64Bit
Definition owl.cpp:580
TWindow * GetCreationWindow()
Definition owl.cpp:128
#define OWL_INI
Definition defs.h:170
#define _OWLFUNC(p)
Definition defs.h:341
#define _OWLDATA(p)
Definition defs.h:340
#define THUNK_ALLOCATOR_DIAGNOSTICS_UPDATE(i)
Definition owl.cpp:311
#define DEFWINDOWPROC
#define THUNK_ALLOCATOR_DIAGNOSTICS_INITIALIZATION
Definition owl.cpp:310
#define THUNK_ALLOCATOR_DIAGNOSTICS_VARIABLES
Definition owl.cpp:309
#define OWLInternalVersion
Definition version.h:89
Base window class TWindow definition, including HWND encapsulation.