C++ - WINAPI - Possible conflict with std::map? - c++

While trying to find a suitable solution to handling window messages, I came across this question here on StackOverflow:
Object oriented c++ win32?
The answers offered great solutions and my personal favourite is the accepted answer using std::map.
However, whenever I use std::map in my code that looks like this:
#include <cstdio>
#include <map>
#include <windows.h>
#include <helixirrwidgets/Window.hpp>
using namespace HelixirrWidgets;
/// ----------------------------
/// #class Window
/// #brief Represents a window.
/// ----------------------------
/// Inner classes:
class Window::Helper{
public:
/// Constructors & destructors:
Helper(void) : active(false), created_window(false), handle_instance(GetModuleHandle(NULL)), handle_window(nullptr), message({0}){
_ms_mopopHelpers.insert({handle_window, this});
}
Helper(Helper const& helper_) : active(false), created_window(false), handle_instance(helper_.handle_instance), handle_window(helper_.handle_window), message(helper_.message){
_ms_mopopHelpers.insert({handle_window, this});
}
Helper(Helper&& helper_) : active(false), created_window(false), handle_instance(std::move(helper_.handle_instance)), handle_window(std::move(helper_.handle_window)), message(std::move(helper_.message)){
helper_.handle_instance = nullptr;
helper_.handle_window = nullptr;
_ms_mopopHelpers.insert({handle_window, this});
}
~Helper(void){
handle_instance = nullptr;
handle_window = nullptr;
_ms_mopopHelpers.erase(handle_window);
}
/// Member data:
bool mutable active, created_window;
HINSTANCE mutable handle_instance;
HWND mutable handle_window;
MSG message;
/// Member functions (overloaded operators, assignment):
Helper& operator=(Helper const& helper_) = default;
Helper& operator=(Helper&& helper_) = default;
/// Static member functions:
static LRESULT CALLBACK handle_callback(HWND handle_window_, UINT message_, WPARAM wparam_, LPARAM lparam_){
if(_ms_mopopHelpers.size() != 0){
// Delegate message handling:
return _ms_mopopHelpers[handle_window_]->_handle_callback(message_, wparam_, lparam_);
}
return 0;
}
private:
/// Member functions:
LRESULT _handle_callback(UINT message_, WPARAM wparam_, LPARAM lparam_){
switch(message_){
case WM_DESTROY:
active = false;
DestroyWindow(handle_window);
return 0;
default:
return DefWindowProc(handle_window, message_, wparam_, lparam_);
}
}
/// Static member data:
static std::map<HWND, Window::Helper*> _ms_mopopHelpers;
};
/// Static member data:
std::map<HWND, Window::Helper*> Window::Helper::_ms_mopopHelpers = {};
/// Constructors & destructors:
Window::Window(void) : _m_bVisible(false), _m_opHelper(new Window::Helper){
}
Window::Window(Window const& window_) = default;
Window::Window(Window&& window_) = default;
Window::Window(std::string const& name_) : _m_bVisible(false), _m_opHelper(new Window::Helper), _m_oName(name_){
}
Window::Window(std::string&& name_) : _m_bVisible(false), _m_opHelper(new Window::Helper), _m_oName(std::move(name_)){
}
Window::~Window(void) = default;
/// Member functions:
bool const& Window::active(void) const noexcept{
return _m_opHelper->active;
}
Window& Window::save_changes(void){
// Create and register window class:
static bool __registered_class_window = false;
static WNDCLASSEX __class_window = {
sizeof(WNDCLASSEX),
CS_HREDRAW | CS_VREDRAW,
Window::Helper::handle_callback,
0, 0,
_m_opHelper->handle_instance,
LoadIcon(_m_opHelper->handle_instance, NULL),
LoadCursor(NULL, IDC_ARROW),
reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)),
NULL,
"Helixirr Widgets Window Class Basic",
LoadIcon(_m_opHelper->handle_instance, NULL)
};
if(!__registered_class_window){
RegisterClassEx(&__class_window);
__registered_class_window = true;
}
// If window hasn't been created yet:
if(!_m_opHelper->created_window){
_m_opHelper->created_window = true;
_m_opHelper->handle_window = CreateWindowEx(
0,
"Helixirr Widgets Window Class Basic",
_m_oName.c_str(),
WS_OVERLAPPEDWINDOW,
_m_uiPos[0], _m_uiPos[1],
_m_uiSize[0], _m_uiSize[1],
NULL, NULL, _m_opHelper->handle_instance, NULL
);
}
_m_opHelper->active = true;
return *this;
}
void Window::show(void){
if(_m_opHelper->active){
if(GetMessage(&_m_opHelper->message, NULL, 0, 0)){
// Handle messages:
TranslateMessage(&_m_opHelper->message);
DispatchMessage(&_m_opHelper->message);
// Handle window displaying:
ShowWindow(_m_opHelper->handle_window, SW_SHOWDEFAULT);
UpdateWindow(_m_opHelper->handle_window);
return;
}
}
}
my window doesn't open for some reason. After cutting way all connections to std::map, window shows up again just fine. What might be causing this wierd phenomenon? It is quite hard to believe there's some kind of conflict.

Judging from your Window::show method, the problem might be that you don't have a message loop. A normal message loop looks like this:
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
But your Window::show method only does this once, so the window might not get pumped the messages needed to actually display the window.

Related

undeclared identifer of a wndproc function

