What I am trying to do, is to create a edit control and procedure within a class. I have tried various things, and tried using parts of a similar question I asked: Win32 C++ Create a Window and Procedure Within a Class.
At the moment, I took them apart.
main_class.h
class MainClass {
private:
HWND hwndMain; // main windows handle
HINSTANCE hInstanceMain; // main windows instance
HWND hTextarea;
public:
bool init(HWND _hwnd, HINSTANCE _hInstance);
bool ShowInfoTextarea();
};
main_class.cpp
// Heres the question
bool MainClass::ShowInfoTextarea() {
if (hTextarea != NULL) return true; // if it is not null, the textarea is likely already displayed.
// Creating the EDIT textarea
hTextarea = CreateWindowEx(0, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
SCREEN_WIDTH+5, 0, WINDOW_WIDTH-SCREEN_WIDTH-10, WINDOW_HEIGHT-30, hwndMain, (HMENU)IDC_CTRL_EDIT, GetModuleHandle(NULL), NULL);
if (hTextarea == NULL) { MessageBox(NULL, L"Could not create the test text control. The program will now close.", NULL, MB_OK | MB_ICONEXCLAMATION); return false; }
// Dozens of attempts with something like:
// lpEditWndProc = (WNDPROC)SetWindowLongPtr(hTextarea, GWLP_WNDPROC, (LONG_PTR)EditControlProc);
// lpEditWndProc = (WNDPROC)SetWindowLongPtr(hTextarea, GWLP_WNDPROC, (LONG_PTR)MainClass::EditControlProc);
// tried static callback functions, etc.
//Every try the compiler said: Are there missing braces ( ) with EditControlProc
}
// To get working, I separated:
WNDPROC lpEditWndProc;
LRESULT CALLBACK EditControlProc(HWND hwndEdit, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
default:
return CallWindowProc(lpEditWndProc, hwndEdit, uMsg, wParam, lParam);
}
return 0; // DONE
}
With my (various) attempts the compiler said: Are there missing braces ( ) with EditControlProc, or said type mismatch when trying to define lpEditWndProc.
I am probably missing out on something simple?
Useage:
main.cpp
MainClass mainclass;
mainclass.ShowInfoTextarea();
Thanks. If missing any info let me know.
Jonathan Potter's comment was the solution.
For anyone who reads:
mainclass.h
class MainClass {
private:
HWND hwndMain;
HINSTANCE hInstanceMain;
HWND hTextarea;
static LRESULT CALLBACK EditControlProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
public:
bool init(HWND _hwnd, HINSTANCE _hInstance);
bool ShowInfoTextarea();
};
mainclass.cpp
// To Show the Textarea
bool MainClass::ShowInfoTextarea() {
if (hTextarea != NULL) return true; // if it is not null, the textarea is likely already displayed.
// Creating the EDIT textarea
hTextarea = CreateWindowEx(0, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
SCREEN_WIDTH+5, 0, WINDOW_WIDTH-SCREEN_WIDTH-10, WINDOW_HEIGHT-30, hwndMain, (HMENU)IDC_CTRL_EDIT, GetModuleHandle(NULL), NULL);
if (hTextarea == NULL) { MessageBox(NULL, L"Could not create the test text control. The program will now close.", NULL, MB_OK | MB_ICONEXCLAMATION); return false; }
SetWindowSubclass(hTextarea, EditControlProc, 0, 0);
}
// Now Works
LRESULT CALLBACK MainClass::EditControlProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch (msg) {
case WM_KEYDOWN:
MessageBox(0,0,0,0);
break;
default:
return DefSubclassProc(hWnd, msg, wParam, lParam);
break;
}
return 0; // DONE
}
Related
I recently tried to subclass a control using the SetWindowSubclass() function. And to my surprise, it worked smoothly at first. I created the control procedure, but it doesn't receive messages like WM_LBUTTONDOWN or WM_KEYDOWN, and I don't understand why! I searched all my time yesterday and I confess that I am losing hope.
Here is my code, simplified (very long in normal times!):
HWND button = CreateWindow("BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 100, 200, 250, 50, hwnd, (HMENU)ID_B_JOUER, instance, NULL);
SetWindowSubclass(boutton, ControlProc, ID_B_JOUER, 0);
And here is the control procedure:
LRESULT CALLBACK ControlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch(msg)
{
case WM_...:
{
MessageBox(hwnd, "Bonjour!", "Message", MB_ICONINFORMATION); // Here it does not work!!! Something be the message.
break;
}
default:
{
DefSubclassProc(hwnd, msg, wParam, lParam);
}
}
return 0;
}
I want to clarify that I am using the GetLastError() function and it returned 0 (no problem). I hope someone has a solution because it's very weird.
You are creating a new BUTTON control and assigning it to a variable named button, but you are subclassing using a different variable named boutton instead.
Assuming that is just a typo, your ControlProc() is returning 0 for every message, it is ignoring the return value of DefSubclassProc(), which may not be 0. You MUST return what DefSubclassProc() returns for unhandled messages.
HWND button = CreateWindow("BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 100, 200, 250, 50, hwnd, (HMENU)ID_B_JOUER, instance, NULL);
SetWindowSubclass(button, &ControlProc, ID_B_JOUER, 0);
...
LRESULT CALLBACK ControlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (msg)
{
case WM_LBUTTONDOWN:
case WM_KEYDOWN:
{
MessageBox(hwnd, "Bonjour!", "Message", MB_ICONINFORMATION);
break;
}
default:
{
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
}
return 0;
}
I know that in Win32 API (no MFC), in an edit control, there's no functionality of enter key. In a standard textbox, when we press the enter key, the cursor jumps to the next line. I want to add that function of the enter key to the edit control.
I tried subclassing and I was able to detect enter key press but how do I jump to the next line?
This is the following subclass :-
LRESULT CALLBACK EditProc
(
HWND hWnd,
UINT msg,
WPARAM wp,
LPARAM lp,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
switch (msg)
{
case WM_CHAR :
{
if (wp == VK_RETURN)
{
// Add functionality here
}
break;
}
default :
{
return DefSubclassProc (hWnd, msg, wp, lp);
}
}
return 0;
}
You can refer to How to Create a Multiline Edit Control:
I create a sample with the following code:
#define ID_EDITCHILD 100
LRESULT CALLBACK WndProc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
static HWND hwndEdit;
RECT rect;
switch (message)
{
case WM_CREATE:
GetClientRect(hwnd, &rect);
hwndEdit = CreateWindowEx(
0, L"EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOVSCROLL,
0, 0, 200, 200,
hwnd,
(HMENU)ID_EDITCHILD,
NULL,
NULL);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return NULL;
}
And it works for me:
Yeah finally, I found the answer. I would like to implement what #CodyGray and #ZhuSong-MSFT said and here's the code :-
HWND hTb = CreateWindow
(
L"Edit", L"LOL",
WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | WS_HSCROLL | WS_VSCROLL | ES_WANTRETURN,
0, 0, LOWORD (lp), HIWORD (lp),
hWnd, NULL, NULL, NULL
);
This even includes adding a horizontal and a vertical scrollbar.
If anyone wants to dock the textbox in the fill area, I have a solution :-
case WM_SIZE :
{
MoveWindow (hTb, 0, 0, LOWORD (lp), HIWORD (lp), TRUE);
break;
}
Just put the above code in the WndProc function of the main window.
I am trying to create a simple application where the user can drag and drop files from outside of the window (usually Explorer) into an area inside of the window. My ultimate purpose is to get the file path to later on process it.
Currently I can drag and drop files into the area but I never receive the WM_DROPFILES event. I have tried with some related functions (DoDragDrop, RegisterDragDrop, CDropSource), but they all have been either impossible to compile or unsuccessful.
Could anyone tell me if I am missing setting any property?
Many thanks in advance
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_CREATE:
{
CreateWindowEx(
WS_EX_ACCEPTFILES,
TEXT("static"),
TEXT("Drag and drop your file to this area"),
WS_VISIBLE | WS_CHILD,
20, // x
20, // y
120, // w
60, // h
hwnd, // parent window
(HMENU) 1, // unique label
NULL, NULL);
}
case WM_DROPFILES:
{
MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(hwnd, Message, wParam, lParam);
}
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"WindowClass",
"MySimpleApp",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
WINDOW_W,
WINDOW_H,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
You are not receiving the WM_DROPFILES message because you are not subclassing the STATIC control you create to receive messages that are sent to it. You are assuming you can catch the message in the control's parent window, but that is not where the message goes. It is sent to the window that you actually drop onto - the STATIC control.
Try this instead:
LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, &StaticWndProc, uIdSubclass);
break;
}
case WM_DROPFILES: {
MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE: {
HWND hStatic = CreateWindowEx(
WS_EX_ACCEPTFILES,
TEXT("static"),
TEXT("Drag and drop your file to this area"),
WS_VISIBLE | WS_CHILD,
20, // x
20, // y
120, // w
60, // h
hwnd, // parent window
(HMENU) 1, // unique label
NULL, NULL);
SetWindowSubclass(hStatic, &StaticWndProc, 0, 0);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default: {
return DefWindowProc(hwnd, Message, wParam, lParam);
}
}
return 0;
}
DoDragDrop() and RegisterDragDrop() (which you should be using instead of WM_DROPFILES) have nothing to do with receiving WM_DROPFILES.
You are missing
DragAcceptFiles( hwnd, TRUE );
Put it just before message loop.
WM_DROPFILES fails to correctly transfer data from 32-bit application to 64-bit one. Could be remedied by implementing IDropTarget and removing WM_DROPFILES handling.
I have a basic wrapper class for my buttons created in C++ with WinAPI. I try to handle messages but it looks like not everything reaches my WndProc
class MyButton {
public:
MyButton(HINSTANCE, HWND);
private:
HWND _hWnd;
static LRESULT CALLBACK _WndProc(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR);
};
MyButton::MyButton(HINSTANCE hInst, HWND hParent)
{
this->_hWnd = CreateWindow(
TEXT("BUTTON"),
TEXT("CLICK ME"),
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10,
100, 25,
hParent,
NULL,
hInst,
this
);
SetWindowSubclass(this->_hWnd, this->_WndProc, 1, (DWORD_PTR)this);
}
LRESULT CALLBACK MyButton::_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSub, DWORD_PTR dwRef)
{
// MessageBox(NULL, L"TEST1", L"TEST1", MB_OK | MB_ICONINFORMATION);
MyButton *pThis = (MyButton*)dwRef;
switch (uMsg)
{
case WM_COMMAND:
MessageBox(NULL, L"TEST2", L"TEST2", MB_OK | MB_ICONINFORMATION);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
The function is called ("TEST1" is shown) but when I click the button, I don't see "TEST2". I also tried with WM_CREATE and it doesn't work either. I don't know what messages are passed to _WndProc
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
To pass a pointer to a member function
When I move out of the class the WNDPROC DefEditProc; and EditKeyProc all works ok. But now as I pasted the code it fails compilation with error error: invalid use of member function (did you forget the '()' ?). So my question is how to squeeze this code into class so I do not pollute global namespace ?
#include <windows.h>
#include <richedit.h>
class richEdit {
HWND richeditWindow;
WNDPROC DefEditProc;
public:
LRESULT EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
}
richEdit() {
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
DefEditProc = (WNDPROC)SetWindowLong(richeditWindow, GWL_WNDPROC, (long)EditKeyProc);
}
~richEdit() {
MSG msg;
while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
}
}
};
int main() {
richEdit re;
}
You are supposed to use a free function instead of a member function, since member functions have an implicit this parameter. You will have to declare EditKeyProc as static CALLBACK, and find another way to pass around the this pointer if you need it.
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
:::
}
Additionally, you may be able to use SetWindowSubclass which will take care of proper subclassing and will keep around an extra pointer argument for you.
Try this:
#include <windows.h>
#include <richedit.h>
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&EditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
Or:
#include <windows.h>
#include <richedit.h>
namespace myNS
{
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&richEditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
}
You can't use a pointer to a member function as a window procedure because Windows expects a regular function pointer. Internally, it won't have enough storage for the member pointer in some cases (they can be up to 16 bytes in MSVC, 32-bit), and it won't know what "this" pointer to use in any case.
If your only interest is avoiding namespace pollution, then the easiest solution is to use a namespace instead of a class. Otherwise, you'll have to make EditKeyProc a static member of the class and use SetWindowLong to store a copy of the "this" pointer for EditKeyProc to access.