Use of static dll

Started by
9 comments, last by jpetrie 14 years, 1 month ago
This might be the wrong place to ask, in which case I hope a moderator might be so kind as to move the thread to where it belongs. The question: I have a dll in which I have a class I want to use in another project. I prefix the class in the dll like so: class __declspec(dllexport) MyClass I then import the headers to the other project and reference the dll, thus using the class as I normaly would. This works as it should. In MyClass is a Init-metod which takes a single HWND and attemps to make a DX10 device and swapchain. However, whenever I try to use the Hwnd, it is always NULL, ie as if not initialized. This is not true, since I use the same HWND for creating the window the device is to be running in (this occurs in the main project, not in the dll), prior to calling the Init-metod. So the question is: Is HWND always null? Or is there something wrong in how I use the class from the dll?
Advertisement
Moving to General Programming.

There's some problem with the way you're using your class. How do you know the HWND is null? Have you tried printing the value out? Are you absolutely sure that the variable is non-NULL when you call the function in the DLL? Have you checked with your debugger?
Quote:
How do you know the HWND is null?


It's a global variable and I set it to NULL like so:
g_hWnd = NULL;

Quote:
Have you tried printing the value out?


No, but when I step through the breakpoints it never changes to anything other than 0x00000000 {unused=???} so I suppose it's just NULL? Or?

Quote:
Are you absolutely sure that the variable is non-NULL when you call the function in the DLL?


It never changes when looking at the breakpoints, altough is _must_ change to something since the window i create with it displays correctly.

Quote:
Have you checked with your debugger?


Well, only the watchwindows, using breakpoints, and the Callstack. Other than that is beyond me, I'm affraid.



Quote:Original post by Bangladesh
Quote:
Are you absolutely sure that the variable is non-NULL when you call the function in the DLL?


It never changes when looking at the breakpoints, altough is _must_ change to something since the window i create with it displays correctly.
What do you use the HWND for exactly? Can we see some code? There's plenty of Win32 functions which will succeed with a NULL HWND.
Certainly, but it's nothing fancy. First I create a window with this function:


HWND g_hWnd = NULL;

// Make window
bool WinInit(HWND hWnd, HINSTANCE hInstance, int width, int height)
{
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));

wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = TEXT("BlackBird");
wcex.hIconSm = 0;
if( !RegisterClassEx(&wcex) )
return FALSE;

RECT rc = { 0, 0, width, height };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
hWnd = CreateWindow( TEXT("BlackBird"), TEXT("BlackBird"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL,
hInstance, NULL);

if( !hWnd )
return false;

ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
return true;
};


Then I try to create a device and swapchain from a class defined in my dll. The method that does this looks like this:

HRESULT Ghost3D::Init(HWND hWnd, UINT windowWidth, UINT windowHeight, bool window)
{
if(!hWnd)
{ return E_FAIL; }

m_hWndMain = hWnd;
m_nBufferWidth = windowWidth;
m_nBufferHeight = windowHeight;
m_bWindowed = window;
return Go();
};

HRESULT Ghost3D::Go(void)
{
// DirectX
// Define a swapchain
//
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof(sd) );
sd.BufferCount = 1;
sd.BufferDesc.Width = m_nBufferWidth;
sd.BufferDesc.Height = m_nBufferHeight;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = m_hWndMain;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = m_bWindowed;

// Test if hardware supports DX10, if not we have to run on the Reference-driver
HRESULT hr = E_FAIL;
D3D10_DRIVER_TYPE driverType = D3D10_DRIVER_TYPE_NULL;
D3D10_DRIVER_TYPE driverTypes[] =
{
D3D10_DRIVER_TYPE_HARDWARE,
D3D10_DRIVER_TYPE_REFERENCE
};
UINT numDriverTypes = sizeof(driverTypes)/sizeof(driverTypes[0]);
for(UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
{
// Try to create the driver
driverType = driverTypes[driverTypeIndex];
hr = D3D10CreateDeviceAndSwapChain(NULL, driverType, NULL, 0, D3D10_SDK_VERSION,
&sd, &m_pSwapChain, &m_pDevice);
if(SUCCEEDED(hr))
{
if(driverType == D3D10_DRIVER_TYPE_HARDWARE)
{
//Log(m_pLogFile, "GhostD3D:\t Using HardWare-enabled rendering");
}
else
{
//Log(m_pLogFile, "GhostD3D:\t Using Reference-rendering");
}
break;
}
}
if( FAILED(hr) )
return hr;
};