I have a class, which I tried in another MFC project which compiled fine, but for some reason in this project it doesn't compile and complains about my wndproc function that is declared in a header file.
Below is the header file of the class I used in another MFC project. I had to comment out #include <windows.h> because it would say it can't be used in MFC, which wasn't the case in the other MFC project (what must be noted is that the other MFC project had precompiled headers).
skin.h
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.h : CSkin class declaration
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#ifndef _SKIN_H_
#define _SKIN_H_
//#include <windows.h>
#include <afxwin.h>
// --------------------------------------------------------------------------
// The CSkin class will load the skin from a resource
// and subclass the associated window, so that the
// WM_PAINT message will be redirected to the provided
// window procedure. All the skin handling will be automatized.
// --------------------------------------------------------------------------
class CSkin
{
// --------------------------------------------------------------------------
// the skin window procedure, where the class
// will handle WM_PAINT and WM_LBUTTONDOWN automatically.
// --------------------------------------------------------------------------
friend LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
private:
// the associated window handle
HWND m_hWnd;
// the associated CWnd window handle
CWnd * m_cwnd;
// the old window procedure
WNDPROC m_OldWndProc;
// skin region
HRGN m_rgnSkin;
// the internal skin device context handle
HDC m_dcSkin;
// bitmap and old bitmap from the device context
HBITMAP m_hBmp, m_hOldBmp;
// skin dimensions
int m_iWidth, m_iHeight;
// on|off toggle
bool m_bEnabled;
// tell the class if it has a window subclassed.
bool m_bHooked;
// skin retrieval helper
bool GetSkinData(int iSkinRegion, int iSkinBitmap);
public:
// ----------------------------------------------------------------------------
// constructor 1 - use it when you have not already created the app window.
// this one will not subclass automatically, you must call Hook() to subclass.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin(int iSkinRegion, int iSkinBitmap);
// ----------------------------------------------------------------------------
// constructor 2 - use it when you have already created the app window.
// this one will subclass the window automatically.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap);
// ----------------------------------------------------------------------------
// destructor - just call the destroyer
// ----------------------------------------------------------------------------
virtual ~CSkin();
// ----------------------------------------------------------------------------
// destroy skin resources and free allocated resources
// ----------------------------------------------------------------------------
void Destroy();
// ----------------------------------------------------------------------------
// subclass a window.
// ----------------------------------------------------------------------------
bool Hook(HWND hWnd);
// ----------------------------------------------------------------------------
// subclass a window. The CWnd version
// ----------------------------------------------------------------------------
bool Hook(CWnd *cwnd);
// ----------------------------------------------------------------------------
// unsubclass the subclassed window.
// ----------------------------------------------------------------------------
bool UnHook();
// ----------------------------------------------------------------------------
// unsubclass the subclassed window. The CWnd version
// ----------------------------------------------------------------------------
bool UnHookCWnd();
// ----------------------------------------------------------------------------
// tell us if we have a window subclassed.
// ----------------------------------------------------------------------------
bool Hooked();
// ----------------------------------------------------------------------------
// toggle skin on/off.
// ----------------------------------------------------------------------------
bool Enable(bool bEnable);
// ----------------------------------------------------------------------------
// tell if the skinning is enabled
// ----------------------------------------------------------------------------
bool Enabled();
// ----------------------------------------------------------------------------
// return the skin bitmap width.
// ----------------------------------------------------------------------------
int Width();
// ----------------------------------------------------------------------------
// return the skin bitmap height.
// ----------------------------------------------------------------------------
int Height();
// ----------------------------------------------------------------------------
// return the skin device context.
// ----------------------------------------------------------------------------
HDC HDC();
// ----------------------------------------------------------------------------
// return the handle region.
// ----------------------------------------------------------------------------
HRGN HRGN();
// toggle skin on/off for Cwnd
bool EnableCWnd(bool bEnable);
};
#endif
This is the cpp file:
skin.cpp
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.cpp : CSkin class implementation
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "skin.h"
// ----------------------------------------------------------------------------
// constructor 1 - use it when you have not already created the app window.
// this one will not subclass automatically, you must call Hook() and Enable()
// to subclass the app window and enable the skin respectively.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin::CSkin(int iSkinRegion, int iSkinBitmap)
{
// default starting values
m_bHooked = false;
m_OldWndProc = NULL;
// try to retrieve the skin data from resource.
if ( !GetSkinData(iSkinRegion, iSkinBitmap) )
throw ("Unable to retrieve the skin.");
}
// ----------------------------------------------------------------------------
// constructor 2 - use it when you have already created the app window.
// this one will subclass the window and enable the skin automatically.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin::CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap)
{
// default starting values
m_bHooked = false;
m_OldWndProc = NULL;
// initialize
CSkin(iSkinRegion, iSkinBitmap);
// subclass
Hook(hWnd);
// enable
Enable(true);
}
// ----------------------------------------------------------------------------
// destructor - just call the destroyer
// ----------------------------------------------------------------------------
CSkin::~CSkin()
{
Destroy();
}
// ----------------------------------------------------------------------------
// destroy skin resources and free allocated resources
// ----------------------------------------------------------------------------
void CSkin::Destroy()
{
// unhook the window
UnHook();
// free bitmaps and device context
if (m_dcSkin) { SelectObject(m_dcSkin, m_hOldBmp); DeleteDC(m_dcSkin); m_dcSkin = NULL; }
if (m_hBmp) { DeleteObject(m_hBmp); m_hBmp = NULL; }
// free skin region
if (m_rgnSkin) { DeleteObject(m_rgnSkin); m_rgnSkin = NULL; }
}
// ----------------------------------------------------------------------------
// toggle skin on/off - must be Hooked() before attempting to enable skin.
// ----------------------------------------------------------------------------
bool CSkin::Enable(bool bEnable)
{
// refuse to enable if there is no window subclassed yet.
if (!Hooked()) return false;
// toggle
m_bEnabled = bEnable;
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
return true;
}
// ----------------------------------------------------------------------------
// tell if the skinning is enabled
// ----------------------------------------------------------------------------
bool CSkin::Enabled()
{
return m_bEnabled;
}
// ----------------------------------------------------------------------------
// hook a window
// ----------------------------------------------------------------------------
bool CSkin::Hook(HWND hWnd)
{
// unsubclass any other window
if (Hooked()) UnHook();
// this will be our new subclassed window
m_hWnd = hWnd;
// set the skin region to the window
SetWindowRgn(m_hWnd, m_rgnSkin, true);
// subclass the window procedure
m_OldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)SkinWndProc);
// store a pointer to our class instance inside the window procedure.
if (!SetProp(m_hWnd, "skin", (void*)this))
{
// if we fail to do so, we just can't activate the skin.
UnHook();
return false;
}
// update flag
m_bHooked = ( m_OldWndProc ? true : false );
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
// successful return if we're hooked.
return m_bHooked;
}
bool CSkin::Hook(CWnd *cwnd)
{
if (Hooked() )UnHookCWnd();
m_cwnd = cwnd;
m_cwnd->SetWindowRgn(m_rgnSkin, TRUE);
m_bHooked = true;
m_cwnd->InvalidateRect(NULL, TRUE);
//m_cwnd->Invalidate(true);
//m_cwnd->UpdateWindow();
return m_bHooked;
}
// ----------------------------------------------------------------------------
// unhook the window
// ----------------------------------------------------------------------------
bool CSkin::UnHook()
{
// just to be safe we'll check this
WNDPROC OurWnd;
// cannot unsubclass if there is no window subclassed
// returns true anyways.
if (!Hooked()) return true;
// remove the skin region from the window
SetWindowRgn(m_hWnd, NULL, true);
// unsubclass the window procedure
OurWnd = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_OldWndProc);
// remove the pointer to our class instance, but if we fail we don't care.
RemoveProp(m_hWnd, "skin");
// update flag - if we can't get our window procedure address again,
// we failed to unhook the window.
m_bHooked = ( OurWnd ? false : true );
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
// successful return if we're unhooked.
return !m_bHooked;
}
bool CSkin::UnHookCWnd()
{
if (!Hooked()) return true;
m_cwnd->SetWindowRgn(NULL, TRUE);
m_bHooked = false;
m_cwnd->InvalidateRect(NULL, TRUE);
return !m_bHooked;
}
// ----------------------------------------------------------------------------
// tell us if there is a window subclassed
// ----------------------------------------------------------------------------
bool CSkin::Hooked()
{
return m_bHooked;
}
// ----------------------------------------------------------------------------
// return the skin bitmap width
// ----------------------------------------------------------------------------
int CSkin::Width()
{
return m_iWidth;
}
// ----------------------------------------------------------------------------
// return the skin bitmap height
// ----------------------------------------------------------------------------
int CSkin::Height()
{
return m_iHeight;
}
// ----------------------------------------------------------------------------
// return the skin device context
// ----------------------------------------------------------------------------
HDC CSkin::HDC()
{
return m_dcSkin;
}
HRGN CSkin::HRGN()
{
return m_rgnSkin;
}
// ----------------------------------------------------------------------------
// skin retrieval helper
// ----------------------------------------------------------------------------
bool CSkin::GetSkinData(int iSkinRegion, int iSkinBitmap)
{
// get app instance handle
HINSTANCE hInstance = GetModuleHandle(NULL);
// -------------------------------------------------
// retrieve the skin bitmap from resource.
// -------------------------------------------------
m_hBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(iSkinBitmap));
if (!m_hBmp) return false;
// get skin info
BITMAP bmp;
GetObject(m_hBmp, sizeof(bmp), &bmp);
// get skin dimensions
m_iWidth = bmp.bmWidth;
m_iHeight = bmp.bmHeight;
// -------------------------------------------------
// then, we retrieve the skin region from resource.
// -------------------------------------------------
// ask resource for our skin.
HRSRC hrSkin = FindResource(hInstance, MAKEINTRESOURCE(iSkinRegion),"BINARY");
if (!hrSkin) return false;
// this is standard "BINARY" retrieval.
LPRGNDATA pSkinData = (LPRGNDATA)LoadResource(hInstance, hrSkin);
if (!pSkinData) return false;
// create the region using the binary data.
m_rgnSkin = ExtCreateRegion(NULL, SizeofResource(NULL,hrSkin), pSkinData);
// free the allocated resource
FreeResource(pSkinData);
// check if we have the skin at hand.
if (!m_rgnSkin) return false;
// -------------------------------------------------
// well, things are looking good...
// as a quick providence, just create and keep
// a device context for our later blittings.
// -------------------------------------------------
// create a context compatible with the user desktop
m_dcSkin = CreateCompatibleDC(0);
if (!m_dcSkin) return false;
// select our bitmap
m_hOldBmp = (HBITMAP)SelectObject(m_dcSkin, m_hBmp);
// -------------------------------------------------
// done
// -------------------------------------------------
return true;
}
// ------------------------------------------------------------------------
// Default skin window procedure.
// Here the class will handle WM_PAINT and WM_LBUTTONDOWN, originally sent
// to the application window, but now subclassed. Any other messages will
// just pass through the procedure and reach the original app procedure.
// ------------------------------------------------------------------------
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
// we will need a pointer to the associated class instance
// (it was stored in the window before, remember?)
CSkin *pSkin = (CSkin*)GetProp(hWnd, "skin");
// to handle WM_PAINT
PAINTSTRUCT ps;
// if we fail to get our class instance, we can't handle anything.
if (!pSkin) return DefWindowProc(hWnd,uMessage,wParam,lParam);
switch(uMessage)
{
case WM_PAINT:
{
// ---------------------------------------------------------
// here we just need to blit our skin
// directly to the device context
// passed by the painting message.
// ---------------------------------------------------------
BeginPaint(hWnd,&ps);
// blit the skin
BitBlt(ps.hdc,0,0,pSkin->Width(),pSkin->Height(),pSkin->HDC(),0,0,SRCCOPY);
EndPaint(hWnd,&ps);
break;
}
case WM_LBUTTONDOWN:
{
// ---------------------------------------------------------
// this is a common trick for easy dragging of the window.
// this message fools windows telling that the user is
// actually dragging the application caption bar.
// ---------------------------------------------------------
SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
break;
}
}
// ---------------------------------------------------------
// call the default window procedure to keep things going.
// ---------------------------------------------------------
return CallWindowProc(pSkin->m_OldWndProc, hWnd, uMessage, wParam, lParam);
}
// toggle skin on/off for Cwnd
bool CSkin::EnableCWnd(bool bEnable)
{
// TODO: Add your implementation code here.
if (!Hooked()) return false;
// toggle
m_bEnabled = bEnable;
m_cwnd->InvalidateRect(NULL, TRUE);
//m_cwnd->Invalidate(TRUE);
//m_cwnd->UpdateWindow();
return true;
}
Here are my other files, if you think they will be relevant:
Hello.h
#pragma once
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow : public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
// This is the height of the bitmap
int iHeight;
int iWidth;
int BitmapId;
// This sets the height variable of the bitmap
void SetHeight(int height);
// This sets the width variable of the bitmap
void SetWidth(int width);
// This is sets the bitmap id for the image
void SetBitmapId(int bitmapid);
};
Hello.cpp
#include <afxwin.h>
#include "Hello.h"
#include "SkinClass/skin.h"
#include "resource.h"
CMyApp myApp;
/////////////////////////////////////////////////////////////////////////
// CMyApp member functions
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMainWindow;
CSkin ii(ID_bitmap, ID_REGION);
CMainWindow* cmw = dynamic_cast<CMainWindow*>(m_pMainWnd);
cmw->SetHeight(ii.Height());
cmw->SetWidth(ii.Width());
cmw->SetBitmapId(ID_bitmap);
ii.Hook(m_pMainWnd);
ii.Enable(true);
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_PAINT()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
Create(NULL, _T("The Hello Application"));
}
void CMainWindow::OnPaint()
{
//CPaintDC dc(this);
//CRect rect;
//GetClientRect(&rect);
//dc.DrawText(_T("Hello, MFC"), -1, &rect,
// DT_SINGLELINE | DT_CENTER | DT_VCENTER);
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CFrameWnd::OnPaint() for painting messages
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bm;
bm.LoadBitmap(BitmapId);
memDC.SelectObject(bm);
dc.BitBlt(0, 0, iWidth, iHeight, &memDC, 0, 0, SRCCOPY);
}
void CMainWindow::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
// if (nChar == VK_ESCAPE) {
// MessageBox("we have entered here");
// //std::cout << "some stuff in here";
//}
if (nChar == VK_ESCAPE) {
PostQuitMessage(0);
}
CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
// This sets the height variable of the bitmap
void CMainWindow::SetHeight(int height)
{
// TODO: Add your implementation code here.
iHeight = height;
}
// This sets the width variable of the bitmap
void CMainWindow::SetWidth(int width)
{
// TODO: Add your implementation code here.
iWidth = width;
}
// This is sets the bitmap id for the image
void CMainWindow::SetBitmapId(int bitmapid)
{
// TODO: Add your implementation code here.
BitmapId = bitmapid;
}
The error message I am getting is:
Error C2065 'SkinWndProc': undeclared identifier mfcexample2 C:\Users\samsu_kag5dbu\source\repos\mfcexample2\mfcexample2\SkinClass\skin.cpp 134
Per https://en.cppreference.com/w/cpp/language/friend :
A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at namespace scope is provided - see namespaces for details.
You are declaring SkinWndProc() for the first time in skin.h as a friend of CSkin, and then both declaring and defining the actual SkinWndProc() function only in skin.cpp after the definition of CSkin::Hook() tries to use it. As such, SkinWndProc() has not actually been declared yet in the namespace that owns CSkin, so it is not visible to Hook(), which is why you are getting an "undeclared identifier" error.
To fix this, you can either:
in skin.cpp, add a forward declaration for SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
bool CSkin::Hook(HWND hWnd)
{
// can now use SkinWndProc as needed...
}
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}
in skin.cpp, move the entire definition of SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}
bool CSkin::Hook(HWND hWnd)
{
// can now use SkinWndProc as needed...
}
in skin.h and skin.cpp, change SkinWndProc() to be a static class method instead of a standalone friend function, eg:
class CSkin
{
static LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
...
};
bool CSkin::Hook(HWND hWnd)
{
// can use CSkin::SkinWndProc as needed...
}
LRESULT CALLBACK CSkin::SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}

