I have a class being instantiated dynamically ( as the pointer is passed back and forth through a C interface ) that has a member boost thread. In a member function the following code is executed:
_thrd = boost::thread( boost::bind( &cls::thrdProc, this, other vars ) );
I know the following:
The thread was created
Because the thread procedure is a non-static member function of the same class "this" is passed as the first argument.
I've tried it with and without boost::bind.
Also in that class is a member variable of a queue I wrote. The thread procedure creates a hidden message window, a communications library is initialized, and as data is received the library sends a Windows message to the message procedure (which is in the thread). The message procedure dispatches the message to a static window procedure which redirects it to a member function of the same class with the thread. Messages are received and the thread procedure is entered. The problem is inside it the queue appears to not have been initialized ( actually all member variables of the class are invalid ). Thus, since the thread procedure and the main application code use a boost::mutex to guard the queue data after a moment ( depends on the number of packets received by the communications library ) I get a runtime error on a boost::lock_guard ( tried boost::scoped_lock too ). It's because the thread procedure calls _queue.push which tries to lock the mutex which is uninitialized so BOOST_VERIFY complains.
I've read several questions and examples that mention the thread copies arguments but the arguments aren't the issue. It seems like this is pointing to something else or wrapper class object doesn't exist. It does though and I can confirm the original pointer is valid.
Is there some other copy issue I'm not aware of?
Thanks in advance!
HEADER FILE
interface language_proxy
{
virtual int connectToQ( int * id ) = 0;
virtual int deconnectFromQ( int const id ) = 0;
virtual int startDataCollection() = 0;
virtual int stopDataCollection() = 0;
...
};
class CMyThread : public language_proxy
{
public:
CMyThread( com lib args );
int connectToQ( int * id );
int deconnectFromQ( int const id );
int startDataCollection();
int stopDataCollection();
...
protected:
boost::mutex _mutex;
CMyQueue _queue;
boost::thread _thrd;
...
uint thrdProc( com lib args );
};
EXECUTABLE FILE(S)
int CMyThread::startDataCollection()
{
// guard against multiple starts
if( boost::thread:id() == _thrd.get_id() )
{
_thrd = boost::thread( boost::bind( &CMyThread::thrdProc, this, member vars for com lib ) );
}
}
uint CMyThread::thrdProc( com lib args )
{
// create hidden messaging window
WNDCLASSEX wc = { 0 };
// many parameters can be ignored since window will be hidden
wc.cbSize = sizeof( WNDCLASSEX );
wc.lpszClassName = MSG_WND_CLS;
wc.lpfnWndProc = &CMyThread::StaticWndProc;
wc.hInstance = GetModuleHandle( 0 );
// register the class with Windows
RegisterClassEx( &wc );
// create a window based on the above class parameters
_msg_hwnd = CreateWindowEx
(
0,
wc.lpszClassName,
L"HiddenMessageWindow",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
HWND_MESSAGE,
0,
0,
this
);
// initialize com lib
// process windows messages
while( true )
{
// process available messages
if( PeekMessage( &msg, _msg_hwnd, 0, 0, PM_REMOVE ) )
{
// break out of the loop if the quit message is found
if( WM_QUIT == msg.message )
{
printf( "Quit message found\n" );
break;
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
// throws boost::thread_interrupted if the thread has been interrupted
boost::this_thread::sleep_for( boost::chrono::milliseconds( _msg_loop_dly ) );
}
}
UnregisterClass( MSG_WND_CLS, GetModuleHandle( 0 ) );
}
LRESULT CALLBACK CMyThread::StaticWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
CMyThread * pWnd = 0;
try
{
// store class pointer for window being created
if( WM_NCCREATE == uMsg )
{
// use SetWindowLongPtr to be 64-bit compatible ( a class pointer will be 64 bits and a long is only 32 bits )
pWnd = ( CMyThread * )( ( LPCREATESTRUCT )lParam )->lpCreateParams;
SetWindowLongPtr( hWnd, GWLP_USERDATA, *reinterpret_cast< LONG_PTR * >( pWnd ) );
}
// access the class pointer
else
{
pWnd = ( CMyThread * )GetWindowLong( hWnd, GWL_USERDATA );
}
// if a class pointer for the window exists, call its procedure
if( pWnd )
{
return( pWnd->WndProc( hWnd, uMsg, wParam, lParam ) );
}
}
catch( ... )
{
int x = 5;
}
// call the default window procedure
return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
}
LRESULT CALLBACK CMyThread::WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
// when a request to close the window is received, destroy the window
case WM_CLOSE:
{
DestroyWindow( hWnd );
} break;
// when a window is destroyed put a quit message in the queue to stop message processing
case WM_DESTROY:
{
PostQuitMessage( 0 );
} break;
// handle messages from com library
case UWM_RX_COM_LIB_MSG:
{
// use GetWindowLongPtr to be 64-bit compatible ( a class pointer will be 64 bits and a long is only 32 bits )
LONG_PTR lPtr = GetWindowLongPtr( hWnd, GWLP_USERDATA );
CMyThread * pWnd = reinterpret_cast< CMyThread * >( &lPtr );
pWnd->onRx();
} break;
// handle all other cases by default behaviour
default:
{
return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
}
}
return( 0 );
}
void CMyThread::onRx()
{
// extract packet(s) from com library
// add packet(s) to vector
// THIS QUEUE IS UNINITIALIZED!
_queue.push( pkts );
}
int C_Interface_create
(
com lib arguments,
language_proxy ** p
)
{
// instantiate the C++ object through the C interface
language_proxy * tmp_p = new CMyThread( com lib args );
if( tmp_p )
{
*p = tmp_p;
}
return( 0 );
}
int C_Interface_start_thread( language_proxy * p )
{
p->startDataCollection();
return( 0 );
}
// actually in a DLL but so you have some idea of what the operation flow is
void main()
{
static language_proxy * s_proxy = 0;
C_Interface_create( 1, 2, 3, &s_proxy );
c_Interface_start_thread( s_proxy );
// for debugging, endless loop to allow rx'd packets to be processed and guarantee
// s_proxy is still alive
while( 1 );
}
It was difficult to tell what your question actually was here, and there is not enough code to be sure, but judging from this:
The problem is inside it the queue [passed to the thread function by pointer] appears to not have been initialized
One possible reason for this (again, I'm guessing as I have nothing to go on), is that the this pointer you're passing to the thread is actually a pointer to a locally-instantiated automatic variable which subsequently is destroyed before your thread starts up.
For example:
void MyQueue::startTheThread()
{
_thrd = boost::thread( boost::bind( &cls::thrdProc, this, other vars ) );
}
int someFreeFunction()
{
MyQueue q;
q.startTheThread();
}
would replicate the behavior I describe. Note that what your'e actually seeing is Undefined Behavior, so anything could happen.
Related
Basically, I want to convert a process ID to a HWND. I am using this code:
DWORD dwWindowId;
CHAR pszClassName[200];
HWND hWnd;
hWnd = GetTopWindow (NULL);
while ( hWnd != NULL )
{
if ( GetClassName (hWnd, pszClassName, 200) > 0)
if ( lstrcmpiA (lpcszWindowClass, pszClassName) == 0)
if ( GetWindowThreadProcessId (hWnd, &dwWindowId) )
if ( dwWindowId == dwProcessId )
return hWnd;
hWnd = GetNextWindow ( hWnd, GW_HWNDNEXT );
}
return NULL;
This worked fine until I tried with a process was created by CreateProcess. What should I do in this case? I have the process info, such as its ID and thread ID from CreateProcess, but I still do not know how to get its hwnd. I did read this:
After you call CreateProcess(), examine the PROCESS_INFORMATION
struct pointed to by lpProcessInformation argument.
PROCESS_INFORMATION contains a handle to the Process you just
started and a Thread ID. With this information call the
GetGUIThreadInfo()function and then examine the GUITHREADINFO
struct pointed to by lpgui. GUITHREADINFO has several HWNDs. Start
with hwndActive, and call GetParent() or GetAncestor() untill the
Main window is found.
By bug_crusher
I have tried EnumChildWindows() and EnumWindows(), and they did not work.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
DWORD PID =0;
GetWindowThreadProcessId(hwnd,&PID);
if(PID == 1)
{
//,,,,,,,,
}
return TRUE;
}
But I don’t get it, can anyone explain that?
I'm a bit confused by what you're actually trying to do, but this function will build a vector of all the top-level windows belonging to the specified process.
void GetWindowsOfProcess(DWORD dwId, std::vector<HWND>& vecWindows)
{
struct WindowsOfProcess
{
std::vector<HWND>* pvecWindows;
DWORD dwProcId;
static BOOL CALLBACK EnumProc(HWND hWnd, LPARAM lParam)
{
DWORD dwProcId;
GetWindowThreadProcessId(hWnd, &dwProcId);
if (dwProcId == reinterpret_cast<WindowsOfProcess*>(lParam)->dwProcId)
reinterpret_cast<WindowsOfProcess*>(lParam)->pvecWindows->push_back(hWnd);
return TRUE;
}
WindowsOfProcess(DWORD dwId, std::vector<HWND>* pvec)
: dwProcId(dwId)
, pvecWindows(pvec)
{
EnumWindows(EnumProc, reinterpret_cast<LPARAM>(this));
}
};
WindowsOfProcess wop(dwId, &vecWindows);
}
I have a DirectX application. It's very simple but I have a problem with it. I create device, device context etc. and everything is working but when I quit, a crash occurs and the error is: HEAP: Free Heap block 3ad7d18 modified at 3ad7d98 after it was freed. It occurs only if I call IDXGISwapChain Present function at least once. If I don't, then the whole cleaning process goes well. Moreover I call Release on every COM object and the crash ALWAYS occurs only when I release the last COM object (order doesn't matter). I use DirectX 11 (Win8 SDK) on Windows 7, MS Visual 2012.
My message loop function:
int Engine::run( ){
MSG msg = { 0 };
mTimeCounter->restart( ); // doesn't matter
while( msg.message != WM_QUIT ){
if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ){
TranslateMessage( &msg );
DispatchMessage( &msg );
} else {
updateScene( mTimeCounter->deltaTime( ) );
drawScene( );
}
}
return static_cast<int>( msg.wParam );
}
updateScene do nothing now and draw scene only call this two functions:
void Engine::sceneBegin( ){
static FLOAT color[] = { 0.05f, 0.15f, 0.05f, 1.0f };
mDeviceContext->ClearRenderTargetView( mBackBufferView, color );
mDeviceContext->ClearDepthStencilView( mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0 );
}
void Engine::sceneEnd( ){
mSwapChain->Present( 0, 0 ); // crash do not occure if i comment this line of code
}
Part of messages switch:
case WM_QUIT : // i do not receive it even once because i press window's X button and it destroy window before i could receive WM_QUIT ( or not? :P )
{
DestroyWindow( mMainWnd );
}
break;
case WM_DESTROY : // i receive it if press window's X button
{
PostQuitMessage( 0 );
}
break;
return DefWindowProc( hWnd, msg, wParam, lParam );
Main function in which i initialize and start my engine:
EngTest *eng = new EngTest( );
eng->initialize( hInstance, L"Hi", show );
int r = eng->run( );
delete eng; // crash occures here but only if i call Present at least once.
Shutdown:
// called in Engine's destructor
void Engine::shutdown( ){
RELEASE_COM( mDepthStencilView );
RELEASE_COM( mDepthStencilBuffer );
RELEASE_COM( mBackBufferView );
RELEASE_COM( mSwapChain );
if( mDeviceContext )
mDeviceContext->ClearState( );
RELEASE_COM( mDeviceContext );
RELEASE_COM( mDevice );
}
RELEASE_COM
#define RELEASE_COM( x ) { if( x != NULL ) { x->Release( ); x = NULL; } }
Ok... That is really annoying. This code seems to be good and problem was in drivers or sth. When i installed new drivers and reboot PC twice then problem disappeared.
For what is worth it, this is how I fixed the exactly same issue. Occurred with just Clear() in render code and only when D3D11_CREATE_DEVICE_DEBUG was on (to add to the mystery):
case WM_CLOSE: // X clicked or Alt+F4
::DestroyWindow(hWnd); // triggers WM_DESTROY
break;
case WM_DESTROY:
gD3d11Context->ClearState();
PostQuitMessage(0); // this triggers the WM_QUIT to break the loop
break;
Plus I have my pointers wrapped in a custom ComPtr implementation. They are members of the class and self-destruct in reverse order of construction. So no ->Release() stacks :)
I have a class:
class Mouse
{
public:
int x;
int y;
void MouseMove( int x, int y );
Mouse();
};
I include it as a header file to my windows.cpp that contains WndProc and WinMain functions.
Just underneath that include I declare a static instance of my mouse:
#include "Mouse.h"
#include "Game.h"
static Mouse mouse;
My mouse methods look like this:
#include "Mouse.h"
void Mouse::MouseMove( int x, int y )
{
this->x = x;
this->y = y;
}
On my WndProc I am processing WM_MOUSEMOUSE and passing the x and y values (which have correct values in through to my MouseMove function:
case WM_MOUSEMOVE:
{
int x = ( short )LOWORD( lParam );
int y = ( short )HIWORD( lParam );
bool leftButtonDown = wParam & MK_LBUTTON;
bool rightButtonDown = wParam & MK_RBUTTON;
mouse.MouseMove( x, y );
}
break;
My MouseMove function runs through and sets this->x to x successfully and the same with the y value. That is all... done.
Now, in my windows loop I am running my game (theGame.Go):
Game theGame = Game( hWnd );
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
theGame.Go();
}
}
My game header looks like this:
#include "Mouse.h"
class Game
{
public:
Game( HWND hWnd );
~Game();
void Go();
void ComposeFrame();
LPD3DXSPRITE sprite;
LPDIRECT3DTEXTURE9 gTexture;
D3DXVECTOR3 pos;
private:
D3DGraphics gfx;
};
My Game construct looks like this:
Game::Game( HWND hWnd )
:
gfx( hWnd )
{
HRESULT result;
sprite = NULL;
result = D3DXCreateSprite( gfx.d3dDevice, &sprite );
assert( !FAILED( result ) );
gTexture = NULL;
result = D3DXCreateTextureFromFile( gfx.d3dDevice, "Images/character001.png", &gTexture );
assert( !FAILED( result ) );
gfx.d3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE);
};
The game object has no idea that the mouse object declared in my windows.cpp exists regardless of the fact I have declared it globally there. So I think to myself, I need to pass the mouse object by reference into my Game object. I start by modifying the windows loop like so:
Game theGame = Game( hWnd, &mouse );
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
theGame.Go();
}
}
Now I add a few parameters to my Game.h class so that I have a reference to the memory and can get the *mouse.x from it later...:
class Game
{
public:
Game( HWND hWnd, Mouse &mouse );
...
I go back to look at my windows loop and there's a squiggly under my call:
Game theGame = Game( hWnd, &mouse );
which states that:
6 IntelliSense: no instance of constructor "Game::Game" matches the argument list
argument types are: (HWND, Mouse *) c:\Users\James\Documents\Visual Studio 2012\Projects\Game\Game\Windows.cpp 75 17 Game
I don't get it? How on earth do I just change one global instance of my mouse and call it from within my god damn Game object :(
You've declared your Game::Game constructor as taking a reference to a Mouse but you are passing it a pointer.
Either change to:
Game::Game(HWND hWnd, Mouse* mouse);
or:
Game(hWnd, mouse);
Also note that when you call your Game constructor, you are actually making an unnecessary copy:
Game theGame = Game( hWnd, &mouse );
Instead, change this to:
Game theGame( hWnd, &mouse );
The problem that's causing your error is that your Game constructor takes a reference to Mouse:
Game( HWND hWnd, Mouse &mouse );
You, however, are passing a pointer to Mouse by taking the address of mouse:
Game theGame = Game( hWnd, &mouse );
You can change one or the other: either change the parameter type to a pointer (Mouse* mouse) or pass mouse as the argument.
However, this is an atypical approach to having a global object. By declaring mouse as static, you are giving it internal linkage and it cannot be accessed in any other translation unit. What you would generally do is have the following declaration in a single header file which you will include whenever you need access to the global mouse object:
extern Mouse mouse;
And then in a single implementation file provide the definition:
Mouse mouse;
I've already asked this question at https://gamedev.stackexchange.com/questions/50374/how-can-i-render-multiple-windows-with-directx-9-in-c but I have not yet received an answer.
I'm trying to render multiple windows, using DirectX 9 and swap chains, but even though I create 2 windows, I only see the first one that I've created. My RendererDX9 header is this:
#include <d3d9.h>
#include <Windows.h>
#include <vector>
#include "RAT_Renderer.h"
namespace RAT_ENGINE
{
class RAT_RendererDX9 : public RAT_Renderer
{
public:
RAT_RendererDX9();
~RAT_RendererDX9();
void Init(RAT_WindowManager* argWMan);
void CleanUp();
void ShowWin();
private:
LPDIRECT3D9 renderInterface; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 renderDevice; // Our rendering device
LPDIRECT3DSWAPCHAIN9* swapChain; // Swapchain to make multi-window rendering possible
WNDCLASSEX wc;
std::vector<HWND> hwindows;
void Render(int argI);
};
}
And my .cpp file is this:
#include "RAT_RendererDX9.h"
static LRESULT CALLBACK MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
namespace RAT_ENGINE
{
RAT_RendererDX9::RAT_RendererDX9() : renderInterface(NULL), renderDevice(NULL)
{
}
RAT_RendererDX9::~RAT_RendererDX9()
{
}
void RAT_RendererDX9::Init(RAT_WindowManager* argWMan)
{
wMan = argWMan;
// Register the window class
WNDCLASSEX windowClass =
{
sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0, 0,
GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
"foo", NULL
};
wc = windowClass;
RegisterClassEx( &wc );
for (int i = 0; i< wMan->getWindows().size(); ++i)
{
HWND hWnd = CreateWindow( "foo", argWMan->getWindow(i)->getName().c_str(),
WS_OVERLAPPEDWINDOW, argWMan->getWindow(i)->getX(), argWMan->getWindow(i)->getY(),
argWMan->getWindow(i)->getWidth(), argWMan->getWindow(i)->getHeight(),
NULL, NULL, wc.hInstance, NULL );
hwindows.push_back(hWnd);
}
// Create the D3D object, which is needed to create the D3DDevice.
renderInterface = (LPDIRECT3D9)Direct3DCreate9( D3D_SDK_VERSION );
// Set up the structure used to create the D3DDevice. Most parameters are
// zeroed out. We set Windowed to TRUE, since we want to do D3D in a
// window, and then set the SwapEffect to "discard", which is the most
// efficient method of presenting the back buffer to the display. And
// we request a back buffer format that matches the current desktop display
// format.
D3DPRESENT_PARAMETERS deviceConfig;
ZeroMemory( &deviceConfig, sizeof( deviceConfig ) );
deviceConfig.Windowed = TRUE;
deviceConfig.SwapEffect = D3DSWAPEFFECT_DISCARD;
deviceConfig.BackBufferFormat = D3DFMT_UNKNOWN;
deviceConfig.BackBufferHeight = 1024;
deviceConfig.BackBufferWidth = 768;
deviceConfig.EnableAutoDepthStencil = TRUE;
deviceConfig.AutoDepthStencilFormat = D3DFMT_D16;
// Create the Direct3D device. Here we are using the default adapter (most
// systems only have one, unless they have multiple graphics hardware cards
// installed) and requesting the HAL (which is saying we want the hardware
// device rather than a software one). Software vertex processing is
// specified since we know it will work on all cards. On cards that support
// hardware vertex processing, though, we would see a big performance gain
// by specifying hardware vertex processing.
renderInterface->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwindows[0],
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&deviceConfig, &renderDevice );
this->swapChain = new LPDIRECT3DSWAPCHAIN9[wMan->getWindows().size()];
this->renderDevice->GetSwapChain(0, &swapChain[0]);
for (int i = 0; i < wMan->getWindows().size(); ++i)
{
renderDevice->CreateAdditionalSwapChain(&deviceConfig, &swapChain[i]);
}
renderDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); // Set cullmode to counterclockwise culling to save resources
renderDevice->SetRenderState(D3DRS_AMBIENT, 0xffffffff); // Turn on ambient lighting
renderDevice->SetRenderState(D3DRS_ZENABLE, TRUE); // Turn on the zbuffer
}
void RAT_RendererDX9::CleanUp()
{
renderDevice->Release();
renderInterface->Release();
}
void RAT_RendererDX9::Render(int argI)
{
// Clear the backbuffer to a blue color
renderDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );
LPDIRECT3DSURFACE9 backBuffer = NULL;
// Set draw target
this->swapChain[argI]->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
this->renderDevice->SetRenderTarget(0, backBuffer);
// Begin the scene
renderDevice->BeginScene();
// End the scene
renderDevice->EndScene();
swapChain[argI]->Present(NULL, NULL, hwindows[argI], NULL, 0);
}
void RAT_RendererDX9::ShowWin()
{
for (int i = 0; i < wMan->getWindows().size(); ++i)
{
ShowWindow( hwindows[i], SW_SHOWDEFAULT );
UpdateWindow( hwindows[i] );
// Enter the message loop
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
if (PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render(i);
}
}
}
}
}
LRESULT CALLBACK MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
//CleanUp();
PostQuitMessage( 0 );
return 0;
case WM_PAINT:
//Render();
ValidateRect( hWnd, NULL );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
I've made a sample function to make multiple windows:
void RunSample1()
{
//Create the window manager.
RAT_ENGINE::RAT_WindowManager* wMan = new RAT_ENGINE::RAT_WindowManager();
//Create the render manager.
RAT_ENGINE::RAT_RenderManager* rMan = new RAT_ENGINE::RAT_RenderManager();
//Create a window.
//This is currently needed to initialize the render manager and create a renderer.
wMan->CreateRATWindow("Sample 1 - 1", 10, 20, 640, 480);
wMan->CreateRATWindow("Sample 1 - 2", 150, 100, 480, 640);
//Initialize the render manager.
rMan->Init(wMan);
//Show the window.
rMan->getRenderer()->ShowWin();
}
How do I get the multiple windows to work?
The "Swap Chain" approach you used for rendering the multiple windows is sounds good compare to creating multiple devices for multiple screens.
Have you checked the codesampler tutorial for rendering the multiple windows using swap chain. If not, pls find the below link which has a working sample project for rendering the multiple windows using swap chain. This code is purely windows Directx 9 specific but you could added your wrapper to achieve platform agnostic.
Creating Multiple Devices
Using Swap Chain
http://www.codesampler.com/dx9src/dx9src_1.htm
My application has a message-only window that is launched from a newly created thread. The thread function creates the message-only window and runs the message pump. The problem I am having is that the message pump never seems to get the WM_CLOSE message. I've removed error-handling to simplify the posted code. Does anyone know what I'm doing wrong?
Constructor:
this->m_hInstance = ::GetModuleHandle( NULL );
this->m_wcx.cbSize = sizeof(WNDCLASSEX); // size of structure
this->m_wcx.style = CS_HREDRAW | CS_VREDRAW; // initially minimized
this->m_wcx.lpfnWndProc = &WndProc; // points to window procedure
this->m_wcx.cbClsExtra = 0; // no extra class memory
this->m_wcx.cbWndExtra = 0; // no extra window memory
this->m_wcx.hInstance = m_hInstance; // handle to instance
this->m_wcx.hIcon = ::LoadIcon( NULL, IDI_APPLICATION ); // default app icon
this->m_wcx.hCursor = ::LoadCursor( NULL, IDC_ARROW ); // standard arrow cursor
this->m_wcx.hbrBackground = NULL; // no background to paint
this->m_wcx.lpszMenuName = NULL; // no menu resource
this->m_wcx.lpszClassName = s_pwcWindowClass; // name of window class
this->m_wcx.hIconSm = NULL; // search system resources for sm icon
this->m_atom = ::RegisterClassEx( &m_wcx );
this->m_hNotifyWindowThread = ::CreateThread(
NULL, // no security attributes
0, // use default initial stack size
reinterpret_cast<LPTHREAD_START_ROUTINE>(NotifyWindowThreadFn), // function to execute in new thread
NULL, // thread parameters
0, // use default creation settings
NULL // thread ID is not needed
);
Destructor:
::DestroyWindow( this->m_hWnd );
::WaitForSingleObject( this->m_hNotifyWindowThread, NW_DEFAULT_TIMEOUT ); // <-- Seems to get stuck here.
::UnregisterClass( s_pwcWindowClass, this->m_hInstance );
Thread function:
s_ptInterface->pobjNotifyWindow->m_hWnd = ::CreateWindow(
s_pwcWindowClass, // window class name
s_pwcWindowName, // window name
WS_ICONIC, // window style is minimized
0, // initial horizontal position
0, // initial vertical position
CW_USEDEFAULT, // window width
0, // window height
NULL, // no parent window
NULL, // no menu
s_ptInterface->pobjNotifyWindow->GetInstanceHandle(), // associated instance
NULL // no additional info for WM_CREATE
);
::ShowWindow( s_ptInterface->pobjNotifyWindow->GetWindowHandle(), SW_HIDE );
::UpdateWindow( s_ptInterface->pobjNotifyWindow->GetWindowHandle();
dbt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
dbt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbt.dbcc_classguid = s_guidForCP210xDevices;
m_hNotify = RegisterDeviceNotification( m_hWnd, &dbt, DEVICE_NOTIFY_WINDOW_HANDLE );
while ( (blRetVal = ::GetMessage(
&msg, // message structure
NULL, // retrieve messages for all windows on this thread
0, // lowest message value to retrieve
0 // highest message value to retrieve
)) != 0 )
{
if ( blRetVal == -1 )
{
return ::GetLastError();
}
else
{
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
}
Window procedure:
switch ( uMsg )
{
case WM_CLOSE:
if ( m_hNotify != NULL )
{
::UnregisterDeviceNotification( m_hNotify );
m_hNotify = NULL;
}
::DestroyWindow( hWnd );
break;
case WM_DESTROY:
::PostQuitMessage( 0 );
break;
case WM_DEVICECHANGE:
if ( pHeader != NULL )
{
if ( pHeader->dbch_devicetype == DBT_DEVTYP_PORT )
{
switch ( wParam)
{
case DBT_DEVICEREMOVECOMPLETE: // Device is gone
::EnterCriticalSection( &(s_ptInterface->csSerialPort) );
s_ptInterface->pobjSerialPort->Close();
::LeaveCriticalSection( &(s_ptInterface->csSerialPort) );
break;
case DBT_DEVICEARRIVAL: // System detected device
::EnterCriticalSection( &(s_ptInterface->csSerialPort) );
s_ptInterface->pobjSerialPort->Open();
::LeaveCriticalSection( &(s_ptInterface->csSerialPort) );
break;
default:
// Do nothing.
break;
}
}
}
break;
default:
// Do nothing.
break;
}
return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
WM_CLOSE is sent when Windows asks your app to close the window. When the user clicks the upper right Close button or presses Alt+F4 for example. None of that is going to happen, you called DestroyWindow(). You need to use WM_DESTROY instead. Which is fine, no need to veto the close request.