Here's what I have so far... Even though I instantiate a Window (subclass of WindowBase), I am getting the error that a pure virtual function is trying to be called. Basically, my program is trying to call WindowBase::WndProc instead of Window::WndProc.
WINDOWBASE.H
#ifndef WINDOWBASE_H_
#define WINDOWBASE_H_
#include <Windows.h>
class WindowBase {
public:
WindowBase(HINSTANCE hInstance, int nCmdShow);
~WindowBase();
void Show();
protected:
virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) = 0;
private:
static LRESULT CALLBACK WndRouter(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
HWND hWnd;
int nCmdShow;
};
#endif /* WINDOWBASE_H_ */
WINDOWBASE.CPP
#include <Windows.h>
#include "WindowBase.h"
#include <tchar.h>
WindowBase::WindowBase(HINSTANCE hInstance, int nCmdShow) {
this->nCmdShow = nCmdShow;
WNDCLASS wcex;
//wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndRouter;
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 = _T("TestMenu");
wcex.lpszClassName = _T("TestWindow");
//wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClass(&wcex)) {
MessageBox(NULL,
"Call to RegisterClassEx failed!",
"Win32 Guided Tour",
NULL);
}
hWnd = CreateWindow(_T("TestWindow"), _T("TestWindow"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, NULL, (void*)this);
if (!hWnd){
MessageBox(NULL,
"Call to CreateWindow failed!",
"Win32 Guided Tour",
NULL);
}
}
WindowBase::~WindowBase() {
}
void WindowBase::Show() {
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
}
LRESULT CALLBACK WindowBase::WndRouter(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
WindowBase* base = NULL;
if (uMsg == WM_NCCREATE) {
base = reinterpret_cast<WindowBase*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)base);
} else {
base = reinterpret_cast<WindowBase*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (!base)
return DefWindowProc(hWnd, uMsg, wParam, lParam);
return base->WndProc(hWnd, uMsg, wParam, lParam); // GETS TO HERE, BUT TRIES TO
// CALL WindowBase::WndProc, INSTEAD OF Window::WndProc
}
WINDOW.H
#ifndef WINDOW_H_
#define WINDOW_H_
#include "windowbase.h"
class Window : public WindowBase {
public:
Window(HINSTANCE hInstance, int nCmdShow);
~Window();
protected:
virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
#endif /* WINDOW_H_ */
If you call CreateWindow in your WindowBase constructor, you start to receive messages from there.
If you create your Window object, it's own constructor have to call the WindowBase constructor. During that point, the Window object is not yet came to existence, and so its virtual functions aren't yet available (they will refer a not yet constructed Window ...).
Your design have also a number of other pitfalls: Think about the scope and lifetime of every component: some of them are used before constructed, other destroyed when still needed.
It's not that easy to wrap an OOP C API (like WIn32) into another OOP language (like C++) that have a notion of "Object" and "scope" the t don't match the one WIN32 thinks about. Take care of both of them, or you can easily get cod that looks working, but used in a more wider context (more window than just one) risk to behave not as expected.
Related
I want to use GDI+ with Pascal scripting which doesn't provide GDI+ natively, but I don't know why When using dll(shared), the process will not exit even when the window is destroyed, I mean to say I can still see the process running from the task manager though it doesn't have any window to see. The process remains at idle i.e without any resource usage
In my dll, for every new hwnd i am hooking my own wndproc and on WM_Paint message i am drawing the specified objects that are so far are requested to be drawn
I am exporting DrawRectangle symbol for drawing and compiling for 32-bit
my dll is
#include <Windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include <objidl.h>
#pragma comment(lib, "Gdiplus.lib")
#include <functional>
#include <map>
#include <memory>
#include <vector>
#define DLL_EXPORT(RETURN_TYPE) \
extern "C" __declspec(dllexport) RETURN_TYPE __stdcall
void msg(const char *str) { MessageBoxA(nullptr, str, "Message", 0); }
void msg(const wchar_t *str) { MessageBoxW(nullptr, str, L"Message", 0); }
class _GdiManager {
public:
_GdiManager() {
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
}
~_GdiManager() { GdiplusShutdown(gdiplusToken); }
private:
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
} GdiManager;
class DrawableObject {
public:
virtual void draw(Gdiplus::Graphics &Graphics) = 0;
virtual ~DrawableObject() = default;
};
namespace DrawableObjects {
class Rectangle : public DrawableObject {
public:
Rectangle(ARGB Color, int X, int Y, int Width, int Height)
: m_X{X}, m_Y{Y}, m_Width{Width}, m_Height{Height}, m_Brush{Color} {}
void draw(Gdiplus::Graphics &graphics) override {
graphics.FillRectangle(&m_Brush, m_X, m_Y, m_Width, m_Height);
}
private:
int m_X, m_Y, m_Width, m_Height;
Gdiplus::SolidBrush m_Brush;
};
} // namespace DrawableObjects
LRESULT MasterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class Painter {
public:
Painter(HWND hWnd) : m_WindowHandle{hWnd}, m_Graphics{hWnd} {
m_OriginalWindowProc = (WNDPROC)GetWindowLongW(m_WindowHandle, GWL_WNDPROC);
SetWindowLongW(m_WindowHandle, GWL_WNDPROC, (LONG)MasterWindowProc);
}
LRESULT CallOriginalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
return CallWindowProcW(m_OriginalWindowProc, hwnd, uMsg, wParam, lParam);
}
LRESULT Paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_PAINT) {
for (auto &o : m_Objects)
o->draw(m_Graphics);
} else if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
}
return 0;
}
std::vector<std::unique_ptr<DrawableObject>> &Objects() { return m_Objects; }
private:
HWND m_WindowHandle;
Gdiplus::Graphics m_Graphics;
WNDPROC m_OriginalWindowProc;
std::vector<std::unique_ptr<DrawableObject>> m_Objects;
};
std::map<HWND, std::unique_ptr<Painter>> windowPaint;
LRESULT MasterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto &p = windowPaint[hwnd];
auto r = p->CallOriginalWndProc(hwnd, uMsg, wParam, lParam);
p->Paint(hwnd, uMsg, wParam, lParam);
return r;
}
auto &insertPainter(HWND hwnd) {
auto &my_painter = windowPaint[hwnd];
if (!my_painter)
my_painter = std::make_unique<Painter>(hwnd);
return my_painter;
}
DLL_EXPORT(int)
DrawRectangle(HWND hwnd, ARGB LineColor, int startX, int startY, int width,
int height) {
auto &my_painter = insertPainter(hwnd);
my_painter->Objects().push_back(std::make_unique<DrawableObjects::Rectangle>(
LineColor, startX, startY, width, height));
return 0;
}
the host program:
//#include "gdi.cpp"
#include <ObjIdl.h>
#include <Windows.h>
#include <cassert>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) {
HWND hWnd;
MSG msg;
WNDCLASS wndClass;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("GettingStarted");
RegisterClass(&wndClass);
hWnd = CreateWindow(TEXT("GettingStarted"), // window class name
TEXT("Getting Started"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
auto dll = LoadLibraryW(L"isGDI.dll");
assert(dll);
auto DrawRectangle = (int(__stdcall *)(
HWND, DWORD, int, int, int, int))GetProcAddress(dll, "DrawRectangle");
assert(DrawRectangle);
DrawRectangle(hWnd, 0xffff0000, 0, 0, 100, 100);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeLibrary(dll);
return msg.wParam;
} // WinMain
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
return DefWindowProc(hWnd, message, wParam, lParam);
} // WndProc
also, the program will work as expected if I call DrawRectangle directly through the source code (without using DLL)
i view that you use global object GdiManager inside dll. this meant that it destructor called from DLL_PROCESS_DETACH, so inside LoaderLock critical section. in destructor you call GdiplusShutdown. when GdiplusShutdown called inside LoaderLock and GDI+ use background thread ( suppressBackgroundThread = FALSE - this is your case) this is always cause deadlock:
GdiplusShutdown signal to background thread exit (set Globals::ThreadQuitEvent) and then wait for background thread exit. thread when exiting try enter to LoaderLock and hung here - because it hold by thread which call GdiplusShutdown. so main thread hung in wait for backgound thread and bacground thread hung in enter LoaderLock critical section.
we can try use suppressBackgroundThread = TRUE, but in this case need call NotificationUnhook. if do this on DLL_PROCESS_DETACH already UB (based on implementation) this can look like ok, or hung, or fail (inside this called for example DestroyWindow which is also error from dll entry, also error if process detach will be called on different thread (compare dll attach) - so window will be created inside NotificationHook on another thread)
correct solution here will be export 2 additional functions from dll , say Start and Stop, and from first call GdiplusStartup and from second GdiplusShutdown. call Start just after dll load and Stop before unload
You are not telling the current thread (the app) to exit. Use PostQuitMessage in the WndProc associated with the window:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
if (message == WM_DESTROY)
PostQuitMessage(0);
else
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
}
I have adopted the typical solution you find out there in order to use the WNDPROC as an object method, but it looks like the WM_DESTROY message is not sent to the object window's own WNDPROC and the program does not exit after closing the window.
My window class looks like this (irrelevant code removed):
class MyWindow : MyApp
{
public:
MyWindow();
void Create(void);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
private:
HWND _hWnd;
};
void MyWindow::Create()
{
// Here I register my class and call CreateWindowEx
// Everything works fine so far
// Part of the code where I assign the static WNDPROC
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MyApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = this->Instance;
wcex.hIcon = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "MyWindowClass";
wcex.hIconSm = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
RegisterClassExW(&wcex);
this->_hWnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_TOOLWINDOW,
wcex.lpszClassName,
"Window Title",
WS_POPUP,
10, 10,
600, 400,
nullptr,
nullptr,
this->Instance,
nullptr
);
ShowWindow(this->_hWnd, SW_SHOW);
UpdateWindow(this->_hWnd);
}
LRESULT CALLBACK MyWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// If I place a MessageBox here, it shows up
}
break;
case WM_DESTROY:
// It never gets to this point
// Decrease windows count
this->WindowsCount--;
PostQuitMessage(0);
break;
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
And now a class which holds the static WNDPROC, which is assigned at creation
class MyApp
{
public:
static HINSTANCE Instance;
static int WindowsCount;
MyApp();
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
};
and implementation
LRESULT CALLBACK MyApp::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Window object
MyWindow* myWindow = NULL;
if (msg == WM_CREATE) {
myWindow = reinterpret_cast<MyWindow *>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)myWindow);
}
else {
myWindow = reinterpret_cast<MyWindow *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
// If window not identified, identify now
if (myWindow) {
return myWindow->WndProc(hWnd, msg, wParam, lParam);
}
// Call window object's processor
return DefWindowProc(hWnd, msg, wParam, lParam);
}
The WM_CLOSE message is not caught either. I really do not understand why these messages are not passed on
You are setting the lpParam parameter of CreateWindowEx() to nullptr, so myWindow is always nullptr in MyApp::WndProc(), thus MyWindow::WndProc() is never called. You need to pass this instead of nullptr.
You are also not doing any error checking to make sure RegisterClassExW() and CreateWindowEx() succeed before calling ShowWindow()/UpdateWindow().
Also, consider using SetWindowSubclass() instead of (Get|Set)WindowLongPtr(GWLP_USERDATA). See Subclassing Controls on MSDN, and Raymond Chen's blog article on Safer Subclassing.
This question already has answers here:
Use class member as WNDPROC/DLGPROC with or without global
(3 answers)
Closed 7 years ago.
I am trying to work my way around the WINAPI to implement the procedure of a modeless Dialog window as member of a custom class. I am not sure it is possible, but I hoped someone knew a way. The goal is to have a procedure with access to member variables of the custom Class
I know it is possible using normal windows.
Example:
//MyClass.h
class MyClass
{
public:
bool init( ... )
static LRESULT CALLBACK redirect(HWND hWnd, UINT msg,
LPARAM lParam, WPARAM wParam);
LRESULT myWndProc(HWND hWnd, UINT msg,
LPARAM lParam, WPARAM wParam);
private:
HWND m_MainHwnd;
}
By defining a static member function that redirects the pointer to the nonstatic actual procedure handling, the procedure can be a member function:
//MyClass.cpp
MyClass::init( ... )
{
//Create the window class for the main Window//
m_windowed = windowed;
WNDCLASSEX wc; //Create a new extended windows class
wc.cbSize = sizeof(WNDCLASSEX); //Size of our windows class
wc.style = CS_HREDRAW | CS_VREDRAW; //class styles
wc.lpfnWndProc = MyClass::redirect; //Default windows procedure function
wc.cbClsExtra = NULL; //Extra bytes after our wc structure
wc.cbWndExtra = NULL; //Extra bytes after our windows instance
wc.hInstance = hInstance; //Instance to current application
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //Title bar Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //Default mouse Icon
wc.hbrBackground = CreateSolidBrush(0xFFFFFF); //Window bg color
wc.lpszMenuName = NULL; //Name of the menu attached to our window
wc.lpszClassName = m_windowName; //Name of our windows class
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO); //Icon in your taskbar
if (!RegisterClassEx(&wc)) //Register our windows class
{
//if registration failed, display error
MessageBox(NULL, "Error registering Main Window class",
"Error", MB_OK | MB_ICONERROR);
return false;
}
m_MainHwnd = CreateWindowEx( //Create our Extended Window
NULL, //Extended style
m_windowName, //Name of our windows class
m_windowName, //Name in the title bar of our window
WS_OVERLAPPEDWINDOW | WS_VISIBLE, //style of our window | Make it visible on showWindow cmd
30, 30, //Top left corner of window
width, //Width of our window
height, //Height of our window
NULL, //Handle to parent window
NULL, //Handle to a Menu
hInstance, //Specifies instance of current program
this //used for an MDI client window
);
}
See the line in the setup in WNDCLASSEX: wc.lpfnWndProc = MyClass::redirect;? This is valid because the the following implementation of the redirect static function:
MyClass::redirect(HWND hwnd, UINT msg, LPARAM lParam, WPARAM wParam)
{
if (msg == WM_CREATE) SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
MyClass * pObject = (MyClass*)((LONG_PTR)GetWindowLongPtr(hwnd, GWL_USERDATA));
if (pObject) return pObject->MyWndProc(hwnd, msg, wParam, lParam);
//return the message for windows to handle it
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
};
This lets you implement the MyWndProc ( ... ) exactly like any other member method.
Now! I really want to be able to do the same thing to my Modeless Dialog box - because it's rather important that it knows the member variables of "MyClass". Also - I like that I can design it and redesign it as I like ( If I have to use normal windows - It'd be tedious to design it all in code. )
Is it possible?
Thank you in advance!
Yes it's possible to use this method with a dialog box, in more or less the same way.
Instead of using WM_CREATE to store the this pointer you use WM_INITDIALOG. Note that the user data is passed to WM_INITDIALOG in lParam itself - there are no structures to dereference like there is with WM_CREATE.
You can store the pointer in DWLP_USER which is a pointer-size window data slot available to the owner of the dialog box.
The final difference is simply return FALSE if the data pointer hasn't been assigned yet - you don't call DefWindowProc from a dialog procedure.
class MyClass
{
public:
static INT_PTR CALLBACK dlgRedirect(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT myDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
}
//...
HWND hwndDlg = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwndParent,
MyClass::dlgRedirect, reinterpret_cast<LPARAM>(this));
//...
INT_PTR CALLBACK MyClass::dlgRedirect(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
MyClass* pThis = reinterpret_class<MyClass*>(GetWindowLongPtr(hWnd, DWLP_USER));
if (pThis) return pThis->myDlgProc(hWnd, uMsg, wParam, lParam);
return FALSE;
}
INT_PTR MyClass::myDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// real dialog procedure here
}
Here is my header
#pragma once
#ifndef BASE_H
#define BASE_H
#include <Windows.h>
#include <windowsx.h>
class Base
{
HWND hWnd;
WNDCLASSEX WndCls;
HRESULT Hr;
public:
Base();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RegisterWnd(HINSTANCE hInstance);
void ShowWnd(int nCmdShow);
~Base();
};
#endif
Here is my base.cpp
#include "Base.h"
Base::Base()
{
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
void Base::RegisterWnd(HINSTANCE hInstance)
{
ZeroMemory(&WndCls, sizeof(WNDCLASSEX));
WndCls.cbSize = sizeof(WNDCLASSEX);
WndCls.hbrBackground = (HBRUSH)COLOR_WINDOW;
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hIcon = LoadIcon(hInstance, NULL);
WndCls.hIconSm = LoadIcon(hInstance, NULL);
WndCls.hInstance = hInstance;
WndCls.lpfnWndProc = WndProc;
WndCls.lpszClassName = "ClsName";
WndCls.style = CS_HREDRAW | CS_VREDRAW;
Hr = RegisterClassEx(&WndCls);
if (FAILED(Hr))
MessageBox(NULL, "Window Class failed to register.", "ERROR", MB_OK);
hWnd = CreateWindowEx(
NULL,
"WndClassName",
"WndName",
WS_OVERLAPPEDWINDOW,
100, 100,
480, 640,
NULL,
NULL,
hInstance,
NULL);
if (FAILED(hWnd))
MessageBox(NULL, "Window Class failed to create", "ERROR", MB_OK);
}
void Base::ShowWnd(int nCmdShow)
{
Hr = ShowWindow(hWnd, nCmdShow);
if (FAILED(Hr))
MessageBox(NULL, "Failed to display Window", "ERROR", MB_OK);
}
Base::~Base()
{
}
And here is my main.cpp
#include "Base.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Base CreateWnd;
CreateWnd.RegisterWnd(hInstance);
CreateWnd.ShowWnd(nCmdShow);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&Msg);
// send the message to the WindowProc function
DispatchMessage(&Msg);
}
// return this part of the WM_QUIT message to Windows
return Msg.wParam;
}
The problem is, I keep getting this error message that I dont understand of. Sorry for the bad explanation..Still a student in programming...
UPDATED :
The error above has been corrected by replacing
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
with
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Thanks to ravi and IInspectable for the quick help.
Now I am having another error D: When i clicked on debug, everything run perfectly but nothing shows up. No window is showing. Visual studio is running perfectly as "Ready". (Sorry i do not want to make another new question because it's still related to creating window in oo
SECOND UPDATE :
My class name in CreateWindowEx is different from the RegisterWnd..My bad. Thanks to IInspectable again for the help.
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
You have to define this with class scope OR how compiler know if its global static OR static member of class. So it should be
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
I did my research but couldn't find an answer. The closest thing I found is "Cannot create window", but it didn't help me. So, here it is!
Basic info
I have a static library and an application using a static library. I hooked up the application to the static library correctly (Include directories, library directories, actual library dependencies, etc). In the static library i have 1 file: IWindow.h. In the application I have 3 files: Main.cpp, WindowMain.h and WindowMain.cpp. IWindow.h defines an abstract window class:
#ifndef IWINDOW_H
#define IWINDOW_H
#include <Windows.h>
namespace NamespaceName
{
template<class T>
class IWindow
{
public:
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual ~IWindow(){}
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
virtual VOID paint(HDC hDC) = 0;
VOID create(HINSTANCE hI, LPCWSTR title, LPCWSTR className, DWORD dwStyle, DWORD dwExStyle = 0, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT, HWND hWndParent = 0, HMENU hMenu = 0);
HWND getHWND();
BOOL isCreated();
protected:
HWND m_hWnd;
BOOL created;
};
template<class T>
LRESULT CALLBACK IWindow<T>::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
T* pThis = NULL;
if(uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (T*)pCreate->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
}
else
{
pThis = (T*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if(pThis)
{
return pThis->handleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
template<class T>
VOID IWindow<T>::create(HINSTANCE hI, LPCWSTR title, LPCWSTR className, DWORD dwStyle, DWORD dwExStyle = 0, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT, HWND hWndParent = 0, HMENU hMenu = 0)
{
WNDCLASS windowClass = {0};
windowClass.hInstance = hI;
windowClass.lpszClassName = className;
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.lpfnWndProc = IWindow::windowProc;
RegisterClass(&windowClass);
m_hWnd = CreateWindowEx(dwExStyle, className, title, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this);
created = (m_hWnd ? TRUE : FALSE);
}
template<class T>
HWND IWindow<T>::getHWND()
{
return m_hWnd;
}
template<class T>
BOOL IWindow<T>::isCreated()
{
return created;
}
}
#endif
Then,
WindowMain.h defines a subclass of IWindow.h
Code:
#ifndef WINDOWMAIN_H
#define WINDOWMAIN_H
#include <FolderName\Video\GUI\IWindow.h>
class WindowMain : public NamespaceName::IWindow<WindowMain>
{
public:
~WindowMain(){}
LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID paint(HDC hDC);
};
#endif
And,
it's accompanying .cpp file
completes it:
#include "WindowMain.h"
LRESULT WindowMain::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
hDC = BeginPaint(m_hWnd, &ps);
FillRect(hDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
paint(hDC);
EndPaint(m_hWnd, &ps);
}
return 0;
}
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
VOID WindowMain::paint(HDC hDC)
{
}
And finally,
the Main.cpp
Code:
#include <Windows.h>
#include <tchar.h>
#include <GdiPlus.h>
#include "WindowMain.h"
#pragma comment(lib, "Gdiplus.lib")
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WindowMain window;
MSG msg;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
window.create(hInstance, L"Test Window", L"Test Window Class", WS_OVERLAPPEDWINDOW);
if(!window.isCreated())
return 1;
ShowWindow(window.getHWND(), nCmdShow);
UpdateWindow(window.getHWND());
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
The window.isCreated() always returns false, thus making the if statement in the Main.cpp invert it to true and always returning 1 and exiting the application. If I omit the if statement from Main.cpp the window does not show up and the application goes on forever, until i force-stop it in the IDE.
Additional questions (answer in comments if you want, these are optional and do not relate to the former question in any way)
I don't like Visual Studio at all. I prefer using NetBeans so much more. I tried to use it for Windows programming but I failed. Do I have to use Visual Studio for Windows programming? Does it have some magical compiler that can compile Win32 programs in a special way? Or am I just doing something wrong?
What are these Gdiplus tokens and startup input? A link to somewhere that explains it would be awesome.
Finally
Thank you for taking your time to read this, and potentially trying to help me. If you need any more information I'll be happy to provide it. If the question was poorly constructed please let me know how to improve it and I will. ;)
Edit #1
Found this: "Win32 API window won't open", just for the record, it didn't help my case either.
Edit #2
In IWindow.h, when I was creating a WNDCLASS, for the window process, I tried to use T::windowProc instead of IWindow::windowProc, but it didn't help either.
Edit #3
Found "WinAPI window doesn't appear", but didn't help either.
Edit #4
"Try setting windowClass.cbWndExtra to sizeof(LONG_PTR) before registering the class." -suggestion from the comments. Tried it too, and also didn't help.
Edit #5
Tried replacing WNDCLASS with WNDCLASSEX and RegisterClass with RegisterClassEx and added windowClassEx.cbSize = sizeof(WNDCLASSEX) (I changed the variable name from windowClass to windowClassEx too), but didn't help either...
I see at least two errors.
First, you register the class with
windowClass.hInstance = hI;
but when you create the window, you pass a different HINSTANCE:
m_hWnd = CreateWindowEx(dwExStyle, className, title, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this);
// ^^^^^^^^^^^^^^^^^^^^^
The HINSTANCE parameters must match in order for the class to be found. You should use hI in both places.
Second, your code uses the m_hWnd member before it is initialized.
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
When the window receives the WM_NCCREATE message, m_hWnd has not yet been initialized. It doesn't get initialized until the CreateWindowEx returns. You need to get the correct window handle to DefWindowProc. One way is to pass the hWnd parameter from windowproc through to handlemessage. Another is to add
m_hWnd = hWnd;
in your if(uMsg == WM_NCCREATE).
Notice that your code assumes that if GWLP_USERDATA is nonzero, then m_hWnd is valid. However, you did nothing to actually make this assumption valid. Between the receipt of the WM_NCCREATE message and the completion of CreateWindowEx, you have a nonzero GWLP_USREDATA but m_hWnd is not initialized.
The way to debug this is to set a breakpoint on your window procedure and step through it. When stepping through the handling of the WM_NCCREATE message, you should have noticed that m_hWnd is not initialized.
There is a third bug in this code, which you would eventually stumble across once you get the window created: You never set m_hWnd back to NULL when the window is destroyed.