Have Direct2D, DXGI SwapChain, HWND transparency, all working at once in C++, without XAML or similar

Context
I have 2 big projects I'm working on: a game engine, and a file explorer replacement. My focus is Windows only. I know they're 2 huge things I'll likely never complete, I just enjoy coding during free time.
Across my many projects I started building up a collection of utilities and essentially made my own library. I'm actually enjoying writing generalized stuff.
That's the tale of how I started trying to make a generic-ish windowing framework for Windows.
A big requirement that libs like glfw and SFML are missing is modularity; my windows are made up of multiple modules, which avoids having a ginormous procedure function that handles everything, and lets me extend the window with future specific modules (I was working on a vulkan module, now i'm working on a direct2d module). Hence all the code you'll be seeing in my snippets related to HWNDs and window procecdures only handles drawing-related messages, which I hope will make navigation easier. Don't worry about the absence of all the WM_CREATE and other kind of messages unrelated to drawing in these snippets.
For the game engine all I need is "updated" direct2d drawing, so with the newer DeviceContext that goes through DXGISwapChain etcc instead of the older RenderTarget.
For the file explorer I "need" (well, ok, want) window transparency, which with the older RenderTarget "just works" (you pass it the ALPHA_PREMULTIPLIED flag and everything is nice).
I can also get a DeviceContext from the older RenderTarget as answered by Paint.NET's creator here; that gives me access to newer functions even though the context was cast from the older RenderTarget. Important: this approach gives me no control over which device that context is associated with.
For the file explorer I also wish to share resources across multiple windows, and the older RenderTarget doesn't seem to allow for that. Resources will be bound to the individual RenderTarget, while with DeviceContexts the resources should be bound to a Device and can be accessed by all the contexts associated with the same Device (at least for my understanding so far).
Q: correct me if I'm wrong about last sentence^
Where the pain begun
Here comes the dreaded issue: creating a swapchain with CreateSwapChainForHWND does not support any ALPHA_* option besides ALPHA_NONE. So if I try to draw on a window the DeviceContext and retain control of which Device creates them I need to find another way.
In short:
One window + DeviceContext newer functions + transparency -> use RenderTarget and cast to DeviceContext
Resources shared across multiple windows + DeviceContext newer functions -> use DXGISwapChain + DeviceContext
Resources shared across multiple windows + DeviceContext newer functions + transparency -> ¯\_(ツ)_/¯ + ???
The debug layer told me CreateSwapChainForComposition does support ALPHA_PREMULTIPLIED, but at that point a whole separate topic of Windows APIs seems to open up, and I've already a huge chunk of DirectX to learn, so I'd rather avoid further months-long-derailings.
But it seems there's no avoiding this issue.
Anyway, I created my CreateSwapChainForComposition swapchain and... That function takes no HWND parameter! So I assume I did create a swapchain somewhere, and direct2d calls are drawing on that swapchain, but the content of that swapchain never reaches the actual window, unless I do some magic with the compoisition APIs perhaps?
Then I started giving a look at those composition APIs and... pretty much everything assumes you're working on an higher level framework, mostly XAML, which I'm not.
The closest thing I found in the docs is this
However that example retreives a direct2d device context from the composition APIs through BeginDraw... there's no swapchain in sight.
And even worse this brings me back to the same issue I had with RenderTarget: If I've no guarantee the DeviceContexts are created from the same Device, so I cannot share resources across multiple windows.
Plus having to learn a new whole topic just to have 2 things working together that I already managed to make work individually really discouraged me.
My code, notes
Notes
d2d::*, dxgi::*, d3d::* classes are just wrappers around MS's ComPtr where I gave various classes a throwing constructor that wraps the "return HRESULT, reference actual return object" functions from MS.
the procedure_result return value of my window modules procedures exist because multiple modules can work on the same message, the behaviour is as follows:
procedure_result.next() this module didn't return any value, let next module evaluate the message.
procedure_result.stop(value) this module returned value, no other module will evaluate the message.
procedure_result.next(value) this module returned value, let next module evaluate the message.
After all modules have processed the message, or after one module called stop(), return the value returned by the last module that evaluated the current message, or pass to the default window procedure if no module returned any value.
I use whitesmiths indentation. I know it's not really widespread but please don't kill me for it :)
If you want to try my code, the project expects you have my CPP_Utils repository in the same directory as follows:
root
> CPP_Utilities_MS //the project we're discussing
> .git
> ...
> CPP_Utilities
> .git
> ...
The CPP_Utilities repository is entirely header-only because screw compile times, so no need to link .libs or add .dlls
For record-keeping reasons I'll leave links to the last commit to the date this question was asked:
CPP_Utilities_MS
CPP_Utilities
...now to the actual directly meaningful code:
How I make my window transparent
inline bool make_glass_CompositionAttribute(HWND hwnd)
{
if (HMODULE hUser = GetModuleHandleA("user32.dll"))
{
//Windows >= 10
pfnSetWindowCompositionAttribute SetWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
if (SetWindowCompositionAttribute)
{
ACCENT_POLICY accent = {ACCENT_ENABLE_BLURBEHIND, 0, 0, 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
SetWindowCompositionAttribute(hwnd, &data);
return true;
}
}
return false;
}
How I create my RenderTarget and get a DeviceContext out of it
used for (1): One window + DeviceContext newer functions + transparency
Create d2d factory
namespace d2d
{
struct factory : details::com_ptr<ID2D1Factory6>
{
using com_ptr::com_ptr;
factory() : com_ptr{[]
{
D2D1_FACTORY_OPTIONS options
{
.debugLevel{details::enable_debug_layer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE}
};
self_t ret{nullptr};
details::throw_if_failed(D2D1CreateFactory
(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
ret.address_of()
));
return ret;
}()} {}
};
}
Create RenderTarget from d2d factory and HWND
namespace d2d
{
struct hwnd_render_target : details::com_ptr<ID2D1HwndRenderTarget>
{
using com_ptr::com_ptr;
hwnd_render_target(const factory& factory, const HWND& hwnd) : com_ptr{[&factory, &hwnd]
{
self_t ret{nullptr};
D2D1_RENDER_TARGET_PROPERTIES properties
{
.type{D2D1_RENDER_TARGET_TYPE_DEFAULT},
.pixelFormat
{
.format{DXGI_FORMAT_UNKNOWN},
.alphaMode{D2D1_ALPHA_MODE_PREMULTIPLIED}
}
};
details::throw_if_failed(factory->CreateHwndRenderTarget(properties, D2D1::HwndRenderTargetProperties(hwnd), ret.address_of()));
return ret;
}()} {}
};
}
Create DeviceContext from RenderTarget
namespace d2d
{
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::hwnd_render_target& hwnd_rt) : com_ptr{create(hwnd_rt )} {}
private:
inline static self_t create(const d2d::hwnd_render_target& hwnd_rt)
{
return hwnd_rt.as<interface_type>();
}
};
}
Window module that makes use of all that
//window modules file/namespace
class render_target : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = render_target;
const d2d::factory& d2d_factory;
std::function<on_draw_signature> on_render;
//adds the WS_EX_NOREDIRECTIONBITMAP flag to the flags used to create the base window
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
render_target(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_hwnd_rt{create_info.d2d_factory, get_base().get_handle()},
d2d_device_context{d2d_hwnd_rt}
{
}
std::function<on_draw_signature> on_render;
void present() noexcept
{
}
private:
d2d::hwnd_render_target d2d_hwnd_rt;
d2d::device_context d2d_device_context;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
d2d_hwnd_rt->Resize({size.x, size.y});
}
};
How I create my DeviceContext from a DXGISwapChain
used for (2): Resources shared across multiple windows + DeviceContext newer functions, does NOT support transparency
Create d3d device
namespace d3d
{
class device : public details::com_ptr<ID3D11Device2>
{
public:
using com_ptr::com_ptr;
device() : com_ptr{create()} {}
private:
inline static self_t create()
{
details::com_ptr<ID3D11Device> base_device;
UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if constexpr (details::enable_debug_layer)
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
}
D3D_FEATURE_LEVEL feature_level_created;
std::array<D3D_DRIVER_TYPE, 3> attempts{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_SOFTWARE};
HRESULT last{S_FALSE};
for (const auto& attempt : attempts)
{
last = D3D11CreateDevice
(
nullptr, // specify null to use the default adapter
attempt,
0,
creation_flags, // optionally set debug and Direct2D compatibility flags
nullptr, // use the lastest feature level
0, // use the lastest feature level
D3D11_SDK_VERSION,
base_device.address_of(), // returns the Direct3D device created
&feature_level_created, // returns feature level of device created
nullptr
);
if (details::succeeded(last)) { break; }
}
details::throw_if_failed(last);
return base_device.as<self_t>();
}
};
}
Get dxgi device from d3d device
namespace dxgi
{
struct device : details::com_ptr<IDXGIDevice3>
{
using com_ptr::com_ptr;
device(const d3d::device& d3d_device) : com_ptr{[&d3d_device]
{
return d3d_device.as<interface_type>();
}()} {}
};
}
Create dxgi swapchain from dxgi device for HWND
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
swap_chain(const dxgi::device& dxgi_device, HWND hwnd) : com_ptr{create(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_NONE},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_IGNORE},
//.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED}, // I wish it was this easy!
.Flags {0},
};
DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fullscreen
{
.RefreshRate{.Numerator{1}, .Denominator{0}},
.Scaling {DXGI_MODE_SCALING_CENTERED},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForHwnd(dxgi_device.get(), hwnd, &desc, &desc_fullscreen, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
}
}
Create d2d device from dxgi device, and create d2d device context from d2d device
namespace d2d
{
class device : public details::com_ptr<ID2D1Device5>
{
public:
using com_ptr::com_ptr;
device(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device) : com_ptr{create(d2d_factory, dxgi_device)} {}
dxgi::device get_dxgi_device() const noexcept
{
details::com_ptr<IDXGIDevice> ret{nullptr};
details::throw_if_failed(get()->GetDxgiDevice(ret.address_of()));
return ret.as<dxgi::device>();
}
private:
inline static self_t create(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_factory->CreateDevice(dxgi_device.get(), ret.address_of()));
return ret;
}
};
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::device& d2d_device) : com_ptr{create(d2d_device)} {}
private:
inline static self_t create(const d2d::device& d2d_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, ret.address_of()));
return ret;
}
};
}
Create d2d bitmap from d2d device context and dxgi swapchain
namespace d2d
{
class bitmap : public details::com_ptr<ID2D1Bitmap1>
{
public:
using com_ptr::com_ptr;
bitmap(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain) : com_ptr{create(d2d_device_context, dxgi_swapchain)} {}
private:
inline static self_t create(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain)
{
//details::com_ptr<ID3D11Texture2D> d3d_texture_back_buffer;
//details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(d3d_texture_back_buffer.address_of())));
details::com_ptr<IDXGISurface2> dxgi_back_buffer;
details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(dxgi_back_buffer.address_of())));
D2D1_BITMAP_PROPERTIES1 properties
{
.pixelFormat{DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED},
.dpiX{1},//TODO dpi stuff
.dpiY{1},
.bitmapOptions{D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW},
};
self_t ret{nullptr};
details::throw_if_failed(d2d_device_context->CreateBitmapFromDxgiSurface(dxgi_back_buffer.get(), &properties, ret.address_of()));
return ret;
}
};
}
Window module that makes use of all that
//window modules file/namespace
class swap_chain: public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle()},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
{
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
dxgi_swapchain.present();
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
//Release things that reference the swapchain before resizing
d2d_device_context->SetTarget(nullptr);
d2d_bitmap_target.reset();
dxgi_swapchain.resize(size);
//re-get back buffer
d2d_bitmap_target = d2d::bitmap{d2d_device_context, dxgi_swapchain};
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
};
Attempt at creating a DeviceContext from a DXGISwapChain WITH transparency and somhow composition (questions here)
would be used for (3): Resources shared across multiple windows + DeviceContext newer functions + transparency
This just doesn't work. I followed the guide linked before, but as mentioned it bypasses/does not consider the manual creation of a swapchain.
I didn't even get it working since what the example is doing doesn't seem to lead me in the right direction. The following code is relevant more for my commented questions than for actual code
Create dxgi swapchain from dxgi device using the CreateSwapChainForComposition method
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
//that nullptr_t is just a temporary flag for testing purposes, to distinguish from the other constructor used in the previous snippets
swap_chain(const dxgi::device& dxgi_device, HWND hwnd, nullptr_t) : com_ptr{create_composition(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create_composition(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_STRETCH},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED},
.Flags {DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(), &desc, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
};
}
Composition wrappers, pretty straightforward
namespace composition
{
struct device : details::com_ptr<IDCompositionDevice>
{
using com_ptr::com_ptr;
device(const dxgi::device& dxgi_device) : com_ptr{[&dxgi_device]
{
self_t ret{nullptr};
details::throw_if_failed(DCompositionCreateDevice(dxgi_device.get(), __uuidof(interface_type), reinterpret_cast<void**>(ret.address_of())));
return ret;
}()} {}
};
struct target : details::com_ptr<IDCompositionTarget>
{
using com_ptr::com_ptr;
target(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateTargetForHwnd(hwnd, TRUE, ret.address_of()));
return ret;
}()} {}
};
struct visual : details::com_ptr<IDCompositionVisual>
{
using com_ptr::com_ptr;
visual(const composition::device& composition_device) : com_ptr{[&composition_device]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateVisual(ret.address_of()));
return ret;
}()} {}
};
struct surface : details::com_ptr<IDCompositionSurface>
{
using com_ptr::com_ptr;
surface(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateSurface(128, 128, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED, ret.address_of()));
return ret;
}()} {}
};
}
Final window for the composition swapchain, questions and doubts are in the comments of this snippet
//window modules file/namespace
class composition_swap_chain : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = composition_swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
composition_swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
// from here...
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle(), nullptr/*flag to create swapchain with the "ForComposition" function*/},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
// ...to here, I get the swapchain I wish to use
// from here...
composition_device{create_info.d2d_device.get_dxgi_device()},
composition_surface{composition_device, get_base().get_handle()},
// ...to here I started initializing some composition stuff
// that I can't seem to be able to "connect" to the previously
// created swapchain in any way
{
// I wish to target the bitmap from the swapchain backbuffer,
// but again this isn't connected in any way to the composition stuff
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
composition::device composition_device;
composition::surface composition_surface;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
// If BeginDraw gives me a d2dDevice context...
POINT offset{}; //?
auto comp_surf_interop{composition_surface.as<ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>()};
auto HR{comp_surf_interop->BeginDraw(nullptr, __uuidof(ID2D1DeviceContext5), reinterpret_cast<void**>(d2d_device_context.address_of()), &offset)};
if (SUCCEEDED(HR))
{
on_render(get_base(), d2d_device_context);
// ...what am I supposed to do with the swapchain?
//dxgi_swapchain.present();
details::throw_if_failed(composition_surface->EndDraw());
ValidateRect(get_base().get_handle(), NULL);
}
else
{
std::cout << details::hr_to_string(HR) << std::endl;
}
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
// So do I resize a swapchain or not?
}
};

