Create OpenGL window win32 API - windows

I am trying to create an OpenGL window inside of already existing parent window using win32 api. However, when creating a child, the function CreateWindowEx returns NULL with an error 1407 ERROR_CANNOT_FIND_WND_CLASS. The window must be a child so there is a space left for other controls such as buttons and checkboxes...
Part of code that creates the parent window:
WNDCLASSEX wincl;
wincl.style = 0;
wincl.lpszMenuName = NULL;
wincl.lpszClassName = L"WindowClass";
wincl.lpfnWndProc = WndProc;
wincl.hInstance = hInstance;
wincl.hIconSm = LoadIcon(NULL, IDC_ICON);
wincl.hIcon = LoadIcon(NULL, IDC_ICON);
wincl.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wincl.cbWndExtra = 0;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.cbClsExtra = 0;
if(!RegisterClassEx(&wincl)){
std::cout << "Window failed to register!" << std::endl;
return false;
}
DWORD style = WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | WS_SIZEBOX;
parentHwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
L"WindowClass",
L"Title",
style,
CW_USEDEFAULT, // X
CW_USEDEFAULT, // Y
800, // Width
600, // Height
NULL,
NULL,
hInstance,
0);
if (parentHwnd == NULL){
std::cout << "Window failed to create!" << std::endl;
return false;
}
ShowWindow(parentHwnd, SW_SHOWNORMAL);
UpdateWindow(parentHwnd);
Inside the message loop, I am creating the child window:
LRESULT WndProc(HWND hwnd, UINT Msg, WPARAM wParam , LPARAM lParam){
switch (Msg){
case WM_CREATE:{
// Create child OpenGL window
childHwnd = CreateWindowEx(0,
L"OpenGL",
NULL,
WS_CLIPCHILDREN | WS_VISIBLE,
100, // X
100, // Y
400, // Width
300, // Height
hwnd, // Parent
0, // ID
NULL,
NULL);
// Prints 1407 ERROR_CANNOT_FIND_WND_CLASS
std::cout << "Last error: " << GetLastError() << std::endl;
}
default:{
return DefWindowProc(hwnd, Msg, wParam, lParam);
}
}
return 0;
}
I have looked into several tutorials (NeHe, cs.rit.edu, and many other but I can't post more than 2 links) and all of them used L"OpenGL" as class name for the second argument in the function CreateWindowEx().
What am I doing wrong? Is "OpenGL" a valid class name such as "Edit" / "Static" / "Button" ? Can I replace it with something else? I tried "Static" but it did not rendered anything.
I know there are many libraries (GLFW, SDL, ...) that handle window creation, but I need to use pure win32 API.
UPDATE:
RegisterClass() solved the problem. Here is the code that works for me:
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance; // Same hinstance used by parent
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"OpenGL";
wc.style = CS_OWNDC;
if(!RegisterClass(&wc)){
std::cout << "Failed to register window!" << std::endl;
return false;
}
childHwnd = CreateWindowEx(0, // Must be zero
L"OpenGL",
NULL,
WS_VISIBLE | WS_CHILD,
100, // X
100, // Y
400, // Width
300, // Height
parentHwnd, // Parent HWND
0, // ID
hInstance, // Same hinstance used by parent
NULL);

Related

CreateSwapChainForHwnd with DXGI_SCALING_STRETCH : erroneous stretch

I'm using a SwapChain created with CreateSwapChainForHwnd with DXGI_SCALING_STRETCH scaling. When the sizes of the swapchain and the window client area differ, the SwapChain's associated bitmap is correctly scaled, but its origin is moved as if the window borders were accounted in the stretch process although there sizes are constant.
Is there a way to fix or circumvent this issue ?
Thank you for the help !
(More precisely : for a client area larger that swapchain, the origin of the bitmap in shifted down, right proportionally respectively to title-bar height and left border width, the part of the bitmap thus moved outside the client area being invisible; a reverse shift is produced with client area smaller than swapchain).
Below a minimal code to reproduce this issue based on visual studio desktop app template. I put everything in InitInstance() (I know it's bad), appart from classical DX #includes and declarations. It corresponds to https://learn.microsoft.com/en-us/windows/win32/direct2d/devices-and-device-contexts except I create the swapchain with CreateSwapChainForHwnd() instead of CreateSwapChainForCoreWindow() :
The pb appears fror MDI childs, as well as for SDI windows with a menu. The way the buffer bitmap is drawn, CopyFromMemory, DrawBitmap or even D2D drawing doesn't matter, as shown in images with the two diagonals made with DrawLine().226707-stretchedout.png
Actually the white bands are flickering during resizing.
Example, starting from Visual Studios's destop app template, replace InitInstance() below and add D2D includes and declarations...
...
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
ATOM aMainClass;
ATOM aChildClass;
const TCHAR lpszMainClassName[] = TEXT("FCLAYERMAIN");
const TCHAR lpszChildClassName[] = TEXT("FCLAYERCHILD");
hInst = hInstance;
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
//prepare MDI windows (main + MDI client + one child)
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = lpszMainClassName;
wcex.hIconSm = NULL;
aMainClass = RegisterClassEx(&wcex);
wcex.lpfnWndProc = ChildWndProc;
wcex.lpszClassName = lpszChildClassName;
wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
aChildClass = RegisterClassEx(&wcex);
hWndMDI = CreateWindow((LPCTSTR)aMainClass,
szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
NULL,
NULL,
hInst,
NULL);
CLIENTCREATESTRUCT ccs;
ccs.idFirstChild = 5000;
hwndClient = CreateWindow(TEXT("mdiclient"),
NULL,
WS_CLIPCHILDREN | WS_CHILD | WS_HSCROLL | WS_VSCROLL | MDIS_ALLCHILDSTYLES,
0,
0,
0,
0,
hWndMDI,
0,
hInst,
(LPSTR)&ccs);
ShowWindow(hWndMDI, SW_SHOW);
ShowWindow(hwndClient, SW_SHOW);
//so as the initial child's client are fits the D2D swapchain (256*256px)
RECT Rect;
Rect.left = 0;
Rect.top = 0;
Rect.right = 256;
Rect.bottom = 256;
AdjustWindowRect(&Rect,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX, FALSE);
HWND hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
ShowWindow(hWnd, SW_SHOW);
//create D2D + D3D + DXGI machinery
D2D1_FACTORY_OPTIONS debugOptions;
debugOptions.debugLevel = D2D1_DEBUG_LEVEL_NONE;
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory1),
&debugOptions,
(void*)&m_D2DFactory
);
if (SUCCEEDED(hr))
{
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_pD3D11device,
NULL,
NULL
);
}
if (SUCCEEDED(hr))
{
IDXGIDevice1 dxgiDevice;
hr = m_pD3D11device->QueryInterface(__uuidof(IDXGIDevice), (void*)&dxgiDevice);
if (SUCCEEDED(hr))
{
hr = m_D2DFactory->CreateDevice(
dxgiDevice,
&m_d2dDevice
);
if (SUCCEEDED(hr))
{
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
);
}
}
IDXGIAdapter dxgiAdapter;
if (SUCCEEDED(hr))
{
hr = dxgiDevice->GetAdapter(&dxgiAdapter);
if (SUCCEEDED(hr))
{
hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&m_dxgiFactory));
SafeRelease(&dxgiAdapter);
dxgiDevice->SetMaximumFrameLatency(1);
SafeRelease(&dxgiDevice);
}
}
}
if (SUCCEEDED(hr))
{
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = 256;
swapChainDesc.Height = 256;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; //DXGI_SCALING_ASPECT_RATIO_STRETCH seems not supported
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.Flags = 0;
hr = m_dxgiFactory->CreateSwapChainForHwnd(
m_pD3D11device,
hWnd,
&swapChainDesc,
nullptr,
nullptr,
&(m_swapChain)
);
}
if (SUCCEEDED(hr))
{
hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_dxgiBackBuffer));
if (SUCCEEDED(hr))
{
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
0,
0,
NULL
);
/
//just in case dpi=0 doesn't work:
bitmapProperties.dpiX = GetDpiForWindow(hWnd);
bitmapProperties.dpiY = GetDpiForWindow(hWnd);
/
hr = m_d2dContext->CreateBitmapFromDxgiSurface(
m_dxgiBackBuffer,
&bitmapProperties,
&(m_pBitmap)
);
}
}
if (SUCCEEDED(hr))
{
D2D1_SIZE_U bitmapSize = m_pBitmap->GetPixelSize();
m_d2dContext->BeginDraw();
m_d2dContext->SetTarget(m_pBitmap);
ID2D1SolidColorBrush* m_pBlackBrush;
hr = m_d2dContext->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Red, 1.0f),
&m_pBlackBrush
);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, 0.0f),
D2D1::Point2F(bitmapSize.height, bitmapSize.width),
m_pBlackBrush,
2.0f,
NULL);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, bitmapSize.width),
D2D1::Point2F(bitmapSize.height, 0.0f),
m_pBlackBrush,
2.0f,
NULL);
}
hr = m_d2dContext->EndDraw();
hr = m_swapChain->Present(1, 0);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Analyse les sélections de menu :
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
...
Replace your AdjustWindowRect with AdjustWindowRectEx and specify WS_EX_MDICHILD extended window style.
Another thing, you’re rendering and even presenting some graphics from within InitInstance method. That’s not the right time for that, too early. Instead, you should move rendering into WM_PAINT handler of the window for which you have created the swap chain.
P.S. This sample might help.
The streching error only occurs for windows with borders and/or menu bar. A possible workaround is thus to add a borderless child window (hWndPic) to the MDI child (hWnd) itself:
//initialization :
hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_SIZEBOX|WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU ,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
hWndPic = CreateWindow(
(LPCTSTR)aPicClass,
NULL,
WS_CHILD,
0,
0,
256,
256,
hWnd,
0,
hInst,
NULL);
Then resize this picture window in response to WM_SIZE sent to the MDI child hWnd; that's all! then there is no need to repaint the picture when the window is resized and the stretch process is correctly handled by D2D, benefitting from hardware acceleration.
// windows procedures :
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_SIZE:
{
RECT Rect;
GetClientRect(hWnd,&Rect);
SetWindowPos(
hWndPic,
HWND_TOP,
0,
0,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
0
);
}
}
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK PicWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}

