I'm calling a Dialog that has it's resources and dlg-procedure in a DLL.
I'm not using a DEF file or LIB file. The function names are known, the function args are known, I'm using GetProcAddress to get a pointer to the functions I'm interested in.
'Why' I'm doing it this way is of no consequence, it's an experiment in 'learning'.
This works on _cdecl functions, but on the CALLBACK (_stdcall), I can't get a pointer to the actual dialog procedure (it returns 0).
Here's how I'm doing my pointers:
//////////////////// DLL TEST STUFF ////////////////////////////
#define DLLPATH "../../testdll/release/testdll.dll"
//typedef some function pointers
typedef HINSTANCE (__cdecl *INSTPROC )(void);
typedef int (__cdecl *VOIDPROC )(void);
typedef LRESULT (__stdcall *DLGROC )(HWND, UINT, WPARAM, LPARAM );
///////////////////////////////////////////////////////////////
As I said, any function that is NOT a callback returns a valid result, additionally, the dialog pops up as expected, no problems with DLL's HINSTANCE.
But without a pointer to it's dlgproc, no button messages have anywhere to go.
Here's some more test code:
//use DLL funcs without lib or def files
//this works
GetInst = (INSTPROC)GetProcAddress(Loadme,"getinst");
//this works
GetID = (VOIDPROC)GetProcAddress(Loadme,"getid");
//this doesn't work, rets 0
DlgProc = (DLGPROC) GetProcAddress(Loadme,"dllProc");
//test for result
dllid =(GetID)();
dllinst=(GetInst)();
//compare hinst OK
wsprintf(buf,"dllinst=%x Loadme=%x",dllinst, Loadme);
MessageBox(hwnd,buf,"",MB_OK);
//check resOurce ID OK
wsprintf(buf,"GetID returned: %d",dllid);
MessageBox(hwnd,buf,"",MB_OK);
//check dllProc addr NOGO, ret=0
wsprintf(buf,"dllProc=%x",DlgProc);
MessageBox(hwnd,buf,"",MB_OK);
// DLL instance, resource ID, parent, dlgproc
DialogBox(Loadme , MAKEINTRESOURCE(dllid), hwnd, DlgProc);
//dialog loads and shows, can't get dlgproc addr
FreeLibrary(Loadme);
///////////////// END DLL TEST STUFF ///////////////
On the DLL side of things, it looks like this:
__declspec(dllexport) LRESULT CALLBACK dllProc(
HWND hwnd,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
hpwnd=hwnd;
switch (Message){
case WM_INITDIALOG:
MessageBox(hwnd,"At DlgProc...","",MB_OK);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam)){
case IDEND:
case IDCANEND:
EndDialog(hwnd,0);
return TRUE;
}
return TRUE;
}
return FALSE;
}
Which really doesn't matter at this point, since I can't get a pointer to the callback in the first place.
If anyone happens to know the fix for getting a pointer to the callback, I would very much appreciate your input.
Thanks for your patience.
You need to declare your dllProc inside an extern "C" block to prevent its name being decorated when exported, so that when you ask for a function called "dllProc" such a function is found (instead of "dllProc#blahmoocow" or whatever madness the C++ decoration produces. :-)).
Place this declaration before the dllProc definition:
extern "C"
{
__declspec(dllexport) LRESULT CALLBACK dllProc(
HWND hwnd, UINT Message, WPARAM wParam, PARAM lParam);
}
You can also then drop the __declspec(dllexport) from the dllProc definition.
Related
I've been looking at creating a custom control with WinApi for my application, and I have made a class which contains the CustomDialogProc and CreateWindowEx and RegisterClass() functions.
I can set a breakpoint inside the CustomDialogProc and it hits, so the class is registered correctly.
However, I have to declare the CustomDialogProc function as static int he header of my class
static LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam);
If I don't set it to static, I get the error
Error C3867 'CustomControl::CustomDialogProc': non-standard syntax; use '&' to create a pointer to member
IS this necessary, this requires all my controls created within this control to be static as well. What if I want multiple instances of this control?
How can I get around this? The main MsgProc doesn't seem to be a static function. Neither is the UpDownDialogProc in the first link shown below
Below is my code for CustomControl.h in case anyone needs it.
Put together from code found at:
https://msdn.microsoft.com/en-us/library/windows/desktop/hh298353(v=vs.85).aspx
https://www.codeproject.com/Articles/485767/True-Windows-control-subclassing
Thanks,
#pragma once
#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
class CustomControl
{
public:
CustomControl();
~CustomControl();
LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
//DO STUFF HERE
break;
}
}
bool CreateControl(HWND hwnd, HINSTANCE* m_hApp_instance)
{
g_hInst = m_hApp_instance;
RegisterSubClass(*g_hInst, WC_LISTBOX, TEXT("CustomControl"), CustomDialogProc);
HWND hwndCustom = CreateWindow(TEXT("CustomControl"), NULL, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU)100, *g_hInst, NULL);
return true;
}
private:
HINSTANCE* g_hInst;
WNDPROC RegisterSubClass(HINSTANCE hInstance, LPCTSTR ParentClass, LPCTSTR ChildClassName, WNDPROC ChildProc) {
WNDCLASSEX twoWayStruct;
WNDPROC parentWndProc;
if (GetClassInfoEx(NULL, ParentClass, &twoWayStruct)) {
parentWndProc = twoWayStruct.lpfnWndProc; // save the original message handler
twoWayStruct.cbSize = sizeof(WNDCLASSEX); // does not always get filled properly
twoWayStruct.hInstance = hInstance;
twoWayStruct.lpszClassName = ChildClassName;
twoWayStruct.lpfnWndProc = ChildProc;
/* Register the window class, and if it fails return 0 */
if (RegisterClassEx(&twoWayStruct))
return parentWndProc; // returns the parent class WndProc pointer;
// subclass MUST call it instead of DefWindowProc();
// if you do not save it, this function is wasted
}
return 0;
}
};
The most common way is to use SetWindowLongPtr to store a pointer to the object associated with the window handle.
HWND hWnd = CreateWindow(...);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) this);
And then in your dialog proc, get that pointer and call into your class:
// this static method is registered with your window class
static LRESULT CALLBACK CustomDialogProcStatic(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
auto pThis = (CustomControl*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (pThis != NULL)
return pThis->CustomDialogProcInstance(hWnd, uMsg, wParam, lParam);
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// this instance method is called by the static method
LRESULT CustomDialogProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
}
Make sure you manage your window and class life cycle appropriately to prevent the window proc from calling a deleted object instance. In many cases, this is as simple as ensuring DestroyWindow is called if your class is destructed.
The Windows API is C language based. It knows nothing about C++, non-static member functions, objects, etc.
So yes, all of your functions that will communicate with the Windows API directly must be static class member functions, or non-class / global / "free" functions.
That doesn't preclude you from writing a C++ wrapper for a C API. That's what libraries such as Microsoft's MFC or the old Borland OWL libraries accomplish. Other independent groups have also written wrappers for the Windows API.
Note that these differing libraries accomplish the goal of hooking a C based API to C++ in different ways. One is to use the SetWindowLongPtr method mentioned in the answer given by #MichaelGunter. Another method is to use maps to associate window handles and static Window procedures.
I would suggest before you try this on your own (creating a wrapper), you investigate how others have done this already, and choose the best approach that fits. Another suggestion is that before you even create a wrapper, you should know the C based API on much more than a cursory level. You need advanced to expert knowledge of any C API you plan to create a C++ wrapper for if you want the wrapper to work relatively flawless under different scenarios.
I think I've fallen in the same trap as many before me where I try to impose a nice OO methodology on win32 API programming. No MFC, no AFX, I'm not even using VC++, I'm using C::B with gcc.
I think what I'm trying to do is impossible, but since MFC exists (although I'm not using it) there must be some way.
I've created a class to contain several window controls. It implements handlers for WM_CREATE and WM_COMMAND, and keeps track of all the associated data around my small group of controls (ID codes and HWNDs).
It works great for buttons, static controls, even light GDI methods, but it all breaks down when I try to subclass an edit control.
Really, I just want to capture the "enter" key, but as anybody who's been down that road before will attest, when an edit control has focus, the parent window doesn't receive WM_KEYDOWN or WM_COMMAND, we are left to implement our own proc. Super lame.
OK, so subclassing an edit control is fine, if the editProc is global or static. I know that is because SetWindowLongPtr needs a function address, and that concept is nebulous for a member function.
So the object of my class is declared as "static" inside the parent WndProc. But the function is not "static" because then I wouldn't have access to non-static data members (completely defeating the purpose of this exercise). I'm hoping that because the objest is itself static, I should be able to properly define the address of one of its member functions.
Readers that have tried this before will either have given up and used MFC or something else, or perhaps have found a clever work-around.
I'll let this sample code do the rest of the talking: (simplified - will not compile as such)
/**** myprogram.c ****/
#include "MyControlGroup.h"
int winMain(){ // etc... }
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// object is static becuse it only needs to be initialized once
static MyControlGroup myControl;
if (msg == WM_CREATE)
myControl.onWMCreate(hWnd);
else if (msg == WM_COMMAND)
myControl.onWMCommand( wParam, lParam );
else if (msg == WM_DESTROY)
PostQuitMessage(0);
return DefWindowProcW(l_hWnd, l_msg, l_wParam, l_lParam);
}
The header file for my class:
/**** MyControlGroup.h ****/
class MyControlGroup
{
private:
HWND m_hWndParent;
HWND m_hWndEditBox;
int m_editBoxID;
public:
MyControlGroup();
void onWMCreate(HWND);
void onWMCommand(WPARAM, LPARAM);
// want to find a way to pass the address of this function to SetWindowLongPtr
LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
...and the implementation:
/**** MyControlGroup.cpp ****/
static int staticID = 1;
MyControlGroup::MyControlGroup()
{
m_editBoxID = staticID++;
}
void MyControlGroup::onWMCreate(HWND hWnd)
{
// My control group has buttons, static controls, and other stuff which are created here with CreateWindowW. It also has an edit control:
m_hWndEditBox = CreateWindowW(L"EDIT", L"initial text", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 150, 20, hWnd, (HMENU)m_editBoxID, NULL, NULL);
/*
To subclass the edit control, I need a pointer to my customized proc. That means I
need a pointer-to-member-function, but SetWindowLongPtr needs a pointer to global or
static function (__stdcall or CALLBACK, but not __thiscall).
*/
// I'd like to do something like this, adapted from a great write-up at
// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
LERSULT (MyControlGroup::*myEditProcPtr)(HWND, UINT, WPARAM, LPARAM);
myEditProcPtr = &MyControlGroup::myEditProc;
// Up to now it compiles ok, but then when I try to pass it to SetWindowLongPtr, I get
// an "invalid cast" error. Any ideas?
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProcPtr);
}
void MyControlGroup::onWMCommand(WPARAM wParam, LPARAM lParam){ /* process parent window messages. Editboxes don't generate WM_COMMAND or WM_KEYDOWN in the parent :''( */}
LRESULT MyControlGroup::myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// process messages like IDOK, WM_KEYDOWN and so on in the edit control
}
Even once I get this done, I'll still need to figure out a way to pass the address of the parent WndProc to myEditProc for the return value, but until I get past this there is no point in worrying about that.
Thanks in advance for reading!
myEditProc needs to be a static function.
Once you've done that you can pass the address of the function directly without going through the intermediate variable:
static LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
...
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProc);
To access your class data from the static function, you can save it in the userdata field of the edit control, e.g.:
// before sub-classing the control
SetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA, (LPARAM)this);
// in the sub-class procedure
MyControlGroup* pThis = (MyControlGroup*)GetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA);
But as #K-ballo suggested, SetWindowSubclass is definitely the way to do this unless you want compatibility with pre-XP. It handles the sub-classing procedure for you automatically, lets you associate a userdata pointer (e.g. this) that is automatically passed to the sub-class procedure, and safely handles removing the sub-class at the end.
i have this abstract code :
i want to use lParam (last parameter) in CreateWindowEx() to save a pointer to a class thats declared in the begining of main - SaveArr. then, i want to use it in the function WndProc.
in the begining i did a global array, and then i could use it anywhere, but its not so "clever" as far as c++ concern, so im trying to upgrade it a bit.
class Samples
{
int arr[ITERATIONS+1];
int index;
...
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
Samples * SaveArr;
...
hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
ClsName,
WindowCaption,
WS_OVERLAPPEDWINDOW,
INITIAL_WIN_LOCAT_X,
INITIAL_WIN_LOCAT_Y,
WIN_WIDTH,
WIN_HIGHT,
NULL,
NULL,
hInstance,
NULL); //here i want to pass SaveArr, so that i can use it in the WndProc(...) function
...
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
... //here i would like to use lParam as the class pointer, meaning using the
SaveArr declared in the main function.
}
}
Adding caller information to the window:
m_window = CreateWindow(..., this);
Similar for the extended CreateWindowEx.
Obtaining a pointer to the caller:
template< typename CallerT >
[[nodiscard]]
CallerT* WindowCaller(HWND window, UINT message, LPARAM lParam) noexcept
{
if (message == WM_NCCREATE) [[unlikely]]
{
const auto caller = reinterpret_cast< CallerT* >(
reinterpret_cast< CREATESTRUCT* >(lParam)->lpCreateParams);
// Change the user data of the window for subsequent messages.
::SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast< LONG_PTR >(caller));
return caller;
}
else
{
// Retrieve the user data of the window.
return reinterpret_cast< CallerT* >(
::GetWindowLongPtr(window, GWLP_USERDATA));
}
}
This method needs to be called in your message callback.
Best way would be
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Samples *savearr = (Samples*)GetWindowLong(hWnd,GWL_USERDATA)
switch(Msg)
{
case WM_CREATE:
SetWindowLong(hWnd, GWL_USERDATA, (LONG)lParam);
break;
}
}
The next time the WndProc is called the value would be in savearr, and can be used.
From the reference:
lpParam [in, optional]
Type: LPVOID
Pointer to a value to be passed to the window through the
CREATESTRUCT structure (lpCreateParams member) pointed to by the
lParam param of the WM_CREATE message. This message is sent to the
created window by this function before it returns.
If an application calls CreateWindow to create a MDI client
window, lpParam should point to a CLIENTCREATESTRUCT structure. If an
MDI client window calls CreateWindow to create an MDI child window,
lpParam should point to a MDICREATESTRUCT structure. lpParam may be
NULL if no additional data is needed.
You're expecting the lParam to be always passed to WndProc, but it is only passed with WM_CREATE.
Note, that even then it's not passed directly, but rather through a structure which is the actual lParam for WM_CREATE.
Your only chance to read the lParam is during WM_CREATE. If you want to keep using the value later then you must store it somewhere. Maybe as a static of the WndProc or otherwise assign it to something else which is going to be scoped.
Why all the insistence on using that last, lpParam value set to X, then catching it on WM_CREATE (through all that indirect struct stuff, no less!) and then setting GWL_USERDATA?!
Why not cut to the chase and do this:
HWND H=CreateWindow(.....)
SetWindowLong(H,GWL_USERDATA,X)
In other words, just put X there directly, yourself, right after the window creation statement.
In my tests it works, and so long as you test the window handle against some list of known handles, you can prevent some errant message picked up by your program, and prevent inappropriate use of something else's userdata.
So I'm writing some wrapper classes for GUI programming in Win32. I'm starting with a Window class, and so far it contains a MainLoop method that is basically a clone of the standard Win32 WinMain function. That way, one can do something like this:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) {
Window *win = new Window();
// Do all your widget creation and add it to the window object...
return win->MainLoop(hInst, hPrev, szCmdLine, nCmdShow);
}
Inside the window object's MainLoop method, it must create the new Win32 window by setting its lpfnWndProc member. This member, as any Win32 programmer knows, is a function pointer to a specifically defined WndProc function. The problem is, if I were to create a WndProc function, I would need access to that window object's members (so that it knew what to draw on the window, etc.). This leaves me two options (that I know of):
I can define WndProc at the top level, but that cuts off access to the object's members.
I can define it as a class method, but then it's not the exact function type that lpfnWndProc asks for, so I can't set it!
Can anyone help me unravel this catch-22?
You could also make it a static member function. :)
Anyways, a solution depends on if you need only one window or if you need multiple windows.
First a solution for single windows:
// in .h
class Window{
public:
static LRESULT WINAPI MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT InternalMessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// ...
};
// in .cpp
#include "Window.h"
Window* global_window = 0;
Window::Window(/*...*/){
if(!global_window)
global_window = this;
else
// error or exception... or something else
}
LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
return global_window->InternalMessageProc(hWnd, msg, wParam, lParam);
}
Now if you want to allow multiple windows, use a std::map (or if your compiler supports std::unordered_map).
Edit: This solution comes with some subtle problems. As #Ben Voigt points out in his comment, you get a chicken and egg problem as the MessageProc is called inside of CreateWindow(Ex), but only after the CreateWindow(Ex) call you have the window handle. Here's a solution based on Ben's next comment (thanks!):
// Window.h stays the same
// in .cpp
#include "Window.h"
#include <map>
std::map<HWND, Window*> window_map;
Window* currently_created_window = 0;
Window::Window(){
currently_created_window = this;
window_handle = CreateWindow(/*...*/);
}
LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
// if the key 'hWnd' doesn't exist yet in the map
// a new key-value pair gets created and the value gets value-initialized
// which, in case of a pointer, is 0
if(window_map[hWnd] == 0){
// window doesn't exist yet in the map, add it
window_map[hWnd] = currently_created_window;
}
window_map[hWnd]->InternalMessageProc(hWnd, msg, wParam, lParam);
}
Be cautious though, as the above example isn't thread-safe. You need to mutex-lock the creation of the window:
Window::Window(/*...*/){
Lock lock_it(your_mutex);
currently_created_window = this;
window_handle = CreateWindow(/*...*/);
lock_it.release();
// rest of the initialization
}
The above should do for the thread-safety (I hope).
You need to create window map and when you create new window just add it to this global map. You can use simple linked list instead of course.
map<HWND, Window *> wndmap;
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
Window *pWnd = wndmap [hwnd];
....
}
WndProc cannot be an instance member function, because Windows will not pass any hidden this parameter. It can be namespace scope or a static member.
One simple solution is to use a map<HWND, Window*> to find the object, and then forward parameters to a method on the object.
Note that WndProc can maintain the map itself, since CreateWindow provides an opaque user parameter that shows up in WM_CREATE which is useful for carrying the Window *, and then you remove the entry in WM_DESTROY.
Define your WndProc as a static class member - this will then be compatible (for all compilers I'm aware of) with non-member function pointer, such as those used in Win32 programming.
But I have to say that this is a bit of a waste of time - there are a zillion Windows class libraries out there, and I don't think the world really needs another one.
Sounds you need to declare the function without defining it. That's what prototypes are for.
class Object;
void f(Object* o);
class Object {
public:
...
void some_method() {
... &f ...
}
void another_method() {
...
}
...
};
void f(Object* o) {
...
o->another_method();
...
}
The other way around might also be possible.
class Object {
public:
...
void some_method();
void another_method();
...
};
void f(Object* o) {
...
o->another_method();
...
}
void Object::some_method() {
... &f ...
}
void Object::another_method() {
...
}
I am learning Windows programming in C++. I created my first Windows, but there's one thing I don't really understand: WNDPROC in WNDCLASS. The structure was documented like this:
typedef struct tagWNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
Now in order to assign to lpfnWndProc, I must have a callback function WindowProc like this:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
And then I have to assign lfpnWndProc like this:
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
As I understand, WindowProc is a function. If I assign like this, it means I am assigning to a function pointer in WNDCLASS. But in the WNDCLASS definition, nothing indicates it is a function pointer. Further more, it looks like a data type to me rather than a function pointer.
Normally I would do get a function pointer like this to pass in as a parameter or used as a variable:
#include <stdio.h>
void my_int_func(int x)
{
printf("%d\n",x);
}
int main(void)
{
void (*foo) (int);
foo = &my_int_func;
foo(2);
(*foo)(2);
}
But the way I have to assign WindowProc just does not make sense to me. Can someone help me understand this?
WNDPROC is a function pointer type. The definition is:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
Functions, sort of like arrays, automatically decay into pointers in certain contexts. The & in your example program is optional.
MSDN says,
lpfnWndProc
Type: WNDPROC
A pointer to the window procedure. You must use the CallWindowProc function to call the window procedure.
WinUser.h defines it as,
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);