How can I change the style of a control (CListBox) initialized with DDX_Control, before DDX_Control is called

I'm modifying an existing project and a dialog has controls I am subclassing to theme differently in some cases (in other cases I will leave it entirely alone). By the time DDX_Control() is called during DoDataExchange(), the hwnd for the ListBox already has styles applied. Specifically, at this time even if I do SetWindowLongPtr(), the LBS_OWNERDRAWFIXED does not work. By "does not work," I mean that although the style is applied, owner draw messages are not received by the CListBox.
Conversely, if I avoid the DDX_Control() and simply do a create, the ListBox does receive the messages and can be ownerdrawn. But if I do this there are now two HWND, only one of which is returned by GetDlgItem(). I believe I can make this work if necessary, but I wondered if there is a secret to intercept the HWND creation of the controls in the dialog (actually a CPropertyPage).
Below is code that doesn't work, with more commented code that "works" but isn't the way I wanted it to work, if possible.
void CMyPropertySheet::DoDataExchange(CDataExchange* pDX)
{
HWND hWndCtrl;
pDX->m_pDlgWnd->GetDlgItem(IDC_LIST1, &hWndCtrl);
if (themed) {
DWORD style = GetWindowLongPtr(hWndCtrl, GWL_STYLE) | LBS_OWNERDRAWFIXED;
SetWindowLongPtr(hWndCtrl, GWL_STYLE, style);
DDX_Control(pDX, IDC_LIST1, m_listbox);
//RECT wr;
//::GetWindowRect(hWndCtrl, &wr);
//m_listbox.Create(style, wr, this, IDC_LIST1);
} else {
DDX_Control(pDX, IDC_LIST1, m_listbox);
}
I should probably add I tried subclassing the window, but it didn't help, and CMyPropertySheet::PreSubclassWindow wasn't soon enough, either.
Some creation flags like LBS_OWNERDRAWFIXED and LBS_SORT are cached and modifying them afterwards have no effect. You have to change the template, or just make a duplicate of the listbox. Copy the style of the old listbox, then hide that listbox, change its ID, and create a new listbox based on the old one. You then have to remove DDX_Control(pDX, IDC_LIST1, m_listbox)
The example below starts with a standard list which has its sort flag set. It duplicates the listbox and disables the sort option.
For simplicity this example avoids LBS_OWNERDRAWFIXED, it uses LBS_SORT instead.
class CMyPropertyPage : public CPropertyPage
{
public:
CListBox m_listbox;
int m_listbox_index;
CMyPropertyPage(int idd) : CPropertyPage(idd)
{
m_listbox_index = 1;
}
void DoDataExchange(CDataExchange* pDX)
{
//This function is automatically called before
//CPropertyPage::OnInitDialog is complete
//On the first call, IDC_LIST1 will point to the template listbox
if(m_listbox.m_hWnd)
{
//m_listbox is ready,
//IDC_LIST1 will refer to the new listbox
DDX_LBIndex(pDX, IDC_LIST1, m_listbox_index);
}
}
BOOL OnInitDialog()
{
CPropertyPage::OnInitDialog();
CListBox* old_listbox = (CListBox*)GetDlgItem(IDC_LIST1);
if(old_listbox)
{
DWORD style = ~LBS_SORT & GetWindowLongPtr(old_listbox->m_hWnd, GWL_STYLE);
CRect rc;
old_listbox->GetWindowRect(&rc);
ScreenToClient(&rc);
old_listbox->SetDlgCtrlID(0);//change the old ID to something unused
old_listbox->ShowWindow(SW_HIDE); //hide the old listbox
m_listbox.Create(style | WS_BORDER, rc, this, IDC_LIST1);
m_listbox.SetFont(GetFont());
}
ASSERT(m_listbox.GetDlgCtrlID() == IDC_LIST1);
m_listbox.AddString(L"2");
m_listbox.AddString(L"1");
m_listbox.AddString(L"0");
UpdateData(FALSE);
return TRUE;
}
};
class CMyWinApp : public CWinApp
{
BOOL InitInstance()
{
CWinApp::InitInstance();
CPropertySheet sh;
CMyPropertyPage page(IDD_PAGE1);
sh.AddPage(&page);
sh.DoModal();
return TRUE;
}
} myapp;
OK, I'm not sure I recommend this to anyone, but I did find a way to modify the template, finally. I had to use VirtualProtect to unlock the memory of the template.
for (int i = 0; i < m_pages.GetSize(); i++) {
CPropertyPage* pPage = GetPage(i);
PROPSHEETPAGE* tpsp = &pPage->m_psp;
const DLGTEMPLATE* pTemplate;
if (tpsp->dwFlags & PSP_DLGINDIRECT) {
pTemplate = tpsp->pResource;
} else {
HRSRC hResource = ::FindResource(tpsp->hInstance, tpsp->pszTemplate, RT_DIALOG);
if (hResource == NULL) return false;
HGLOBAL hTemplate = LoadResource(tpsp->hInstance, hResource);
if (hTemplate == NULL) return false;
pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate);
if (pTemplate == NULL) return false;
}
if (afxOccManager != NULL) {
DLGITEMTEMPLATE *pItem = _AfxFindFirstDlgItem(pTemplate);
DLGITEMTEMPLATE *pNextItem;
BOOL bDialogEx = IsDialogEx(pTemplate);
int iItem, iItems = DlgTemplateItemCount(pTemplate);
for (iItem = 0; iItem < iItems; iItem++) {
pNextItem = _AfxFindNextDlgItem(pItem, bDialogEx);
DWORD dwOldProtect, tp;
if (bDialogEx) {
_DialogSplitHelper::DLGITEMTEMPLATEEX *pItemEx = (_DialogSplitHelper::DLGITEMTEMPLATEEX *)pItem;
if (pItemEx->id == IDC_LIST1) {
if (VirtualProtect(&pItemEx->style, sizeof(pItemEx->style), PAGE_READWRITE, &dwOldProtect)) {
pItemEx->style |= LBS_OWNERDRAWFIXED;
VirtualProtect(&pItemEx->style, sizeof(pItemEx->style), dwOldProtect, &tp);
}
}
} else {
if (pItem->id == IDC_LIST1) {
if (VirtualProtect(&pItem->style, sizeof(pItem->style), PAGE_READWRITE, &dwOldProtect)) {
pItem->style |= LBS_OWNERDRAWFIXED;
VirtualProtect(&pItem->style, sizeof(pItem->style), dwOldProtect, &tp);
}
}
}
pItem = pNextItem;
}
}
}
return true;

