sending lparam as a pointer to class, and use it in WndProc()
Asked Answered
E

5

6

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.

}

}
Eckard answered 12/9, 2011 at 7:55 Comment(1)
lParam stands for long parameter as following MSVC++ and Windows SDK naming.Eichler
M
4

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.

Morgan answered 26/2, 2018 at 21:21 Comment(0)
K
2

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.

Kaka answered 12/9, 2011 at 8:10 Comment(3)
This is horribly inefficient. Given how often the message pump is hit, you're reassigning many many times unnecessarily.Conquian
@Mike Kwan Samples savearr = (Samples)GetWindowLong(hWnd,GWL_USERDATA) Can be used in whichever message case is required. Since the message hasn't been specified, i just added it at the beginning.Kaka
You need to save the lpCreateParams from the CREATESTRUCT, since the CREATESTRUCT itself is valid only for the lifetime of the WM_CREATE message. I.e., SetWindowLong(hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT*)lParam)->lpCreateParams). (As for efficiency: First profile your program. I bet the cost of the GetWindowLong never shows up as even a blip.)Dashpot
E
1

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.

Ethbinium answered 12/9, 2011 at 8:0 Comment(4)
i don't understand, i have the lparam value, sent to WndProc, so someone is sending it. is it posible that everytime WndProc is called, a pointer to the class i made will be there in the lparam value ?Eckard
lParam is the parameter of the message, so whoever sends the message - sets its value. In case of WM_CREATE - the CreateWindow sets it, to point to a certain structure that contains data required to create the window. One of the members of that structure is the lParam you pass to CreateWindow. The fact that these variables have the same name means nothing - they're not the same variables.Ethbinium
ah...ok i see my mistake. then i guess my best option is just to save it as a global value in the program, though it doesn't look good.Eckard
No, don't use a global. Use SetWindowLong() to save it in the WM_CREATE message and GetWindowLong() when you need to retrieve it.Fatigue
C
0

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.

Conquian answered 12/9, 2011 at 8:8 Comment(0)
E
0

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.

Euton answered 6/8, 2014 at 5:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.