I am trying to create a swapchain from a dll.
My process creates a window, initializes Direct3D11 and loads the dll.
It works perfectly fine when the process creates a non fullscreen window.
But as soon as the process creates a fullscreen window I get an access violation when calling D3D11CreateDeviceAndSwapChain from the dll.
The call occurs in a separate thread. (not from the dllmain thread.)
Now unfortunately I can't even debug properly since breakpoints freeze the fullscreen window and it is on top of my debugger window.
What causes the access violation?
EDIT: I have done some static analysis of dxgi.dll and found that when creating the swapchain it checks for a series of extended and basic window styles. Also I noticed that it should print a debug message before crashing so I hooked up a remote debugger to capture it. The message just says what I already knew:
DXGI ERROR: IDXGIFactory::CreateSwapChain: The specified output window has a style that is incompatible with DXGI. Output windows must have at least one of:
WS_POPUP
WS_BORDER
WS_CAPTION
WS_SYSMENU
WS_MAXIMIZEBOX
WS_MINIMIZEBOX
WS_THICKFRAME
WS_EX_CLIENTEDGE
WS_EX_CONTEXTHELP
WS_EX_DLGMODALFRAME
WS_EX_TOOLWINDOW
WS_EX_WINDOWEDGE
or not have WS_EX_TOPMOST. [ MISCELLANEOUS ERROR #78: ]
The error code is: DXGI_ERROR_INVALID_CALL
So why does intializing direct3d work from my executable but using the window it creates in my dll doesn't?
EDIT2: Apparently calling D3D11CreateDeviceAndSwapChain with DXGI_SWAP_CHAIN_DESC::Windowed set to FALSE in the executable changes the window styles causing the next call to D3D11CreateDeviceAndSwapChain to fail.
CODE SNIPPET:
IDXGISwapChain* swapChain;
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.BufferCount = 1;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
desc.OutputWindow = FindWindowW(L"Window Title", NULL);
desc.SampleDesc.Count = 1;
desc.Windowed = ((GetWindowLongPtr(desc.OutputWindow, GWL_STYLE) & WS_POPUP) != 0) ? false : true;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.BufferDesc.Width = 1;
desc.BufferDesc.Height = 1;
desc.BufferDesc.RefreshRate.Numerator = 0;
desc.BufferDesc.RefreshRate.Denominator = 1;
if (!desc.OutputWindow)
{
return nullptr;
}
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, };
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG, featureLevelArray, ARRAYSIZE(featureLevelArray), D3D11_SDK_VERSION, &desc, &swapChain, &device, &featureLevel, &deviceContext)))
{
return nullptr;
}
Calling D3D11CreateDeviceAndSwapChain with DXGI_SWAP_CHAIN_DESC::Windowed set to FALSE in the executable changes the window styles causing the next call to D3D11CreateDeviceAndSwapChain to fail.
Related
So i wanna draw an overlay over another window, but im getting no real runtime error the visual Studio debugging tools tell me that the result of
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWND, D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, NULL, &device);
is 0x8876086c. So here are the snippets of my code that are important and lead to this error(D3DERR_INVALIDCALL), which leads to the device being a nullpointer, which means i can't do anything with it.
I couldn't really figure out what led to this as i pretty much followed the documentation
int Paint::init(HWND hWND) {
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, &object))) {
exit(1);
}
ZeroMemory(¶ms, sizeof(params));
params.BackBufferWidth = width;
params.BackBufferHeight = height;
params.Windowed = true;
params.hDeviceWindow = hWND;
params.MultiSampleQuality = D3DMULTISAMPLE_NONE;
params.BackBufferFormat = D3DFMT_A8R8G8B8;
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWND, D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, NULL, &device);
and in the header file:
class Paint {
private:
IDirect3D9Ex* object = NULL;
IDirect3DDevice9Ex* device = NULL;
DWORD behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
D3DPRESENT_PARAMETERS params;
ID3DXFont* font = 0;
HWND TargetHWND;
int width, height;
int init(HWND(hWND));
}
D3DPRESENT_PARAMETERS params = {};
// Use Win32 BOOL "TRUE" instead of C++ "true"
params.Windowed = TRUE;
params.hDeviceWindow = m_window;
// params.BackBufferWidth, BackBufferHeight are ignored for Windowed = TRUE
// For Windowed = TRUE, use params.BackBufferFormat = D3DFMT_UNKNOWN, which is zero.
// For params.BackBufferCount zero is assumed to be 1, but best practice
// would be to set it
params.BackBufferCount = 1;
// You used D3DMULTISAMPLE_NONE for the MultiSampleQuality instead of MultiSampleType.
// It's all zero anyhow.
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
// --->>> This is the actual bug: there is no valid SwapEffect that has a value of zero <<<---
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
You are making the assumption that the Direct3D9 device supports D3DCREATE_HARDWARE_VERTEXPROCESSING, but you haven't validated it actually supports it. That said, D3DCREATE_SOFTWARE_VERTEXPROCESSING has known performance issues on Windows 10 so you should probably just require HW anyhow.
You should not be using legacy Direct3D9 or Direct3D9Ex for new projects. It's mostly emulated on newer versions of Windows, has lots of strange behaviors, and is almost 20 years old at this point. There's no support for the Direct3D 9 debug device on Windows 8.x or Windows 10. You should consider Direct3D 11 as a much better starting place for developers new to DirectX.
I want to modify a DirectX-Application's behavior (namely I want to implement a program similar to the Statman-Application by OrfeasZ [https://github.com/OrfeasZ/Statman/releases] as Onscreen-Info for Hitman 2) by injecting code (as DLL) into it and hooking the DirectX DeviceInterface VMT.
Since there are very limited resources on how to do this for DirectX11-Applications, I first wanted to learn how to do this in DX9 by creating a program that gets the DeviceInterface pointer of any DirectX9-Application. I wrote this code in the DllMain() function of my DLL (which is almost a 1:1 copy/paste of the third answer to this thread Hooking DirectX EndScene from an injected DLL):
HMODULE hDLL = GetModuleHandleA("d3d9");
LPDIRECT3D9(__stdcall*pDirect3DCreate9)(UINT) = (LPDIRECT3D9(__stdcall*)(UINT))GetProcAddress(hDLL, "Direct3DCreate9");
LPDIRECT3D9 pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
HRESULT hRes = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
//WNDPROC TempWndProc;
WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC, WndProc,0L,0L,GetModuleHandle(NULL),NULL,NULL,NULL,NULL,TEXT("1"),NULL };
RegisterClassEx(&wc);
HWND hWnd = CreateWindow(TEXT("1"), NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
IDirect3DDevice9 * ppReturnedDeviceInterface;
hRes = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &ppReturnedDeviceInterface);
pD3D->Release();
DestroyWindow(hWnd);
if (pD3D == NULL) {
//printf ("WARNING: D3D FAILED");
return false;
}
unsigned long* pInterface = (unsigned long*)*((unsigned long*)ppReturnedDeviceInterface);
When I inject the DLL into a DirectX9-Application (I've tried this with Civilization V and Total War: Shogun 2), it opens a window, so it actually is able to get the Direct3DCreate9 function from the d3d9.dll within the game, but pD3D->CreateDevice() always returns `D3DERR_INVALIDCALL. I don't really get what could be the reason for this, especially since the rest of this program works flawlessly. Does anybody have any idea what is missing/wrong?
D3DERR_INVALIDCALL
The method call is invalid. For example, a method's parameter may not
be a valid pointer.
Based on the error information this issue may caused by invalid parameter of IDirect3D9::CreateDevice method. You need initialize the pointer:
IDirect3DDevice9 *pReturnedDeviceInterface = NULL;
Also check if hWnd is a valid window handle and d3ddm.Format etc.
I've been learning DirectX by following some tutorials but every time I type something myself then DirectX won't work. Here is an example of the latest error that I can't fix after hours of research:
//Header.h
static HWND hWnd;
static IDXGISwapChain* swapChain;
static ID3D11Device* dev;
static ID3D11DeviceContext* devCon;
static ID3D11RenderTargetView* renderTarget;
//DirectX.cpp
bool InitD3D11(HINSTANCE hInst)
{
HRESULT hr;
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = 800;
bufferDesc.Height = 600;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.Windowed = true;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &dev, NULL, &devCon);
ID3D11Texture2D* backBuffer;
hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
hr = dev->CreateRenderTargetView(backBuffer, NULL, &renderTarget);
backBuffer->Release();
devCon->OMSetRenderTargets(1, &renderTarget, NULL);
return true;
}
//This is not the full code - I linked directx and lib files and stuff like that.
//If I copy and paste code from tutorials, everything runs fine
Whenever I call InitD3d11, I get an error saying that the swapChain is a NULL pointer. I assume that bufferDesc and/or swapChainDesc has some invalid data but the compiler can't give me any clue what is responsible for the error. Can someone please show me how to trace and fix errors like this? Thanks.
You are not checking the HRESULT values so you are missing all error-handling. For COM programming, even with Direct3D, you must check the HRESULT of every method that can return one for failure. If the return value is safe to ignore, it returns void instead.
Checking HRESULT values in old-school C/C++ programs is done with either the FAILED or SUCCEEDED macros.
hr = D3D11CreateDeviceAndSwapChain( /* ... */ *);
if (FAILED(hr))
return false;
In most cases, a failed HRESULT is treated as a 'fast-fail' or a fatal error. In other words, the program cannot proceed if the call fails.
In other cases there can be special case handling to recover from the error perhaps by using different options. For a detailed example of that, see Anatomy of Direct3D 11 Create Device.
In older Microsoft samples based on the legacy DXUT framework, the error handling was done with macros like V or V_RETURN which did some tracing or tracing & fatal exit.
In modern C++ samples, we actually use a helper DX::ThrowIfFailed that generates a C++ exception on a failed HRESULT for the fast-fail scenario. This makes the code more streamlined and readable:
DX::ThrowIfFailed(
D3D11CreateDeviceAndSwapChain( /* ... */ *)
);
The function itself is defined as:
#include <exception>
namespace DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch DirectX API errors
throw std::exception();
}
}
}
Your program should be compiled with /EHsc which is already in the default Visual Studio templates. See this topic page for more details.
From your code snippet above, you are following a pretty old-school tutorial. A lot has changed even for DirectX 11 development and most of those older tutorials are going to lead to confusion. As you are new to DirectX, I recommend you take a look at the DirectX Tool Kit and the tutorials there first. You can then come back to the older tutorials with a better understanding of 'modern' Direct3D and be able to extract more relevant information from the old stuff.
After checking all HRESULTS, the next thing you should do is enable the Direct3D debug layer which provides extra debugging information in the output window.
DWORD createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
hr = D3D11CreateDeviceAndSwapChain(
nullptr, D3D_DRIVER_TYPE_HARDWARE,
nullptr, createDeviceFlags, nullptr,
0, D3D11_SDK_VERSION,
&swapChainDesc, &swapChain, &dev, nullptr, &devCon);
You'll note I'm using the C++11 nullptr which is supported on Visual C++ 2010 or later instead of the old-school NULL. That's because it is typed. You were using NULL in two places in your version where the parameter wasn't actually a pointer, it's a number.
With this, you'll get a lot of feedback on basic errors that would help diagnose why in your particular case the swapchain is failing to create. I suspect it's because you are providing some values that are only meaningful for "exclusive fullscreen mode" and not for windowed mode (bufferDesc.RefreshRate). Instead, try:
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferDesc.Width = 800;
swapChainDesc.BufferDesc.Height = 600;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
This works on VS 2013 or VS 2015 which will initialize the structure to 0 with the ={}; syntax. On older compilers, you'll need to use ZeroMemory like you did in your old-school code above.
Note that using D3D11_CREATE_DEVICE_DEBUG will fail on a system that lacks the debugging device which is intended only for developers to use. Details on where you obtain the debug device layer differs based on which version of Windows you are using. See Direct3D SDK Debug Layer Tricks which has a little summary table at the bottom.
Also, debugging in the presence of uninitialized variables is a huge pain. Uninitialized pointers in particular can be a huge time waster. It would help if you did the following:
static HWND hWnd = nullptr;
static IDXGISwapChain* swapChain = nullptr;
static ID3D11Device* dev = nullptr;
static ID3D11DeviceContext* devCon = nullptr;
static ID3D11RenderTargetView* renderTarget = nullptr;
Even better, instead of using raw pointers for your COM objects, you should be using a C++ smart-pointer like Microsoft::WRL::ComPtr. See this topic page for details. Our modern samples use it, and it works for classic Win32 desktop apps as well as for Windows Store, UWP, and Xbox One apps since it's just a C++ template.
Whenever my Direct3D 11 application switches from fullscreen to windowed mode, IDXGISwapChain::ResizeBuffers triggers a breakpoint. In Visual Studio's console, the message "Critical error 0xc0000374" is printed, with no other information. Apparently this means a heap corruption has occurred. Okay then. I can't get any more information about why this occurred because the window obnoxiously obscures my view and prevents me from controlling the visual studio window.
I'm following the suggestions made on this MSDN page. By that I mean I copied and pasted the code.
This is my setup code (ignore the lack of error checking):
DXGI_SWAP_CHAIN_DESC swap_chain_desc = { 0 };
swap_chain_desc.BufferCount = 2;
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.OutputWindow = window::get();
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swap_chain_desc.Windowed = TRUE;
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
if (fullscreen == 1) {
swap_chain_desc.BufferDesc.Width = window::desktop_width();
swap_chain_desc.BufferDesc.Height = window::desktop_height();
} else {
swap_chain_desc.BufferDesc.Width = window::width();
swap_chain_desc.BufferDesc.Height = window::height();
}
HRESULT hr = D3D11CreateDeviceAndSwapChain (
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
NULL,
NULL,
NULL,
D3D11_SDK_VERSION,
&swap_chain_desc,
&m_swap_chain,
&m_device,
NULL,
&m_context
);
ID3D11Texture2D* back_buffer;
hr = m_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&back_buffer);
hr = m_device->CreateRenderTargetView(back_buffer, NULL, &m_view);
back_buffer->Release();
m_context->OMSetRenderTargets(1, &m_view, NULL);
D3D11_VIEWPORT viewport = { 0 };
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
if (fullscreen == 1) {
viewport.Width = window::desktop_width();
viewport.Height = window::desktop_height();
} else {
viewport.Width = window::width();
viewport.Height = window::height();
}
m_context->RSSetViewports(1, &viewport);
if (fullscreen == 1) {
m_swap_chain->SetFullscreenState(TRUE, NULL);
}
Is there anything there that might have caused this problem? The rendering code is just a ClearRenderTargetView and Present after a PeekMessage loop, nothing important.
This issue was fixed by showing the window before setting the swap chain's fullscreen state. Either call UpdateWindow(hwnd) and ShowWindow(hwnd, SW_SHOW) or set the window style to WS_VISIBLE when calling CreateWindow() or CreateWindowEx(), preferably before doing anything DirectX related.
I'm creating a hidden window and I'm looking to getting a pointer to IDXGISwapChain::Present(). The problem is that I can't get a valid Direct3D10 device, nor a valid swap chain.
HWND hwnd = CreateWindow(TEXT("flhiSTATIC"), TEXT("flh DXGI Window"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, 0, NULL, NULL, 0);
DXGI_SWAP_CHAIN_DESC scd;
ZeroMemory(&scd, sizeof(scd));
scd.BufferCount = 2;
RECT rcWnd;
GetClientRect(hwnd, &rcWnd);
scd.BufferDesc.Width = rcWnd.right - rcWnd.left;
scd.BufferDesc.Height = rcWnd.bottom - rcWnd.top;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // also tried DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
scd.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED;
scd.BufferDesc.RefreshRate.Numerator = 60;
scd.BufferDesc.RefreshRate.Denominator = 1;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hwnd;
scd.SampleDesc.Count = 1;
scd.SampleDesc.Quality = 0;
scd.Windowed = TRUE;
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
pD3D10CreateDeviceAndSwapChain = reinterpret_cast<D3D10CREATEDEVICEANDSWAPCHAIN_PROC *>(GetProcAddress(d3d10, "D3D10CreateDeviceAndSwapChain"));
HRESULT hr = pD3D10CreateDeviceAndSwapChain(NULL /*pAdapter*/, D3D10_DRIVER_TYPE_HARDWARE, NULL, D3D10_CREATE_DEVICE_DEBUG, D3D10_SDK_VERSION, &scd, &pSwapChain, &pDev);
// this guy always fails with 0 in both pSwapChain and pDev...
Any idea what might be wrong with the above code?
I completely forgot about the need to create or assign a pre-existing window class :(
That was the problem; lesson learned - always check the return codes of all the calls.
edit
I completely forgot to call CreateWindow with a valid window class, either one I'd previously registered or one that's registered by someone within the current module.