Support modal and modeless dialogs in ActiveX controls

I want to create an ActiveX control supporting modal and modeless dialogs, which can be used in multiple IE9~IE11 tab pages at the same time. It means a pop-up of dialog box by the ActiveX control within current IE tab page won't prevent us from switching to other IE tab pages and these dialogs only appear in current IE tab page. First I created an ATL Dynamic-link library (DLL) application named as AtlActiveX using ATL/C++ by Visual Studio 2010. Then add an ActiveX control named as AtlActiveXCtl, specifying Allow merging of proxy/stub code and IObjectWithSite (IE object support) options. And then add a dialog resource named as IDD_DIALOG1. If not mentioned here, other options are left as default. Since windows/dialogs need to be supported, the member variable m_bWindowOnly has to be assigned in CAtlActiveXCtl constructor. After that add a message handler for WM_CREATE message, displaying dialog boxes in OnCreate function. The full code of AtlActiveXCtl.h is attached below.
I referred to another question Display Modal Dialog Box with ActiveX on StackOverflow. Three pop-up dialogs are tested in OnCreate() function.
#pragma once
#include "resource.h"
#include <atlctl.h>
#include "AtlActiveX_i.h"
using namespace ATL;
// Modeless dialog
class CModelessDialog : public CDialogImpl<CModelessDialog>
{
public:
enum { IDD = IDD_DIALOG1 };
BEGIN_MSG_MAP(CModelessDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
END_MSG_MAP()
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL &)
{
return 0;
}
LRESULT OnClose(UINT, WPARAM, LPARAM, BOOL &)
{
DestroyWindow();
return 0;
}
};
// CAtlActiveXCtl
class ATL_NO_VTABLE CAtlActiveXCtl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IAtlActiveXCtl, &IID_IAtlActiveXCtl, &LIBID_AtlActiveXLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IOleControlImpl<CAtlActiveXCtl>,
public IOleObjectImpl<CAtlActiveXCtl>,
public IOleInPlaceActiveObjectImpl<CAtlActiveXCtl>,
public IViewObjectExImpl<CAtlActiveXCtl>,
public IOleInPlaceObjectWindowlessImpl<CAtlActiveXCtl>,
public ISupportErrorInfo,
public IObjectWithSiteImpl<CAtlActiveXCtl>,
public IQuickActivateImpl<CAtlActiveXCtl>,
public IDataObjectImpl<CAtlActiveXCtl>,
public IProvideClassInfo2Impl<&CLSID_AtlActiveXCtl, NULL, &LIBID_AtlActiveXLib>,
public CComCoClass<CAtlActiveXCtl, &CLSID_AtlActiveXCtl>,
public CComControl<CAtlActiveXCtl>
{
public:
CAtlActiveXCtl()
{
m_bWindowOnly = 1;
}
DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE |
OLEMISC_CANTLINKINSIDE |
OLEMISC_INSIDEOUT |
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST
)
DECLARE_REGISTRY_RESOURCEID(IDR_ATLACTIVEXCTL)
BEGIN_COM_MAP(CAtlActiveXCtl)
COM_INTERFACE_ENTRY(IAtlActiveXCtl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IViewObjectEx)
COM_INTERFACE_ENTRY(IViewObject2)
COM_INTERFACE_ENTRY(IViewObject)
COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceObject)
COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY(IOleControl)
COM_INTERFACE_ENTRY(IOleObject)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IQuickActivate)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()
BEGIN_PROP_MAP(CAtlActiveXCtl)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
// Example entries
// PROP_ENTRY_TYPE("Property Name", dispid, clsid, vtType)
// PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()
BEGIN_MSG_MAP(CAtlActiveXCtl)
CHAIN_MSG_MAP(CComControl<CAtlActiveXCtl>)
DEFAULT_REFLECTION_HANDLER()
MESSAGE_HANDLER(WM_CREATE, OnCreate)
END_MSG_MAP()
// Handler prototypes:
// LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
// LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
// LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)
{
static const IID* const arr[] =
{
&IID_IAtlActiveXCtl,
};
for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i], riid))
return S_OK;
}
return S_FALSE;
}
// IViewObjectEx
DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE)
// IAtlActiveXCtl
public:
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
IOleInPlaceFrame *pOleInPlaceFrame = NULL;
IOleInPlaceUIWindow *pOleInPlaceUIwindow = NULL;
IOleInPlaceSite *pOleInPlaceSite = NULL;
OLEINPLACEFRAMEINFO oleInPlaceFrameInfo;
oleInPlaceFrameInfo.cb = sizeof(oleInPlaceFrameInfo);
HRESULT hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (LPVOID *)&pOleInPlaceSite);
if (hr != S_OK)
return S_OK;
RECT rc1, rc2;
hr = pOleInPlaceSite->GetWindowContext(&pOleInPlaceFrame, &pOleInPlaceUIwindow, &rc1, &rc2, &oleInPlaceFrameInfo);
{
HWND hWndTop = NULL;
pOleInPlaceSite->GetWindow(&hWndTop);
pOleInPlaceFrame->EnableModeless(TRUE);
::EnableWindow(hWndTop, FALSE);
//[1] Message box, cannot switch tab page
MessageBox(L"Hello, world!", L"MSG", MB_OK);
//[2] Modal dialog, cannot switch tab page
CSimpleDialog<IDD_DIALOG1> dlg;
dlg.DoModal(hWndTop);
//[3] Modeless dialog, can switch tab page, but always stays on top of IE browser
CModelessDialog *pDlg = new CModelessDialog;
pDlg->Create(hWndTop);
pDlg->ShowWindow(SW_SHOWNORMAL);
pOleInPlaceFrame->EnableModeless(FALSE);
::EnableWindow(hWndTop, TRUE);
}
return 0;
}
HRESULT OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;
// Set Clip region to the rectangle specified by di.prcBounds
HRGN hRgnOld = NULL;
if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
hRgnOld = NULL;
bool bSelectOldRgn = false;
HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
if (hRgnNew != NULL)
bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
LPCTSTR pszText = _T("AtlActiveXCtl");
TextOut(di.hdcDraw,
(rc.left + rc.right) / 2,
(rc.top + rc.bottom) / 2,
pszText,
lstrlen(pszText));
if (bSelectOldRgn)
SelectClipRgn(di.hdcDraw, hRgnOld);
return S_OK;
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
};
OBJECT_ENTRY_AUTO(__uuidof(AtlActiveXCtl), CAtlActiveXCtl)
Modal and modeless dialogs need to be supported in ActiveX controls within IE11 tab pages just like in plain Win32 GUI applications. Can anyone kindly help me to solve this problem?