Edit:
However it never goes further than the first hWnd-check when I look if it's NULL or not. But if hWnd can be null without failing then I guess I've been shooting myself in the foot all along.
Quote:
In MyClass is a Init-metod which takes a single HWND and attemps to make a DX10 device and swapchain. However, whenever I try to use the Hwnd, it is always NULL, ie as if not initialized.

It's a global variable and I set it to NULL like so:
g_hWnd = NULL;

Based on the code you provided, g_hWnd is always null because you never assign anything to it. All the code you showed manipulated HWNDs that are passed as parameters to the functions in question or are members of the class. Doesn't seem like you even need the global HWND.
Quote:Original post by jpetrie
Based on the code you provided, g_hWnd is always null because you never assign anything to it. All the code you showed manipulated HWNDs that are passed as parameters to the functions in question or are members of the class. Doesn't seem like you even need the global HWND.


Sorry, the code was taken out of context: I acctually feed the g_hWnd to the function when I create the window. Here is the actual code which I think should work. Or do I have it backwards?

HWND g_hWnd = NULL;

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
int width = 640;
int height = 480;

if (!WinInit(g_hWnd, hInstance, width, height))
return 0;

Ghost3D* pDevice = new Ghost3D();
if(FAILED(pDevice->Init(g_hWnd, (UINT)width, (UINT)height, true)))
{
}

MSG msg = {0};
while(msg.message != WM_QUIT)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Do other stuff
if(!Render())
return 0;
}

delete pDevice; pDevice = NULL;
return 0;
};
hWnd = CreateWindow

That line assigns the local copy of the HWND parameter to the return value of CreateWindow(). The value you passed in is unaffected.

If you want to change the value of the HWND passed, pass it as a pointer or (preferably) reference instead, or return the HWND from the function.
Quote:
Sorry, the code was taken out of context: I acctually feed the g_hWnd to the function when I create the window. Here is the actual code which I think should work. Or do I have it backwards?

But you never assign to the global HWND. You assign to copies of that HWND, because it's passed by value to all your functions. Also, if you just pass this HWND around like this there really isn't any reason to make it global. It can be a local variable in WinMain.

A HWND is an opaque type, but in practice it is really a pointer to some structure. Observe:
#include <iostream>int i = 0;void f(int* q) {  q = &i}int main() {  int* p = 0;  f(p);  std::cout << p;}

This prints 0, not some arbitrary address (of the global i), because we modified q, a copy of p -- not p itself. This code is the same as:
#include <iostream>typedef int* HWND;int i = 0;void f(HWND q) {  q = &i}int main() {  HWND p = 0;  f(p);  std::cout << p;}

and is conceptually equivalent to your own code. You're copy the HWND each time you pass it -- not passing it by reference.

You could modify your code to pass the HWND around by reference (HWND& in the parameter list), but that isn't a very elegant design. Better to encapsulate everything -- the creation of the window, the device setup, et cetera -- in the Ghost3D object (or some object that Ghost3D will depend on). That way you don't need the global HWND or the local that you pass around in a cumbersome fashion.
Thanks Evil Steveand jpetrie; I did a quick test and reference solved it. Proof I need to read up on my c++.

I am curious about jpetrie suggestion as to encapsulate everything. Would that mean I should wrap the WndProc into the Ghost3D-class as well?

Since I'm thinking about putting the "graphic-engine" in the dll and the "game" in the app, shouldn't I put the creation of the window and the main game loop "outside" the render-code?

Thoughts on best practice?

This topic is closed to new replies.

Advertisement