Force Win32 common controls to draw on `ID2D1HwndRenderTarget`?

I draw most of my UI using ID2D1HwndRenderTarget, but I want some classic window controls: button, edit. How to
ID2D1HwndRenderTarget * canvas = nullptr; // it's global object
HWND button = nullptr; // it's global object
HWND edit = nullptr; // it's global object
HWND custom = nullptr; // it's global object
// mainWindow WinPproc
case WM_CREATE:
button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
custom = CreateWindowExW(0, L"custom", L"Custom", WS_CHILD | WS_VISIBLE, 10, 90, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // it's my custom class
break;
case WM_PAINT:
BeginPaint(hWnd, nullptr);
render_target->BeginPaint();
... GUI rendering stuff ....
HRESULT result = render_target->EndDraw();
if(result != S_OK)
{
// Error handling
...
}
EndPaint(hWnd, nullptr);
break;
// custom WinProc
case WM_PAINT:
BeginPaint(hWnd, nullptr);
render_target->BeginPaint();
... rendering stuff ....
HRESULT result = render_target->EndDraw();
if(result != S_OK)
{
// Error handling
...
}
EndPaint(hWnd, nullptr);
break;
Only things painted with render_target are visible. I understand why: becausebutton and edit are default win32 controls, internally drawn using PAINTSTRUCT->HDC context. I read Direct2D and GDI Interoperability Overview and get the concept, but still don't know where this HDC intrecpet should take place? I don't want touch default control WM_PAINT. I have to supclass all default win32 controls?
How to force those Win32 controls to draw onto my render_target?
If you want to share a device context (HDC) between GDI and Direct2D, you can use a ID2D1DCRenderTarget and Bind to this HDC when you want to render on it.
This is demonstrated in this official sample : GDI/Direct2D Interoperability Sample
Note, as is, it doesn't compile/work with today's Visual Studio. So, here is a similar code here with a simple button and textbox:
#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <d2d1.h>
template<class Interface>
inline void
SafeRelease(Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
class DemoApp
{
public:
DemoApp();
~DemoApp();
HRESULT Initialize();
private:
HRESULT CreateDeviceIndependentResources();
HRESULT CreateDeviceResources();
void DiscardDeviceResources();
HRESULT OnRender(const PAINTSTRUCT& ps);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
HWND m_hwnd;
ID2D1Factory* m_pD2DFactory;
ID2D1DCRenderTarget* m_pDCRT;
ID2D1SolidColorBrush* m_pBlackBrush;
};
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
if (SUCCEEDED(CoInitialize(NULL)))
{
DemoApp app;
if (SUCCEEDED(app.Initialize()))
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CoUninitialize();
}
return 0;
}
DemoApp::DemoApp() :
m_hwnd(NULL),
m_pD2DFactory(NULL),
m_pDCRT(NULL),
m_pBlackBrush(NULL)
{
}
DemoApp::~DemoApp()
{
SafeRelease(&m_pD2DFactory);
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
HRESULT DemoApp::Initialize()
{
HRESULT hr;
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = (HINSTANCE)&__ImageBase;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&wcex);
// Create the application window.
// Because the CreateWindow function takes its size in pixels, we obtain the system DPI and use it to scale the window size.
FLOAT dpiX, dpiY;
m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
m_hwnd = CreateWindow(
L"D2DDemoApp",
L"Direct2D Demo App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
NULL,
NULL,
(HINSTANCE)&__ImageBase,
this
);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
UpdateWindow(m_hwnd);
}
}
return hr;
}
HRESULT DemoApp::CreateDeviceIndependentResources()
{
// Create D2D factory
return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
}
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pDCRT)
{
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
if (SUCCEEDED(hr))
{
// Create a black brush.
hr = m_pDCRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
}
}
return hr;
}
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
HRESULT DemoApp::OnRender(const PAINTSTRUCT& ps)
{
HRESULT hr;
RECT rc;
// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
// Draw the pie chart with Direct2D.
// Create the DC render target.
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);
m_pDCRT->BeginDraw();
m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pDCRT->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(150.0f, 150.0f), 100.0f, 100.0f), m_pBlackBrush, 3.0);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f + 100.0f * 0.15425f), (150.0f - 100.0f * 0.988f)), m_pBlackBrush, 3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f + 100.0f * 0.525f), (150.0f + 100.0f * 0.8509f)), m_pBlackBrush, 3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f - 100.0f * 0.988f), (150.0f - 100.0f * 0.15425f)), m_pBlackBrush, 3.0
);
hr = m_pDCRT->EndDraw();
if (SUCCEEDED(hr))
{
// Draw the pie chart with GDI.
// Save the original object.
HGDIOBJ original = NULL;
original = SelectObject(ps.hdc, GetStockObject(DC_PEN));
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(ps.hdc, blackPen);
Ellipse(ps.hdc, 300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
Polyline(ps.hdc, pntArray1, 2);
Polyline(ps.hdc, pntArray2, 2);
Polyline(ps.hdc, pntArray3, 2);
DeleteObject(blackPen);
// Restore the original object.
SelectObject(ps.hdc, original);
}
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);
auto button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hwnd, (HMENU)1, (HINSTANCE)&__ImageBase, 0); // win32 control
auto edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hwnd, (HMENU)2, (HINSTANCE)&__ImageBase, 0); // win32 control
return 1;
}
LRESULT result = 0;
DemoApp* pDemoApp = (DemoApp*)(GetWindowLongPtr(hwnd, GWLP_USERDATA));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_PAINT:
case WM_DISPLAYCHANGE:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
pDemoApp->OnRender(ps);
EndPaint(hwnd, &ps);
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
return result;
}
And here is how it renders (left circle is Direct2D, right is aliased GDI):
How to force Win32 controls to draw onto my ID2D1Bitmap1? Is that even possible?
Yes. You can write GDI content to a Direct2D GDI-compatible render target. This approach is useful for applications that primarily render with Direct2D but have an extensibility model or other legacy content that requires the ability to render with GDI.
Step:
To render GDI content to a Direct2D GDI-compatible render target, use
an ID2D1GdiInteropRenderTarget, which provides access to a device
context that can accept GDI draw calls. Unlike other interfaces, an
ID2D1GdiInteropRenderTarget object is not created directly. Instead,
use the QueryInterface method of an existing render target instance.
Refer: Draw GDI Content to a Direct2D GDI-Compatible Render Target

