OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
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
#define OWLInternalVersion
Definition version.h:89
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
Base window class TWindow definition, including HWND encapsulation.