Related
Why is D3Dcompiler_43.dll Loaded and Unloaded every frame?
Why does my two window direct-X 11 application drop from 2000 Hz to 12 Hz when I resize one of the windows?
In simplified sample I have attached code from, it starts with two identical sized windows at 2000 Hz, but if I change the size of either one, the frame rate of both goes to 12 Hz.
If I minimize either one, I get back to full speed, 4000Hz with one window.
When it is running at 12 Hz, I get the following Output Trace EVERY FRAME:
'Galaxies.exe' (Win32): Loaded 'C:\Windows\System32\D3Dcompiler_43.dll'
'Galaxies.exe' (Win32): Unloaded 'C:\Windows\System32\D3Dcompiler_43.dll'
This trace comes out when I call IDGXISwapChain->Present().
This trace does not come out when running 2000Hz.
I do not expect to need D3Dcompiler_43.dll at all, since I build my shaders at compile time, not run time. But if it did need it, then it should load it and keep it, not load each frame.
And if I make the window sizes match again, or minimize one, then the trace stops.
C++, Directx 11
My basic template is based on the Microsoft Sample "SimpleTexturePC".
Main changes from that template:
compile textures into program rather than reading at run time
compile shaders into program rather than reading at run time
extend DeviceResources to be able to support more than one window
by making arrays for all the resources, such as RenderTargetView
Extending Main.cpp to create more than one window, and have a WinProc for each.
Each of these unique WinProcs then calls a WinProcCommon with an "index" that identifies the window.
Adding a second camera for use on 2nd window
Adding a trace to the main application "Galaxies.cpp" so I could see the FPS.
(the rest of the main application behavior is not in simplified sample).
Main.cpp
//
// Main.cpp
//
#include ...
namespace
{
std::unique_ptr<Galaxies> g_game_p;
};
LPCWSTR g_szAppName0 = L"World1";
LPCWSTR g_szAppName1 = L"World2";
LPCWSTR g_szAppName2 = L"World3";
LRESULT CALLBACK WndProcWorld1(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProcWorld2(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProcWorld3(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProcCommon(UINT, HWND, UINT, WPARAM, LPARAM);
// Entry point
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
if (!XMVerifyCPUSupport())
return 1;
HRESULT hr = CoInitializeEx(nullptr, COINITBASE_MULTITHREADED);
if (FAILED(hr))
return 1;
g_game_p = std::make_unique<Galaxies>();
int border = getInvisableBorderWidth();
// Register / create window 1
{
// Register class
WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProcWorld1;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIconW(hInstance, L"IDI_ICON1");
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
wcex.lpszClassName = L"WindowClass1";
wcex.hIconSm = LoadIconW(wcex.hInstance, L"IDI_ICON1");
if (!RegisterClassExW(&wcex)) // W implies unicode version.
return 1;
// Create window
int w, h;
g_game_p->GetDefaultWindowSize(w, h);
HWND hwnd = CreateWindowExW(0, wcex.lpszClassName, g_szAppName0, WS_OVERLAPPEDWINDOW,
50 - border, 50, w, h, nullptr, nullptr, hInstance, nullptr);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(g_game_p.get()));
if (!hwnd)
return 1;
g_game_p->Initialize(kGameWindow1, hwnd, w, h); // this chooses non-fullscreen location, though we will go full screen in a sec...
ShowWindow(hwnd, SW_SHOWNORMAL);
}
// Register / create window 2
{
// Register class
WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProcWorld2;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIconW(hInstance, L"IDI_ICON2");
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
wcex.lpszClassName = L"WindowClass2";
wcex.hIconSm = LoadIconW(wcex.hInstance, L"IDI_ICON2");
if (!RegisterClassExW(&wcex)) // W implies unicode version.
return 1;
// Create window
int w, h;
g_game_p->GetDefaultWindowSize(w, h);
HWND hwnd = CreateWindowExW(0, wcex.lpszClassName, g_szAppName1, WS_OVERLAPPEDWINDOW,
100 - border, 100, w + 00, h + 00, nullptr, nullptr, hInstance, nullptr);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(g_game_p.get()));
if (!hwnd)
return 1;
g_game_p->Initialize(kGameWindow2, hwnd, w, h);
ShowWindow(hwnd, SW_SHOWNORMAL);
}
// Main message loop
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
g_game_p->Tick();
}
}
g_game_p.reset();
CoUninitialize();
return static_cast<int>(msg.wParam);
}
// First parameter "index" used to differentiate the three windows.
LRESULT CALLBACK WndProcCommon(UINT index, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WindowStruct& winInfo = g_deviceResources_p->GetWindowStruct(index);
auto galaxies_p = reinterpret_cast<Galaxies*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
switch (message)
{
case WM_PAINT:
if (winInfo.in_sizemove && galaxies_p)
{
RECT rc;
GetWindowRect(hWnd, &rc);
galaxies_p->OnWindowSizeChanged(index, rc.right - rc.left, rc.bottom - rc.top);
galaxies_p->Tick();
}
else
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_MOVE:
if (galaxies_p)
{
galaxies_p->OnWindowMoved(index);
}
break;
case WM_SIZE:
if (wParam == SIZE_MINIMIZED)
{
if (!winInfo.minimized)
{
winInfo.minimized = true;
if (!winInfo.in_suspend && galaxies_p)
galaxies_p->OnSuspending();
winInfo.in_suspend = true;
}
}
else if (winInfo.minimized)
{
winInfo.minimized = false;
if (winInfo.in_suspend && galaxies_p)
galaxies_p->OnResuming();
winInfo.in_suspend = false;
}
else if (!winInfo.in_sizemove && galaxies_p)
{
RECT rc;
GetWindowRect(hWnd, &rc);
galaxies_p->OnWindowSizeChanged(index, rc.right - rc.left, rc.bottom - rc.top);
}
break;
case WM_ENTERSIZEMOVE:
winInfo.in_sizemove = true;
break;
case WM_EXITSIZEMOVE:
winInfo.in_sizemove = false;
if (galaxies_p)
{
RECT rc;
GetWindowRect(hWnd, &rc);
galaxies_p->OnWindowSizeChanged(index, rc.right - rc.left, rc.bottom - rc.top);
}
break;
case WM_GETMINMAXINFO:
if (lParam)
{
auto info = reinterpret_cast<MINMAXINFO*>(lParam);
info->ptMinTrackSize.x = 320;
info->ptMinTrackSize.y = 200;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case ... mouse cases
Mouse::ProcessMessage(message, wParam, lParam);
break;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
Keyboard::ProcessMessage(message, wParam, lParam);
break;
case WM_MENUCHAR:
return MAKELRESULT(0, MNC_CLOSE);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
// Windows procedure x3
LRESULT CALLBACK WndProcWorld1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return WndProcCommon(kGameWindow1, hWnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProcWorld2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return WndProcCommon(kGameWindow2, hWnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProcWorld3(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return WndProcCommon(kGameWindow3, hWnd, message, wParam, lParam);
}
// Exit helper
void ExitGame() noexcept
{
PostQuitMessage(0);
}
Galaxies.cpp
//
// Galaxies.cpp
//
#include ...
// Global variables
std::unique_ptr<DX::StepTimer> g_timer_p;
std::unique_ptr<DX::DeviceResources> g_deviceResources_p;
std::unique_ptr<DirectX::CommonStates> g_states_p;
std::unique_ptr<padInput> g_input_p;
std::unique_ptr<Camera> g_camera_p[2];
std::unique_ptr<HudText> g_text_p;
Galaxies::Galaxies() noexcept(false)
{
g_deviceResources_p = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
g_deviceResources_p->RegisterDeviceNotify(this);
}
void Galaxies::Initialize(UINT index, HWND window, int width, int height)
{
static int once = true;
if (once)
{
g_deviceResources_p->CreateDeviceResources();
once = false;
}
switch (index)
{
case kGameWindow1:
{
g_timer_p = std::make_unique<DX::StepTimer>();
g_input_p = std::make_unique<padInput>();
g_camera_p[0] = std::make_unique<Camera>();
g_camera_p[1] = std::make_unique<Camera>();
g_text_p = std::make_unique<HudText>();
// PER OBJECT INITIALIZE
g_camera_p[0]->Initialize(0);
g_camera_p[1]->Initialize(1);
g_input_p ->Initialize();
g_text_p ->Initialize();
g_deviceResources_p->SetWindow(index, window, width, height);
g_deviceResources_p->CreateWindowSizeDependentResources(index);
OnDeviceRestored();
break;
}
case kGameWindow2:
case kGameWindow3:
default:
{
g_deviceResources_p->SetWindow(index, window, width, height);
g_deviceResources_p->CreateWindowSizeDependentResources(index);
break;
}
}
}
void Galaxies::GetDefaultWindowSize(int& width, int& height) const noexcept
{
...
}
void Galaxies::OnDeviceLost()
{
g_states_p.reset();
m_spPerFrameConstantBuffer.Reset();
g_text_p->OnDeviceLost();
}
void Galaxies::OnDeviceRestored()
{
CreateDeviceDependentResources();
CreateWindowSizeDependentResources(0);
CreateWindowSizeDependentResources(1);
WindowSizeChangeConfirmed(0);
WindowSizeChangeConfirmed(1);
g_text_p->OnDeviceRestored();
}
void Galaxies::OnSuspending()
{
}
void Galaxies::OnResuming()
{
g_timer_p->ResetElapsedTime();
}
void Galaxies::OnWindowMoved(UINT index)
{
auto r = g_deviceResources_p->GetOutputSize(index);
g_deviceResources_p->WindowSizeChanged(index, r.width, r.height);
}
// Pass in CURRENT (Last known) window size (icluding frame), so we can compare that with current size
// as read in deviceResources.
void Galaxies::OnWindowSizeChanged(UINT index, int width, int height)
{
// returns false if size unchanged
if (g_deviceResources_p->WindowSizeChanged(index, width, height))
{
CreateWindowSizeDependentResources(0);
WindowSizeChangeConfirmed(index);
}
}
void Galaxies::Tick()
{
UpdateRenderGPUTextures();
g_timer_p->Tick([&]()
{
UpdateInput();
UpdateCameras();
UpdateWorld();
});
RenderWorld(0);
if (!g_deviceResources_p->isWindowMinimized(1))
{
RenderWorld(1);
}
}
void Galaxies::Clear(int index)
{
// Clear the views.
auto context = g_deviceResources_p->GetD3DDeviceContext();
auto renderTargetView = g_deviceResources_p->GetRenderTargetView(index);
auto depthStencilView = g_deviceResources_p->GetDepthStencilView(index);
context->ClearRenderTargetView(renderTargetView, Colors::Black);
context->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}
void Galaxies::CreateDeviceDependentResources()
{
auto device = g_deviceResources_p->GetD3DDevice();
g_states_p = std::make_unique<CommonStates>(device);
// Create constant buffers.
D3D11_BUFFER_DESC bufferDesc = {};
bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufferDesc.MiscFlags = 0;
bufferDesc.ByteWidth = sizeof(PerFrameConstantBuffer);
DX::ThrowIfFailed(device->CreateBuffer(&bufferDesc, nullptr, m_spPerFrameConstantBuffer.ReleaseAndGetAddressOf()));
}
// Allocate all memory resources that change on a window SizeChanged event.
void Galaxies::CreateWindowSizeDependentResources(UINT index)
{
// TODO: Initialize windows-size dependent objects here.
}
void Galaxies::UpdateRenderGPUTextures()
{
// Don't try to render anything before the first Update.
if (g_timer_p->GetFrameCount() == 0)
{
return;
}
}
void Galaxies::UpdateInput()
{
g_input_p->Update();
if (g_input_p->isKeyDownEdge(escape))
{
ExitGame();
}
}
void Galaxies::UpdateCameras()
{
g_camera_p[0]->Update();
// g_camera_p[1]->Update();
}
// Updates the world.
void Galaxies::UpdateWorld()
{
}
void Galaxies::RenderWorld(int index) // index 0, 1. not 2 (not edit window)
{
if (g_timer_p->GetFrameCount() == 0)
{
return;
}
Clear(index);
// Update Constants Buffer with per-frame variables.
// Though I now do this up to 3 times per frame - once per window.
auto context = g_deviceResources_p->GetD3DDeviceContext();
auto d3dBuffer = m_spPerFrameConstantBuffer.Get();
// Exclude 3rd window for now
if (index != 2)
{ // PRE-LOCK
MapGuard mappedResource(context, d3dBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0);
PerFrameConstantBuffer* data = reinterpret_cast<PerFrameConstantBuffer*>(mappedResource.get());
g_camera_p[index]->GetPerFrameData(data);
data->frameTime = g_timer_p->GetElapsedSeconds();
if (g_input_p->isButtonToggleSet(leftShoulder))
{
data->frameTime *= 5;
}
data->totalTime = g_timer_p->GetTotalSeconds();
int fps = (int)g_timer_p->GetFramesPerSecond();
char buffer[TEXT_NUM_OF_CHAR];
memset(buffer, 127, sizeof(buffer));
if (index == 0)
{
snprintf(&buffer[0 * TEXT_NUM_CHAR_PER_ROW], TEXT_NUM_CHAR_PER_ROW + 1, "Window 1 ");
}
else
{
snprintf(&buffer[0 * TEXT_NUM_CHAR_PER_ROW], TEXT_NUM_CHAR_PER_ROW + 1, "Window 2 ");
}
snprintf(&buffer[TEXT_NUM_CHAR_PER_ROW], TEXT_NUM_CHAR_PER_ROW + 1, "Galaxies V0.01 ");
snprintf(&buffer[2 * TEXT_NUM_CHAR_PER_ROW], TEXT_NUM_CHAR_PER_ROW + 1, " FPS = %d ", fps);
for (int i = 0; i < TEXT_NUM_OF_CHAR; i++)
{
data->alpha[4 * i] = buffer[i] - 0x20; // 0x20 is the first ascii char in our texture atlas. It is " "
}
} // UNLOCK (in mappedResource destructor)
context->VSSetConstantBuffers(CONSTANTS_PER_FRAME, 1, &d3dBuffer);
context->PSSetConstantBuffers(CONSTANTS_PER_FRAME, 1, &d3dBuffer);
auto renderTargetView = g_deviceResources_p->GetRenderTargetView(index);
auto depthStencilView = g_deviceResources_p->GetDepthStencilView(index);
context->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
// Set the viewport.
auto viewport = g_deviceResources_p->GetScreenViewport(index);
context->RSSetViewports(1, &viewport);
if (index != 2)
{
g_text_p->Render();
}
g_deviceResources_p->Present(index);
}
void Galaxies::WindowSizeChangeConfirmed(UINT index)
{
auto size = g_deviceResources_p->GetOutputSize(index);
switch (index)
{
case (kGameWindow1):
g_camera_p[0]->OnWindowSizeChanged(size.width, size.height);
g_text_p ->OnWindowSizeChanged(size.width, size.height);
break;
case (kGameWindow2):
g_camera_p[1]->OnWindowSizeChanged(size.width, size.height);
break;
case (kGameWindow3):
break;
default:
break;
}
}
DeviceResources.cpp
//
// DeviceResources.cpp
//
#include ...
using namespace DirectX;
using namespace DX;
using Microsoft::WRL::ComPtr;
DeviceResources::DeviceResources(
DXGI_FORMAT backBufferFormat,
DXGI_FORMAT depthBufferFormat,
UINT backBufferCount,
D3D_FEATURE_LEVEL minFeatureLevel,
unsigned int flags) noexcept :
m_backBufferFormat{ backBufferFormat },
m_depthBufferFormat{ depthBufferFormat },
m_backBufferCount{backBufferCount},
m_d3dMinFeatureLevel(minFeatureLevel),
m_window(),
m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1),
m_colorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709),
m_options(flags | c_FlipPresent | c_AllowTearing),
m_deviceNotify(nullptr)
{
m_windowCount = 0;
for (int index = 0; index < c_MaxWindows; index++)
{
m_window[index] = nullptr;
m_WindowStruct[index] = WindowStruct();
m_screenViewport[index] = { 100, 100, 800, 600, 0, 1 };
}
}
void DeviceResources::CreateDeviceResources()
{
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
CreateFactory();
...
// Determine feature levels
...
ComPtr<IDXGIAdapter1> adapter;
GetHardwareAdapter(adapter.GetAddressOf());
// Create the Direct3D 11 API device / context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
HRESULT hr = E_FAIL;
if (adapter)
{
hr = D3D11CreateDevice(
adapter.Get(),
D3D_DRIVER_TYPE_UNKNOWN,
nullptr,
creationFlags,
s_featureLevels,
featLevelCount,
D3D11_SDK_VERSION,
device.GetAddressOf(),
&m_d3dFeatureLevel,
context.GetAddressOf()
);
}
if (FAILED(hr))
{
// fall back to WARP device.
...
}
ThrowIfFailed(hr);
ThrowIfFailed(device.As(&m_d3dDevice));
ThrowIfFailed(context.As(&m_d3dContext));
}
// recreate every time the window size is changed.
void DeviceResources::CreateWindowSizeDependentResources(UINT index)
{
if (!m_window[index])
{
throw std::exception("need valid window handle");
}
// Clear the previous window size specific context.
ID3D11RenderTargetView* nullViews[] = { nullptr };
m_d3dContext->OMSetRenderTargets(_countof(nullViews), nullViews, nullptr);
m_d3dRenderTargetView[index].Reset();
m_d3dDepthStencilView[index].Reset();
m_renderTargetTexture[index].Reset();
m_depthStencilTexture[index].Reset();
m_d3dContext->Flush();
// Determine the render target size in pixels.
const UINT backBufferWidth = std::max<UINT>(static_cast<UINT>(m_WindowStruct[index].currentPos.width), 1u);
const UINT backBufferHeight = std::max<UINT>(static_cast<UINT>(m_WindowStruct[index].currentPos.height), 1u);
const DXGI_FORMAT backBufferFormat = (m_options & (c_FlipPresent | c_AllowTearing | c_EnableHDR)) ? NoSRGB(m_backBufferFormat) : m_backBufferFormat;
if (m_swapChain[index])
{
// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain[index]->ResizeBuffers(
m_backBufferCount,
backBufferWidth,
backBufferHeight,
backBufferFormat,
(m_options & c_AllowTearing) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
...
}
else
{
ThrowIfFailed(hr);
}
}
else
{
// Create a descriptor for the swap chain.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = backBufferWidth;
swapChainDesc.Height = backBufferHeight;
swapChainDesc.Format = backBufferFormat;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = m_backBufferCount;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = (m_options & (c_FlipPresent | c_AllowTearing | c_EnableHDR)) ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swapChainDesc.Flags = (m_options & c_AllowTearing) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsSwapChainDesc = {};
fsSwapChainDesc.Windowed = TRUE;
// Create a SwapChain
ThrowIfFailed(m_dxgiFactory->CreateSwapChainForHwnd(
m_d3dDevice.Get(),
m_window[index],
&swapChainDesc,
&fsSwapChainDesc,
nullptr, m_swapChain[index].ReleaseAndGetAddressOf()
));
ThrowIfFailed(m_dxgiFactory->MakeWindowAssociation(m_window[index], DXGI_MWA_NO_ALT_ENTER));
}
// Create a render target view of the swap chain back buffer.
ThrowIfFailed(m_swapChain[index]->GetBuffer(0, IID_PPV_ARGS(m_renderTargetTexture[index].ReleaseAndGetAddressOf())));
CD3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc(D3D11_RTV_DIMENSION_TEXTURE2D, m_backBufferFormat);
ThrowIfFailed(m_d3dDevice->CreateRenderTargetView(
m_renderTargetTexture[index].Get(),
&renderTargetViewDesc,
m_d3dRenderTargetView[index].ReleaseAndGetAddressOf()
));
if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN)
{
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC depthStencilDesc(
m_depthBufferFormat,
backBufferWidth,
backBufferHeight,
1,
1,
D3D11_BIND_DEPTH_STENCIL
);
ThrowIfFailed(m_d3dDevice->CreateTexture2D(
&depthStencilDesc,
nullptr,
m_depthStencilTexture[index].ReleaseAndGetAddressOf()
));
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
ThrowIfFailed(m_d3dDevice->CreateDepthStencilView(
m_depthStencilTexture[index].Get(),
&depthStencilViewDesc,
m_d3dDepthStencilView[index].ReleaseAndGetAddressOf()
));
}
// Set the 3D rendering viewport
m_screenViewport[index] = CD3D11_VIEWPORT(
0.0f,
0.0f,
static_cast<float>(backBufferWidth),
static_cast<float>(backBufferHeight)
);
}
void DeviceResources::SetWindow(UINT index, HWND window, int width, int height) noexcept
{
m_window[index] = window;
int indent = 50 * (index + 1);
RECTxywh windowPos = { indent - getInvisableBorderWidth(), indent, width, height };
m_WindowStruct[index].setWindowedByWindowPos(windowPos, 0, true);
}
bool DeviceResources::WindowSizeChanged(UINT index, int width, int height)
{
RECTxywh newRc = { 0, 0, width, height };
RECTxywh oldRc = { 0, 0, m_WindowStruct[index].currentPos.width, m_WindowStruct[index].currentPos.height };
if (newRc == oldRc)
{
// i.e. size didnt actually change
return false;
}
m_WindowStruct[index].setWindowedByWindowPos(newRc, 0, false);
CreateWindowSizeDependentResources(index);
return true;
}
// Recreate all device resources
void DeviceResources::HandleDeviceLost(UINT index)
{
if (m_deviceNotify)
{
m_deviceNotify->OnDeviceLost();
}
m_d3dDepthStencilView[index].Reset();
m_d3dRenderTargetView[index].Reset();
m_renderTargetTexture[index].Reset();
m_depthStencilTexture[index].Reset();
m_swapChain[index].Reset();
m_d3dContext.Reset();
m_d3dDevice.Reset();
m_dxgiFactory.Reset();
CreateDeviceResources();
CreateWindowSizeDependentResources(index);
if (m_deviceNotify)
{
m_deviceNotify->OnDeviceRestored();
}
}
int DeviceResources::getNumberOfMonitors()
{
ComPtr<IDXGIAdapter1> adapter;
GetHardwareAdapter(adapter.GetAddressOf());
IDXGIOutput* output = NULL;
int count = 0;
for (int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i)
{
count++;
}
return count;
}
RECTxywh DeviceResources::getMonitorSize(int monitor)
{
RECT position = getMonitorPos(monitor);
RECTxywh size(0, 0, position.right - position.left, position.bottom - position.top);
return size;
}
// 1 based index into monitors
RECT DeviceResources::getMonitorPos(int monitor)
{
ComPtr<IDXGIAdapter1> adapter;
GetHardwareAdapter(adapter.GetAddressOf());
RECT pos = {};
IDXGIOutput* output = NULL;
int count = -1;
if ( DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(monitor, &output) )
{
DXGI_OUTPUT_DESC outputDesc;
HRESULT hr = output->GetDesc(&outputDesc);
pos = outputDesc.DesktopCoordinates;
}
return pos;
}
RECT DeviceResources::getWindowPos(int index)
{
RECT windowRect;
HWND window = m_window[index];
GetWindowRect(window, &windowRect);
return windowRect;
}
void DeviceResources::setWindowPosition(int index, RECTxywh pos)
{
HWND hWnd = m_window[index];
SetWindowPos(hWnd, HWND_TOP, pos.left, pos.top, pos.width, pos.height, SWP_FRAMECHANGED)
ShowWindow(hWnd, SW_SHOWNORMAL)
}
bool DeviceResources::isWindowMinimized(int index)
{
return IsIconic(m_window[index]);
}
// Present the contents of the swap chain to the screen.
void DeviceResources::Present(int index)
{
HRESULT hr = E_FAIL;
if (m_options & c_AllowTearing)
{
hr = m_swapChain[index]->Present(0, DXGI_PRESENT_ALLOW_TEARING);
Print(L"TRACE: Present Present %i \n", index);
}
else
{
hr = m_swapChain[index]->Present(1, 0);
}
m_d3dContext->DiscardView(m_d3dRenderTargetView[index].Get());
if (m_d3dDepthStencilView[index])
{
// Discard the contents of the depth stencil.
m_d3dContext->DiscardView(m_d3dDepthStencilView[index].Get());
}
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
HandleDeviceLost(index);
}
else
{
ThrowIfFailed(hr);
if (!m_dxgiFactory->IsCurrent())
{
CreateFactory();
}
}
}
void DeviceResources::CreateFactory()
{
ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf())));
}
void DeviceResources::GetHardwareAdapter(IDXGIAdapter1** ppAdapter)
{
...
}
I want to create a function which displays the shell context menu for a given file list (as string),
but the files are from different location on the shell namespace.
(It is the same the context menu which is shown when you right click selected files in the search results area in Windows Explorer)
I saw that the issue was discussed in couple of questions before, but none of them gave a solution:
1. Showing a Windows context menu for multiple items
2. Display file properties dialog for files in different directories
3. How to display system context menu for multiple files in different folders?
However, I noticed that the comments there are referring to this blog (2007), it is written by the author of a file manager for windows. (his file manager software defenetly shows that he did it)
He mentions overriding IShellFolder::GetUIObjectOf method and implementing a new IDataObject interface.
13 years have been passed since then, so I'm thinking using new functions intorduced in Windows Vista:
1. I'm trying to use SHCreateDefaultContextMenu instead CDefFolderMenu_Create2.
2. Maybe the function SHCreateDataObject can be used instead of implementing a new DataObject?
I'm not a COM expert, and I struggle to do those tasks,
Hope someone will fill the missing blocks.
I have put here my effort.
It is implemented as a dll to make it easier for other to use and share.
#include <Windows.h>
#include <shlobj.h>
#define MIN_SHELL_ID 1
#define MAX_SHELL_ID 30000
extern "C"
{
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd);
// Try to convert pICv1 into IContextMenu2 or IcontextMenu3
// In case of success, release pICv1.
HRESULT UpgradeContextMenu(
LPCONTEXTMENU pICv1, // In: The context menu version 1 to be converted.
void** ppCMout, // Out: The new context menu (or old one in case
// the convertion could not be done)
int* pcmType) // Out: The version number.
{
HRESULT hr;
// Try to get version 3 first.
hr = pICv1->QueryInterface(IID_IContextMenu3, ppCMout);
if (NOERROR == hr)
{
*pcmType = 3;
}
else
{
hr = pICv1->QueryInterface(IID_IContextMenu2, ppCMout);
if (NOERROR == hr)
*pcmType = 2;
}
if (*ppCMout)
{
pICv1->Release(); // free version 1
}
else { // only version 1 is supported
*pcmType = 1;
*ppCMout = pICv1;
hr = NOERROR;
}
return hr;
}
// Global ref to the context menu to be used while
// the window messages are being handled.
LPCONTEXTMENU2 g_pIContext_v2 = NULL;
// Handle window messages.
// The messages: WM_DRAWITEM, WM_MEASUREITEM, WM_INITMENUPOPUP, WM_MENUSELECT
// should be redirected to this function.
__declspec(dllexport) void OnWindowMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (g_pIContext_v2 == NULL) // Version 1 is used.
return;
switch (msg)
{
case WM_DRAWITEM:
case WM_MEASUREITEM:
if (wp)
break;
case WM_INITMENUPOPUP:
g_pIContext_v2->HandleMenuMsg(msg, wp, lp);
break;
case WM_MENUSELECT:
// Get the help text and update the status bar , Not implemented yet.
break;
}
}
// Shows the shell context menu for the given file list at the given position.
// * error checking is not implemented here.
__declspec(dllexport) HRESULT ShowContextMenu(
HWND hWnd,
int xPos,
int yPos,
wchar_t **fullFileNames, // path + file name
size_t fullFileNamesSize
)
{
HRESULT hr;
// absolute pidl of the files.
LPITEMIDLIST *pidlChilds;
pidlChilds = new LPITEMIDLIST[fullFileNamesSize];
// Get the desktop folder.
LPSHELLFOLDER pDesktop;
SHGetDesktopFolder(&pDesktop);
size_t pidlChildsSize = 0;
for (size_t i = 0; i < fullFileNamesSize; i++)
{
PIDLIST_ABSOLUTE pidl_absolute;
UINT attributes;
hr = SHParseDisplayName(fullFileNames[i], NULL, &pidl_absolute, SFGAO_SYSTEM, (SFGAOF*)&attributes);
pidlChilds[pidlChildsSize] = pidl_absolute;
pidlChildsSize++;
}
// Probably I need to use the new class in place of the pDesktop arg.
DEFCONTEXTMENU dcm =
{
hWnd,
NULL, // contextMenuCB
NULL, // pidlFolder,
pDesktop,
pidlChildsSize,
(LPCITEMIDLIST*)pidlChilds,
NULL, // *punkAssociationInfo
NULL, // cKeys
NULL // *aKeys
};
IContextMenu *pCMv1 = NULL; // Version 1
IContextMenu *pCMx = NULL; // Versions 2 or 3
hr = SHCreateDefaultContextMenu(&dcm, IID_IContextMenu, (void**)&pCMv1);
int pcmType = 0;
hr = UpgradeContextMenu(pCMv1, (void**)&pCMx, &pcmType);
if (pcmType > 1) // pCMv1 was feed inside UpgradeContextMenu function.
{
g_pIContext_v2 = (LPCONTEXTMENU2)pCMx;
}
HMENU hMenu = CreatePopupMenu();
hr = pCMx->QueryContextMenu(hMenu, 0, MIN_SHELL_ID, MAX_SHELL_ID, CMF_EXPLORE | CMF_CANRENAME);
UINT cmdID = TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RETURNCMD,
xPos, yPos, 0, hWnd, 0);
if (cmdID >= MIN_SHELL_ID && cmdID <= MAX_SHELL_ID)
ProcessCMCommand(pCMx,
cmdID - MIN_SHELL_ID, // offset passed
hWnd
);
// Free resources
for (size_t i = 0; i < pidlChildsSize; i++)
CoTaskMemFree(pidlChilds[i]);
pDesktop->Release();
pCMx->Release();
delete[] pidlChilds;
g_pIContext_v2 = NULL;
return hr;
}
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd)
{
CMINVOKECOMMANDINFOEX ici;
ZeroMemory(&ici, sizeof(ici));
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
ici.hwnd = hWnd;
ici.fMask = CMIC_MASK_UNICODE;
ici.lpVerb = MAKEINTRESOURCEA(idCmdOffset);
ici.lpVerbW = MAKEINTRESOURCEW(idCmdOffset);
ici.nShow = SW_SHOWNORMAL;
return pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
}
/*
class CIShellFolderSpy : public IShellFolder
{
public:
// constructor
CIShellFolderSpy(LPSHELLFOLDER sf) {
sf->AddRef();
m_iSF = sf;
}
~CIShellFolderSpy() { m_iSF->Release(); }
// IUnknown methods --------
STDMETHOD_(ULONG, AddRef)() {
return m_iSF->AddRef();
}
STDMETHOD_(ULONG, Release)() {
return m_iSF->Release();
}
STDMETHOD_(HRESULT, QueryInterface(REFIID riid, void **ppvObject)) {
return m_iSF->QueryInterface(riid, ppvObject);
}
// IShellFolder methods ----
STDMETHOD_(HRESULT, BindToObject)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToObject(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, BindToStorage)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToStorage(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, CompareIDs)(
LPARAM lParam,
PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2) {
return m_iSF->CompareIDs(lParam, pidl1, pidl2);
}
STDMETHOD_(HRESULT, CreateViewObject)(
HWND hwndOwner,
REFIID riid,
void **ppv) {
return m_iSF->CreateViewObject(hwndOwner, riid, ppv);
}
STDMETHOD_(HRESULT, EnumObjects)(
HWND hwnd,
SHCONTF grfFlags,
IEnumIDList **ppenumIDList) {
return m_iSF->EnumObjects(hwnd, grfFlags, ppenumIDList);
}
STDMETHOD_(HRESULT, GetAttributesOf)(
UINT cidl,
PCUITEMID_CHILD_ARRAY apidl,
SFGAOF *rgfInOut) {
return m_iSF->GetAttributesOf(cidl, apidl, rgfInOut);
}
STDMETHOD_(HRESULT, GetDisplayNameOf)(
PCUITEMID_CHILD pidl,
SHGDNF uFlags,
STRRET *pName) {
return m_iSF->GetDisplayNameOf(pidl, uFlags, pName);
}
STDMETHOD_(HRESULT, ParseDisplayName)(
HWND hwnd,
IBindCtx *pbc,
LPWSTR pszDisplayName,
ULONG *pchEaten,
PIDLIST_RELATIVE *ppidl,
ULONG *pdwAttributes
) {
return m_iSF->ParseDisplayName(hwnd,
pbc,
pszDisplayName,
pchEaten,
ppidl,
pdwAttributes);
}
STDMETHOD_(HRESULT, SetNameOf)(
HWND hwnd,
PCUITEMID_CHILD pidl,
LPCWSTR pszName,
SHGDNF uFlags,
PITEMID_CHILD *ppidlOut
) {
return m_iSF->SetNameOf(
hwnd,
pidl,
pszName,
uFlags,
ppidlOut);
}
virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST *apidl,
REFIID riid,
UINT *rgfReserved,
void **ppv)
{
if (InlineIsEqualGUID(riid, IID_IDataObject))
{
// i ignore the pidl array supplied and return the good data object
return m_pMDObj->QueryInterface(riid, ppv);
}
else {
// i've seen IDropTarget requests, let base handle them
return m_iSF->GetUIObjectOf(hwndOwner, cidl, apidl, riid, rgfReserved, ppv);
}
}
protected:
LPSHELLFOLDER m_iSF;
CMultiDataObject* m_pMDObj;
}; // end of class
*/
}
Got a LINK2019 error that I can't figure out. Error code: Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol __vsnprintf referenced in function "long __stdcall StringVPrintfWorkerA(char *,unsigned int,unsigned int *,char const *,char *)" (?StringVPrintfWorkerA##YGJPADIPAIPBD0#Z) Direct X C:\Visual Studio Programs\Direct X\Direct X\dxerr.lib(dxerra.obj) 1
main.cpp:
#include <Windows.h>
#include <memory>
#include "BlankDemo.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
LPWSTR cmdLine, int cmdShow)
{
UNREFERENCED_PARAMETER(prevInstance);
UNREFERENCED_PARAMETER(cmdLine);
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASS);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = "DX11BookWindowClass";
if (!RegisterClassEx(&wndClass))
return -1;
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_EX_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowA("DX11BookWindowClass", "BlankWin32Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left,
rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
if (!hwnd)
return -1;
ShowWindow(hwnd, cmdShow);
std::auto_ptr<Dx11DemoBase> demo(new BlankDemo());
// Demo Initialize
bool result = demo->Initialize(hInstance, hwnd);
// Error reporting if there is an issue
if (result == false)
return -1;
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Update and Draw
demo->Update(0.0f);
demo->Render();
}
}
// Demo Shutdown
demo->Shutdown();
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
defualt:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
// implementation of the BlankDemo class
BlankDemo::BlankDemo()
{
}
BlankDemo::~BlankDemo()
{
}
bool BlankDemo::LoadContent()
{
return true;
}
void BlankDemo::UnloadContent()
{
}
void BlankDemo::Update(float dt)
{
}
void BlankDemo::Render()
{
if (d3dContext_ == 0)
return;
float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };
d3dContext_->ClearRenderTargetView(backBufferTarget_, clearColor);
swapChain_->Present(0, 0);
}
BlankDemo.h file:
#pragma once
#ifndef _BLANK_DEMO_H_
#define _BLANK_DEMO_H_
#include "Dx11DemoBase.h"
class BlankDemo : public Dx11DemoBase
{
public:
BlankDemo();
virtual ~BlankDemo();
bool LoadContent();
void UnloadContent();
void Update(float dt);
void Render();
};
#endif // !_BLANK_DEMO_H_
Dx11DemoBase::Dx11DemoBase() : driverType_(D3D_DRIVER_TYPE_NULL),
featureLevel_(D3D_FEATURE_LEVEL_11_0), d3dDevice_(0), d3dContext_(0),
swapChain_(0), backBufferTarget_(0)
{
}
Dx11DemoBase::~Dx11DemoBase()
{
Shutdown();
}
bool Dx11DemoBase::LoadContent()
{
// Override with demo specifics, if any...
return true;
}
void Dx11DemoBase::UnloadContent()
{
// Override with demo specifics, if any...
}
void Dx11DemoBase::Shutdown()
{
UnloadContent();
if (backBufferTarget_) backBufferTarget_->Release();
if (swapChain_) swapChain_->Release();
if (d3dContext_) d3dContext_->Release();
if (d3dDevice_) d3dDevice_->Release();
d3dDevice_ = 0;
d3dContext_ = 0;
swapChain_ = 0;
backBufferTarget_ = 0;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
bool Dx11DemoBase::Initialize(HINSTANCE hInstance, HWND hwnd)
{
hInstance_ = hInstance;
hwnd_ = hwnd;
RECT dimensions;
GetClientRect(hwnd, &dimensions);
unsigned int width = dimensions.right - dimensions.left;
unsigned int height = dimensions.bottom - dimensions.top;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_SOFTWARE
};
unsigned int totalDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
unsigned int totalFeatureLevels = ARRAYSIZE(featureLevels);
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = true;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
unsigned int creationFlags = 0;
#ifdef _DEBUG
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT result;
unsigned int driver = 0;
for (driver = 0; driver < totalDriverTypes; ++driver)
{
result = D3D11CreateDeviceAndSwapChain(0, driverTypes[driver], 0,
creationFlags, featureLevels, totalFeatureLevels, D3D11_SDK_VERSION,
&swapChainDesc, &swapChain_, &d3dDevice_, &featureLevel_, &d3dContext_);
if (SUCCEEDED(result))
{
driverType_ = driverTypes[driver];
break;
}
}
if (FAILED(result))
{
DXTRACE_MSG("Failed to create the Direct3d device!");
return false;
}
ID3D11Texture2D* backBufferTexture;
result = swapChain_->GetBuffer(0, _uuidof(ID3D11Texture2D), (LPVOID*)&backBufferTexture);
if (FAILED(result))
{
DXTRACE_MSG("Failed to get the swap chain back buffer!");
return false;
}
result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0, &backBufferTarget_);
if (backBufferTexture)
backBufferTexture->Release();
if (FAILED(result))
{
DXTRACE_MSG("Failed to create the render target view!");
return false;
}
d3dContext_->OMSetRenderTargets(1, &backBufferTarget_, 0);
D3D11_VIEWPORT viewport;
viewport.Width = static_cast<float>(width);
viewport.Height = static_cast<float>(height);
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
d3dContext_->RSSetViewports(1, &viewport);
return LoadContent();
}
And lastly, the Dx11DemoBase.h header:
#pragma once
#ifndef _DEMO_BASE_H_
#define _DEMO_BASE_H_
#include <d3d11.h>
#include <D3DX11.h>
#include <dxerr.h>
class Dx11DemoBase
{
public:
Dx11DemoBase();
virtual ~Dx11DemoBase();
bool Initialize(HINSTANCE hInstance, HWND hwnd);
void Shutdown();
virtual bool LoadContent();
virtual void UnloadContent();
virtual void Update(float dt) = 0;
virtual void Render() = 0;
protected:
HINSTANCE hInstance_;
HWND hwnd_;
D3D_DRIVER_TYPE driverType_;
D3D_FEATURE_LEVEL featureLevel_;
ID3D11Device* d3dDevice_;
ID3D11DeviceContext* d3dContext_;
IDXGISwapChain* swapChain_;
ID3D11RenderTargetView* backBufferTarget_;
};
#endif // !_DEMO_BASE_H_
Add legacy_stdio_definitions.lib to Additional dependencies list in the Project properties > Configuration Properties > Linker > Input as recommended in this answer: https://stackoverflow.com/a/34230122/6693304
The legacy DirectX SDK is deprecated, so it hasn't been officially updated since the release of Visual Studio 2010 RTM (June 2010). See Microsoft Docs
This has a few specific implications:
The Windows 8.0 SDK, Windows 8.1 SDK, and Windows 10 SDK all have newer headers than the legacy DirectX SDK where they overlap. You can still make use of it with VS 2012, 2013, 2015, or 2017 but you need to reverse the traditional include/lib path order in the VC++ Directories settings. There are a few other quirks covered at the bottom of this Microsoft Docs topic page. See also The Zombie DirectX SDK.
The DLL import libraries in the legacy DirectX SDK are missing some of the imports that are present in the Windows 8.x or Windows 10 SDK. They generally work fine with all C/C++ compilers because they are fairly standard Win32 without any version-specific CRT references.
Static libraries, however, are not guaranteed to be binary compatible from version to version of the C/C++ compiler. dxguid.lib just has some data in it, so it generally works, but dxerr.lib has actual code. Hence with the major changes in the C/C++ Runtime in VS 2015, it no longer works without link errors.
There are two basic solutions to the dxerr.lib problem:
Build your own copy of the code. It's available here. This is the most robust as it will always match your compiler toolset.
You can add legacy_stdio_definitions.lib, but keep in mind that you are already relying on very out-dated files so you should work to remove/minimize use of the legacy DirectX SDK over time.
Many online tutorials for DirectX 11 and books are outdated w.r.t. to the DirectX SDK and still use d3dx11 which is also deprecated. There are numerous open source replacements available for this functionality. See Living without D3DX.
All that said, this question has already been answered on StackOverflow and would have shown up if you just searched dxerr.
I've created a dialogbox with progress bar and a cancel button using CreateDialogParam to show status while copying several files (using CopyFileEx).
How do I cancel the process using CopyFileEx correctly, starting from pressing the cancel button in dialogbox? Is there anyway I can do it without using global variable? And How do I correctly handle the returned PROGRESS_CANCEL? I have provided questions in the code below to make clearer what help do I need.
//copy function
BOOL copy(HWND &hWnd, std::vector <FILECONSOLIDATEPARAMS> &vec)
{
//pass vector as lparam to dialogbox proc
LPARAM lp = reinterpret_cast<LPARAM>(&vec);
HWND hCopy = CreateDialogParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1),
hwndmain, (DLGPROC)dlgboxcopyproc, lp);
static HWND hIDC_STATIC, hIDC_STATIC4;
hIDC_STATIC = GetDlgItem(hCopy, IDC_STATIC);
hIDC_STATIC4 = GetDlgItem(hCopy, IDC_STATIC4);
LPBOOL pbCancel = FALSE;
size_t s;
for (s = 0; s != vec.size(); s++)
{
SendMessage(hIDC_STATIC, WM_SETTEXT, 0, (LPARAM)vec[s].filename);
SendMessage(hIDC_STATIC4, WM_SETTEXT, 0,(LPARAM)vec[s].destination);
BOOL b = CopyFileEx(vec[s].filename, vec[s].destination,
&CopyProgressRoutine,(LPVOID)hCopy,pbCancel, NULL);
//how to catch and process PROGRESS_CANCEL?
if (!b)
{
DWORD dw = GetLastError();
ShowErrMsg(dw);
}
}
PostMessage(hCopy, WM_DESTROY, 0, 0);
return TRUE;
}
//dialogbox procedure
INT_PTR CALLBACK dlgboxcopyproc(HWND hWndDlg,UINT Msg,WPARAM wParam,LPARAM
lParam)
{
//translate passed lparam back to vector
std::vector<FILECONSOLIDATEPARAMS>& vect =
*reinterpret_cast<std::vector<FILECONSOLIDATEPARAMS>*>(lParam);
INITCOMMONCONTROLSEX _icex;
_icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
_icex.dwICC = ICC_PROGRESS_CLASS;
InitCommonControlsEx(&_icex);
static HWND hParent;
static HWND hIDCancel;
static HWND hIDC_PROGRESS1;
static HWND hIDC_STATIC;
hParent = GetParent(hWndDlg);
hIDCancel = GetDlgItem(hWndDlg, IDCANCEL);
hIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
switch (Msg)
{
case WM_INITDIALOG:
{
SendMessage(hIDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
}
return (INT_PTR)TRUE;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hWndDlg, FALSE); //how to make pbCancel = TRUE?
return (INT_PTR)TRUE;
}
}
break;
case WM_DESTROY:
{
DestroyWindow(hWndDlg);
}
}
return FALSE;
}
//copyprogressroutine callback function
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize,
LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize,
LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD
dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID
lpData)
{
HWND hWndDlg = (HWND)lpData;
static HWND hwndIDC_PROGRESS1;
hwndIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
DOUBLE Percentage = ((DOUBLE)TotalBytesTransferred.QuadPart /
(DOUBLE)TotalFileSize.QuadPart) * 100;
switch (dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
SendMessage(hwndIDC_PROGRESS1, PBM_SETPOS, (WPARAM) Percentage, 0);
break;
case CALLBACK_STREAM_SWITCH:
Percentage = 0;
break;
}
return PROGRESS_CONTINUE; //how to make conditional return PROGRESS_CANCEL?
}
code like
LPBOOL pbCancel = FALSE;
CopyFileEx(, pbCancel, )
senseless. really we simply pass 0 in place pbCancel and have no ability cancel operation.
we need alocate some variable (let name it bCancel) of type BOOL and pass pointer of this variable to CopyFileEx
something like this:
BOOL bCancel = FALSE;
CopyFileEx(, &bCancel, )
the CopyFileEx will be periodically query value of this variable by passed pointer and if it became true - break operation and return ERROR_REQUEST_ABORTED error.
the next - CopyFileEx not return until operation is complete - this is synchronous api - as result it can not be called from GUI thread (or it simply block GUI). need call it from separate thread.
so basic solution - allocate some data structure, here place BOOL bCancel, and other data, which we will be use during copy. we will access this data structure from 2 threads - gui thread (from dialog procedure) and from separate thread, which will call CopyFileEx. for correct implement this - need use reference counting on structure. the lpProgressRoutine need post messages to gui thread for notify it about progress (gui thread can move progress bars on this notification). dialog can containing for example Cancel button. when user click it - GUI thread simply set bCancel = TRUE, as result CopyFileEx abort operation on next chunk. for start copy - need start new thread, which call CopyFileEx. based on required logic - can do this from WM_INITDIALOG or say when user press some button in dialog
basic code:
struct CopyDlg
{
enum {
WM_COPYRESULT = WM_APP, WM_PROGRESS
};
struct ProgressData {
LARGE_INTEGER TotalFileSize;
LARGE_INTEGER TotalBytesTransferred;
LARGE_INTEGER StreamSize;
LARGE_INTEGER StreamBytesTransferred;
DWORD dwStreamNumber;
};
PCWSTR m_lpExistingFileName, m_lpNewFileName;
HWND m_hwnd, m_hwndFile, m_hwndStream;
ULONG m_shift;
LONG m_dwRefCount;
LONG m_hwndLock;
BOOL m_bCancel;
CopyDlg()
{
m_dwRefCount = 1;
}
void AddRef()
{
InterlockedIncrement(&m_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&m_dwRefCount))
{
delete this;
}
}
void LockWnd()
{
InterlockedIncrement(&m_hwndLock);
}
void UnlockWnd()
{
if (!InterlockedDecrement(&m_hwndLock))
{
// want m_hwnd be valid at PostMessage(p->m_hwnd,) time
EndDialog(m_hwnd, 0);
}
}
void OnProgress(DWORD dwCallbackReason, ProgressData* p)
{
if (dwCallbackReason == CALLBACK_STREAM_SWITCH)
{
if (p->dwStreamNumber == 1)
{
m_shift = 0;
LONGLONG QuadPart = p->TotalFileSize.QuadPart;
while (QuadPart > MAXLONG)
{
m_shift++;
QuadPart >>= 1;
}
SendMessage(m_hwndFile, PBM_SETRANGE32, 0, (LPARAM)QuadPart);
SendMessage(m_hwndFile, PBM_SETPOS, 0, 0);
}
SendMessage(m_hwndStream, PBM_SETRANGE32, 0, (LPARAM)(p->StreamSize.QuadPart >> m_shift));
SendMessage(m_hwndStream, PBM_SETPOS, 0, 0);
}
else
{
SendMessage(m_hwndStream, PBM_SETPOS, (LPARAM)(p->StreamBytesTransferred.QuadPart >> m_shift), 0);
SendMessage(m_hwndFile, PBM_SETPOS, (LPARAM)(p->TotalBytesTransferred.QuadPart >> m_shift), 0);
}
}
ULONG StartCopy(PCWSTR lpExistingFileName, PCWSTR lpNewFileName)
{
m_bCancel = FALSE;
m_lpExistingFileName = lpExistingFileName;
m_lpNewFileName = lpNewFileName;
LockWnd();
AddRef();
if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CopyThread), this, 0, 0))
{
CloseHandle(hThread);
return NOERROR;
}
Release();
UnlockWnd();
return GetLastError();
}
static INT_PTR CALLBACK _DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
}
if (CopyDlg* p = reinterpret_cast<CopyDlg*>(GetWindowLongPtrW(hwndDlg, DWLP_USER)))
{
p->AddRef();
INT_PTR r = p->DialogProc(hwndDlg, uMsg, wParam, lParam);
p->Release();
return r;
}
return 0;
}
INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
Release();
break;
case WM_DESTROY:
m_bCancel = TRUE;
break;
case WM_COMMAND:
switch (wParam)
{
case MAKEWPARAM(IDCANCEL, BN_CLICKED):
m_bCancel = TRUE;
break;
}
break;
case WM_CLOSE:
m_bCancel = TRUE;
UnlockWnd();
ShowWindow(hwndDlg, SW_HIDE);//for not get wm_close twice
break;
case WM_COPYRESULT:
UnlockWnd();
// lParam == error code from CopyFileExW
DbgPrint("CopyFileExW=%u\n", (ULONG)lParam);
break;
case WM_PROGRESS:
OnProgress((DWORD)wParam, reinterpret_cast<ProgressData*>(lParam));
delete (void*)lParam;
break;
case WM_INITDIALOG:
AddRef();
m_hwnd = hwndDlg;
m_hwndStream = GetDlgItem(hwndDlg, IDC_PROGRESS1);
m_hwndFile = GetDlgItem(hwndDlg, IDC_PROGRESS2);
m_hwndLock = 1;
StartCopy(L"**", L"**");
break;
}
return 0;
}
static ULONG CALLBACK CopyThread(CopyDlg* p)
{
PostMessage(p->m_hwnd, WM_COPYRESULT, 0, CopyFileExW(
p->m_lpExistingFileName, p->m_lpNewFileName,
reinterpret_cast<LPPROGRESS_ROUTINE>(CopyProgressRoutine),
p, &p->m_bCancel, 0) ? NOERROR : GetLastError());
p->Release();
return 0;
}
static DWORD CALLBACK CopyProgressRoutine(
__in LARGE_INTEGER TotalFileSize,
__in LARGE_INTEGER TotalBytesTransferred,
__in LARGE_INTEGER StreamSize,
__in LARGE_INTEGER StreamBytesTransferred,
__in DWORD dwStreamNumber,
__in DWORD dwCallbackReason,
__in HANDLE /*hSourceFile*/,
__in HANDLE /*hDestinationFile*/,
__in_opt CopyDlg* p
)
{
switch(dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
case CALLBACK_STREAM_SWITCH:
if (ProgressData* data = new ProgressData)
{
data->TotalFileSize = TotalFileSize;
data->TotalBytesTransferred = TotalBytesTransferred;
data->StreamSize = StreamSize;
data->StreamBytesTransferred = StreamBytesTransferred;
data->dwStreamNumber = dwStreamNumber;
if (!PostMessage(p->m_hwnd, WM_PROGRESS, dwCallbackReason, (LPARAM)data))
{
delete data;
return PROGRESS_CANCEL;
}
}
break;
}
// for debugging
//Sleep(3000);
//MessageBoxW(0,0,0,0);
return PROGRESS_CONTINUE;
}
};
if (CopyDlg* p = new CopyDlg)
{
DialogBoxParamW((HINSTANCE)&__ImageBase, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP, CopyDlg::_DialogProc, (LPARAM)p);
p->Release();
}
I try to do a notepad++ plugin using C++. I need to do a dockable window in it. It should look like the search window in notepad++. I cant find a solution. So could you please help me?
Also I have the code "DockingFeature" which notepad++ provides me. However I cant create a dockable window.
//StaticDialog.h
#ifndef STATIC_DIALOG_H
#define STATIC_DIALOG_H
//#include "resource.h"
#include "Window.h"
#include "Notepad_plus_msgs.h"
enum PosAlign{ALIGNPOS_LEFT, ALIGNPOS_RIGHT, ALIGNPOS_TOP, ALIGNPOS_BOTTOM};
struct DLGTEMPLATEEX {
WORD dlgVer;
WORD signature;
DWORD helpID;
DWORD exStyle;
DWORD style;
WORD cDlgItems;
short x;
short y;
short cx;
short cy;
// The structure has more fields but are variable length
} ;
class StaticDialog : public Window
{
public :
StaticDialog() : Window() {};
~StaticDialog(){
if (isCreated()) {
::SetWindowLongPtr(_hSelf, GWL_USERDATA, (long)NULL); //Prevent run_dlgProc from doing anything, since its virtual
destroy();
}
};
virtual void create(int dialogID, bool isRTL = false);
virtual bool isCreated() const {
return (_hSelf != NULL);
};
void goToCenter();
void destroy() {
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGREMOVE, (WPARAM)_hSelf);
::DestroyWindow(_hSelf);
};
protected :
RECT _rc;
static BOOL CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0;
void alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT & point);
HGLOBAL makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate);
};
#endif //STATIC_DIALOG_H
//staticDialog.cpp
#include "StaticDialog.h"
void StaticDialog::goToCenter()
{
RECT rc;
::GetClientRect(_hParent, &rc);
POINT center;
center.x = rc.left + (rc.right - rc.left)/2;
center.y = rc.top + (rc.bottom - rc.top)/2;
::ClientToScreen(_hParent, ¢er);
int x = center.x - (_rc.right - _rc.left)/2;
int y = center.y - (_rc.bottom - _rc.top)/2;
::SetWindowPos(_hSelf, HWND_TOP, x, y, _rc.right - _rc.left, _rc.bottom - _rc.top, SWP_SHOWWINDOW);
}
HGLOBAL StaticDialog::makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate)
{
// Get Dlg Template resource
HRSRC hDialogRC = ::FindResource(_hInst, MAKEINTRESOURCE(dialogID), RT_DIALOG);
HGLOBAL hDlgTemplate = ::LoadResource(_hInst, hDialogRC);
DLGTEMPLATE *pDlgTemplate = (DLGTEMPLATE *)::LockResource(hDlgTemplate);
// Duplicate Dlg Template resource
unsigned long sizeDlg = ::SizeofResource(_hInst, hDialogRC);
HGLOBAL hMyDlgTemplate = ::GlobalAlloc(GPTR, sizeDlg);
*ppMyDlgTemplate = (DLGTEMPLATE *)::GlobalLock(hMyDlgTemplate);
::memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg);
DLGTEMPLATEEX *pMyDlgTemplateEx = (DLGTEMPLATEEX *)*ppMyDlgTemplate;
if (pMyDlgTemplateEx->signature == 0xFFFF)
pMyDlgTemplateEx->exStyle |= WS_EX_LAYOUTRTL;
else
(*ppMyDlgTemplate)->dwExtendedStyle |= WS_EX_LAYOUTRTL;
return hMyDlgTemplate;
}
void StaticDialog::create(int dialogID, bool isRTL)
{
if (isRTL)
{
DLGTEMPLATE *pMyDlgTemplate = NULL;
HGLOBAL hMyDlgTemplate = makeRTLResource(dialogID, &pMyDlgTemplate);
_hSelf = ::CreateDialogIndirectParam(_hInst, pMyDlgTemplate, _hParent, (DLGPROC)dlgProc, (LPARAM)this);
::GlobalFree(hMyDlgTemplate);
}
else
_hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, (DLGPROC)dlgProc, (LPARAM)this);
if (!_hSelf)
{
return;
}
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGADD, (WPARAM)_hSelf);
}
BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG :
{
StaticDialog *pStaticDlg = (StaticDialog *)(lParam);
pStaticDlg->_hSelf = hwnd;
::SetWindowLongPtr(hwnd, GWL_USERDATA, (long)lParam);
::GetWindowRect(hwnd, &(pStaticDlg->_rc));
pStaticDlg->run_dlgProc(message, wParam, lParam);
return TRUE;
}
default :
{
StaticDialog *pStaticDlg = (StaticDialog *)(::GetWindowLongPtr(hwnd, GWL_USERDATA));
if (!pStaticDlg)
return FALSE;
return pStaticDlg->run_dlgProc(message, wParam, lParam);
}
}
}
void StaticDialog::alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT & point)
{
RECT rc, rc2;
::GetWindowRect(handle, &rc);
point.x = rc.left;
point.y = rc.top;
switch (pos)
{
case ALIGNPOS_LEFT :
::GetWindowRect(handle2Align, &rc2);
point.x -= rc2.right - rc2.left;
break;
case ALIGNPOS_RIGHT :
::GetWindowRect(handle, &rc2);
point.x += rc2.right - rc2.left;
break;
case ALIGNPOS_TOP :
::GetWindowRect(handle2Align, &rc2);
point.y -= rc2.bottom - rc2.top;
break;
default : //ALIGNPOS_BOTTOM
::GetWindowRect(handle, &rc2);
point.y += rc2.bottom - rc2.top;
break;
}
::ScreenToClient(_hSelf, &point);
}