Access violation at destructor call from CMainFrame after end of mfc application

i'm working on a mfc application for embedded compact. At the moment i try to add a dialog which offers to close the programm. My problem is that i get an access violation at the destructor call from my CMainFrame.
To make it more clear first a piece of code.
This is the startpoint of my application:
SWinApp.h
class SWinApp : public CWinApp
{
public:
SWinApp();
~SWinApp(){};
public:
virtual BOOL InitInstance();
protected:
DECLARE_MESSAGE_MAP()
};
SWinApp.cpp
SWinApp::SWinApp():CWinApp()
{
}
BOOL SWinApp::InitInstance()
{
CRuntimeClass* pRuntimeClass = RUNTIME_CLASS( SMainFrame );
CObject* pObject = pRuntimeClass->CreateObject();
ASSERT( pObject->IsKindOf( RUNTIME_CLASS( SMainFrame ) ) );
m_pMainWnd = (SMainFrame*)pObject;
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
return FALSE; // this is the next executed line after the access violation
}
BEGIN_MESSAGE_MAP(SWinApp, CWinApp)
END_MESSAGE_MAP()
SMainFrame.h
class SMainFrame : public CFrameWnd
{
DECLARE_DYNCREATE(SMainFrame)
protected:
SMainFrame();
~SMainFrame();
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
afx_msg LRESULT OnDialogReady(WPARAM wParam, LPARAM lParam);
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
CurrentData* GetCurrentData(){return this->currentData;};
Configuration* GetConfiguration(){return this->configuration;};
private:
BOOL m_shown;
CurrentData* currentData;
Configuration* configuration;
std::map<UINT, CDialog*> views;
UINT currentID;
};
SMainFrame.cpp
IMPLEMENT_DYNCREATE(SMainFrame, CFrameWnd)
SMainFrame::SMainFrame()
{
CString appName;
appName.LoadStringW(IDS_APP_NAME);
Create(NULL, appName);
m_shown = FALSE;
this->configuration = new Configuration();
this->currentData = new CurrentData();
this->currentID = IDD_MAIN_MENU_DIALOG;
views.insert(std::make_pair(IDD_MAIN_MENU_DIALOG, new MainMenuDialog(this->configuration, this->currentData)));
// There are more dialogs but for tests one is enough
}
SMainFrame::~SMainFrame()
{
for(auto iterator:views)
{
delete iterator.second;
}
delete this->configuration;
delete this->currentData; //Is executed properly
} // After this line an access violation occurres...
BEGIN_MESSAGE_MAP(SMainFrame, CFrameWnd)
ON_MESSAGE(WM_USER_DIALOG_READY, &SMainFrame::OnDialogReady)
ON_WM_CREATE()
ON_WM_ACTIVATE()
END_MESSAGE_MAP()
// SMainFrame message handlers
int SMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
ModifyStyle(WS_CAPTION, 0, SWP_DRAWFRAME | SWP_NOZORDER );
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
afx_msg LRESULT SMainFrame::OnDialogReady(WPARAM wParam, LPARAM lParam)
{
DialogThreadParams* params = (DialogThreadParams*)lParam;
this->currentID = params->nextDialogID;
m_shown = FALSE;
this->ActivateFrame();
return 0;
}
BOOL SMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE | SWP_DRAWFRAME;
cs.style = WS_EX_CLIENTEDGE;
return TRUE;
}
void SMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
if(m_shown == FALSE)
{
m_shown = TRUE;
if(IsWindow(views[currentID]->m_hWnd))
{
views[currentID]->SetFocus();
}
else
{
views[currentID]->DoModal();
}
}
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
}
The MainMenuDialog is an inherited class from StandardDialog, which is inherited from CDialog. I will post only pieces of the code now, since this post is already too long(If you need more tell me which part can be interesting)...
The dialog which offers the possibility to close the app ist called ShutdownDialog(only inherited from CDialog) and is stored as a private variable of MainMenuDialog:
ShutdownDialog* shutdownDialog;
Created in constructor of MainMenuDialog:
this->shutdownDialog = new ShutdownDialog();
I show the dialog on a button click:
void MainMenuDialog::OnClickedShutdownButton()
{
shutdownDialog->DoModal();
}
And is deleted in destructor of MainMenuDialog:
MainMenuDialog::~MainMenuDialog()
{
delete shutdownDialog;
}
In ShutdowDialog i close the app with this piece of code:
AfxGetMainWnd()->PostMessage(WM_CLOSE);
EndDialog( 0 );
Until this everything is working fine. The application starts to destroy the objects. But after finishing the call to SMainFrame destructor i get an access violation. The program doesnt stops, just a line in Output window. It continues with statement "return FALSE;" in SWinApp InitInstance().
I know that access violation occurres when deleting an object twice or using a pointer where the depending object was already destroyed, but i cannot figure out whats going wrong here. Additional i have to say that SWinApp and SMainFrame was created by a colleague and i modified the parts with the dialogs in SMainFrame.
I thought that the m_pMainWnd could be the problem since after the destructor call to SMainFrame it must be an invalid pointer. So i tried:
SMainFrame::~SMainFrame()
{
for(auto iterator:views)
{
delete iterator.second;
}
delete this->configuration;
delete this->currentData;
AfxGetApp()->m_pMainWnd = NULL;
}
But the violation still occurres...
I searched for the Callstack Window but could not find it under the view tab...
Sry for this long post!
And sry if this is too specific...But i am struggling since severals hours and have no idea what i can try...Any help is welcome!
Change AfxGetMainWnd()->PostMessage(WM_CLOSE); to ::PostQuitMessage( 0 );