RegisterPointerInputTarget does nothing even after returning true

I've tried using RegisterPointerInputTarget (Win32 API) to capture all PT_TOUCH messages, but my app behaves exactly as if the function was never called. I've followed all advice I could find: the app has uiAccess=true, is signed, and is installed to C:\Program Files. The windows is created as a child of HWND_MESSAGE and the RegisterPointerInputTarget returns 1. However, my WndProc is not receiving any WM_POINTER messages at all. If I instead create a normal window, my WndProc receives WM_POINTER messages as expected, only when I'm pointing over the window.
Below is the code of my program. Note that the calls to InitializeTouchInjection, ShowWindow, UpdateWindow and AccSetRunningUtilityState should be unnecessary, I've just been trying everything I've seen in any example I could find.
I'm using a Wacom Intuos, tried both with the pen and with the fingers.
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include "oleacc.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NCPOINTERUP:
case WM_NCPOINTERDOWN:
case WM_NCPOINTERUPDATE:
case WM_TOUCH:
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERUPDATE:
{
std::cout << "TOUCH MESSAGE " << message << " WPARAM " << wParam << " LPARAM " << lParam << std::endl;
break;
}
}
std::cout << "SOME MESSAGE " << message << " HWND " << hWnd << " WPARAM " << wParam << " LPARAM " << lParam << std::endl;
return DefWindowProc(hWnd, message, wParam, lParam);
}
int main()
{
//InitializeTouchInjection(10, TOUCH_FEEDBACK_NONE);
LPCSTR className = "TouchHookWindow";
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
//wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
//wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
//wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = className;
if (!RegisterClass(&wc))
{
std::cout << "Failed to register window class!\n";
return -1;
}
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPEDWINDOW;
//DWORD dwExStyle = WS_EX_NOACTIVATE;// | WS_EX_TRANSPARENT;
//DWORD dwStyle = WS_OVERLAPPEDWINDOW;
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = 100;
rc.bottom = 100;
AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
//HWND hWnd = CreateWindowEx(dwExStyle, className, "Touch Hook", dwStyle, 0, 0, rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, hInstance, NULL);
HWND hWnd = CreateWindowEx(0, className, "TouchHook", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
//HWND hWnd = CreateWindowEx(dwExStyle, className, "TouchHook", dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, hInstance, NULL);
//HWND hWnd = CreateWindowEx(dwExStyle, className, "TouchHook", dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
std::cout << "Failed to create window!\n";
return -1;
}
//ShowWindow(hWnd, SW_SHOW);
//UpdateWindow(hWnd);
//SetForegroundWindow(hWnd);
//SetFocus(hWnd);
{
BOOL result = RegisterTouchWindow(hWnd, 0);
std::cout << "RegisterTouchWindow result " << result << std::endl;
}
{
BOOL result = RegisterPointerInputTarget(hWnd, PT_TOUCH);
std::cout << "RegisterPointerInputTarget result " << result << std::endl;
}
{
//HRESULT hr = AccSetRunningUtilityState(hWnd, ANRUS_TOUCH_MODIFICATION_ACTIVE, ANRUS_TOUCH_MODIFICATION_ACTIVE);
//std::cout << "AccSetRunningUtilityState " << (FAILED(hr) ? "FAILED" : "SUCCESS") << std::endl;
}
{
MSG msg;
BOOL result;
while (result = GetMessage(&msg, NULL, 0,0) != 0)
{
if (result != -1)
{
if (msg.message == WM_QUIT)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
return 0;
}

creating rebar control and introduction a band with toolbar into the rebar

I use this code to create a rebar control, and introduction a band with toolbar into the rebar.
But when window is shown up, I can not see the toolbar. and when I check the height of the rebar, in this line of code: int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; I find rebar's height is only 4 pixels.
#include <windows.h>
#include <stdlib.h>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE instance;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
instance = hInstance;
WNDCLASSEX 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 = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"Example";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
RegisterClassEx(&wcex);
HWND hWnd = CreateWindow(L"Example", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
500, 500, NULL, NULL, hInstance, NULL);
// Initialize common controls.
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
HWND hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, 100, 50, hWnd, NULL, instance, NULL);
// create toolbar
HWND hWndToolbar = CreateWindowEx(0 , TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_TOOLTIPS,
0, 0, 0, 0, hwndRebar, (HMENU)0, instance, NULL);
HIMAGELIST hImageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, 3, 0);
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
TBBUTTON tbb[4] =
{
{0,0,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{1,1,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{2,2,TBSTATE_ENABLED,TBSTYLE_BUTTON},
};
SendMessage(hWndToolbar, (UINT) TB_ADDBUTTONS, 3, (LPARAM)&tbb);
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar , SW_SHOW);
// Initialize band info.
REBARBANDINFO rbBand = { sizeof(REBARBANDINFO) };
rbBand.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_COLORS;
rbBand.fStyle = RBBS_GRIPPERALWAYS;
// Get the height of the toolbar.
DWORD dwBtnSize = (DWORD)SendMessage(hWndToolbar, TB_GETBUTTONSIZE, 0,0);
// Set values unique to the band with the toolbar.
rbBand.clrFore = RGB(233, 233, 233);
rbBand.clrBack = RGB(200, 200, 200);
rbBand.lpText = TEXT("");
rbBand.hwndChild = hWndToolbar;
rbBand.cyChild = LOWORD(dwBtnSize);
rbBand.cyMinChild = LOWORD(dwBtnSize);
rbBand.cxMinChild = 3 * HIWORD(dwBtnSize);
// The default width is the width of the buttons.
rbBand.cx = 0;
// Add the band
SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
// show the main window
ShowWindow(hWnd, nCmdShow);
// check the rebar size
WINDOWPLACEMENT wp;
GetWindowPlacement(hwndRebar, &wp);
int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
As follows from the comments, the solution was:
REBARBANDINFO rbBand;
rbBand.cbSize = REBARBANDINFO_V3_SIZE;
// initialize the rest here
This seems to affect older versions of Windows (specifically, XP), because the original code compiles and runs fine on Windows 7.
This was mentioned in the comments to a MSDN page: http://msdn.microsoft.com/en-us/library/windows/desktop/bb774393.aspx
As was already stated, the cbSize member of the REBARBANDINFO struct needed to be set.
Note the following about the provided code as well:
The images on the toolbar buttons don't show up. Right after creating the image list you have to make the following call to load the images:
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
The TBBUTTON array is declared with a size of 4 but it only gets populated with 3 items. Technically it should be declared as:
TBBUTTON tbb[3]

CreateWindowEx, GetLastError, Windows 7, and XP oddities and odd behavior

The following code works on Window 7, but when I run it on Windows XP, it fails with an error message returned by windows: "The system can not find the file specified".
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <uxtheme.h>
#include <string>
const char g_szClassName[] = "myWindowClass";
const char title[] = "Window Title\0";
COLORREF WinColor;
HFONT defaultFont;
NONCLIENTMETRICSA Metrics;
DWORD dwVersion;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
HBRUSH hBrushColor;
bool LastError = false;
bool W32Error (const char * Msgtext);
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_NCCREATE:
{
Metrics.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo (SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &Metrics, 0);
defaultFont = (HFONT) CreateFontIndirect (& Metrics.lfMessageFont);
return TRUE;
}
break;
case WM_CTLCOLORSTATIC: {
dwVersion = GetVersion();
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
if ( (IsAppThemed()) && (dwMajorVersion < 6) ) {
WinColor = GetSysColor(COLOR_WINDOW);
SetBkColor((HDC)wParam, WinColor);
return (LRESULT)hBrushColor;
}
}
break;
case WM_CREATE: {
HWND hButton = CreateWindowEx(
0,
"BUTTON", "Button",
WS_TABSTOP |
WS_VISIBLE |
WS_CHILD |
BS_NOTIFY |
BS_PUSHBUTTON,
10, 10, 96, 32, hwnd,
(HMENU)50,
GetModuleHandle(NULL),
NULL);
if (W32Error ("Button Creation Failed\nReason:")) exit (1);
return TRUE;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// Initialize common controls.
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(icc);
icc.dwICC = ICC_WIN95_CLASSES |
ICC_COOL_CLASSES |
ICC_INTERNET_CLASSES|
ICC_LINK_CLASS |
ICC_STANDARD_CLASSES|
ICC_PROGRESS_CLASS |
ICC_USEREX_CLASSES;
InitCommonControlsEx(&icc);
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// see if something is going on before the window registration takes place....
if (W32Error ("Previous Check for Error?\nReason:")) exit (1);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
if (W32Error ("Window Registration Failed\nReason:")) exit (1);
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if (W32Error ("Window Creation Failed\nReason:")) exit (1);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
SendMessageA(hwnd, WM_SETFONT, WPARAM (defaultFont), TRUE);
SendMessageA(hwnd, WM_SETTEXT, WPARAM(NULL) , LPARAM (title));
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
bool W32Error (const char * Msgtext)
{
LPTSTR errorText = NULL;
DWORD dwLastError = GetLastError();
if (!dwLastError) {
LastError = false;
return LastError;
}
// use system message tables to retrieve error text
// allocate buffer on local heap for error text
// Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM
dwLastError,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&errorText, // output
0, // minimum size for output buffer
0); // arguments - see note
if ( NULL != errorText ) {
std::string Message;
Message += Msgtext;
Message += "\n";
Message += errorText;
MessageBoxA(NULL, (LPCSTR)Message.c_str(), "An Internal Error Occurred", MB_OK);
LocalFree(errorText);
errorText = NULL;
LastError = true;
} else {
LastError = false;
}
return LastError;
}
As an additional, I have a resource.rc and resource.h file linked in with winres that complies in a manifest.xml that provides a controls version 6 specification.
Ok, so here is the deal, This runs on Windows 7 without a hitch. And it runs on XP, unless I put in the line if (W32Error ("Window Registration Failed\nReason:")) exit (1);
(basically, it calls my error checking routine which includes GetLastError())
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
if (W32Error ("Window Registration Failed\nReason:")) exit (1);
When I include that line, I get an error "The system can not find the file specified." The return value back from RegisterClassEx does not go into the if block, so RegisterClassEx returns Ok.
This only happens on Windows XP, it works just fine on Windows 7. If I don't include this line, it works as if nothing is wrong.
The point of this sample is to get themes to work right on both XP and Windows 7. (not an easy task.)
Why is RegisterClassEx returning Ok, but generates an error for GetLastError on Windows XP and not on Windows 7?
Also, what file is it looking for anyway? I have seen other Google results say It has to do with a messed up message loop or window procedure, but that's not the case here.
General mistake... Due to MSDN you may check GetLastError only when RegisterClassEx "return value is zero". When RegisterClassEx or CreateWindowEx or any other functions are succeeded, GetLastError may return garbage, there is no guarantee that GetLastError will returns zero (if different behavior does not described in Return value section).

Resources