Why make pointers static in dialog procs? - winapi

I'm going through many examples of basic win32 dialog procedures. They seem to take this basic pattern.
class Person
{
char Name[63];
int Age;
};
BOOL CALLBACK EditDlgProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static Person* person;
switch(uMsg)
{
case WM_INITDIALOG:
person = (Person*)lParam;
return FALSE;
case WM_COMMAND:
.....
}
return FALSE;
}
I want to know why make person static?
Is this for efficiency? (avoid assigning person to lParam every call)
Is this for sharing?

It is to maintain state of the pointer person between calls to the function EditDlgProc() .
It ensures that:
The pointer is created only once during the lifetime of the program.
It maintains state of the pointer across consecutive calls to the function and
Gets destroyed only once when the program ends.
Is this for efficiency?
Yes, Possibly. Difficult to say without knowing the design considerations.
Is this for sharing?
No.
Note that the scope of an static variable is limited to the function in this case, So it cannot be shared as such.

Related

Suppressed keyboard messages don't die

I regularly use a certain application (which is closed-sourced, single-threaded, 32-bit, native) that predates such niceties as configurable key bindings. It does support plug-ins, though, so I'm making one to provide the missing feature.
I'm using SetWindowsHookEx to add a WH_KEYBOARD hook. Some key presses go through unchanged, and others get suppressed and handled manually. For the latter case I'm (as the documentation seems to suggest) just returning 1 and ignoring the output of CallNextHookEx. That works in most cases, but there's at least one case--Ctrl+D--that the application is still handling even though I told it not to.
Is there something more/different I should be doing to squash that keystroke?
Maybe relevant: normally I get both an HC_NOREMOVE and an HC_ACTION version of each message, but for Ctrl+D I only get the HC_ACTION version.
C# source:
private IntPtr ProcessWindowsMessage(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0 || code == HC_NOREMOVE)
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
Keys key = GetKeyFromWParam(wParam);
Keys keyCombo = key | GetCurrentModifiers();
var isKeyDownEvent = IsKeyDownEvent(lParam);
var handled = stateMachine.Add(keyCombo, isKeyDownEvent);
if (handled)
{
Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
return LResultHandled;
}
else
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
private static readonly IntPtr LResultHandled = new IntPtr(1);

sendmessage() doesn't work inside a function

If I do it like this, it works - it populates the listbox:
BOOL CALLBACK MnDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
res = stmt->executeQuery("SELECT id FROM tremreg ORDER BY id DESC");
while (res->next())
{
int i = res->getInt("ID");
std::string str = boost::lexical_cast<std::string>(i);
char *cstr = new char[10];
strcpy_s(cstr, 10, str.c_str());
SendMessage(GetDlgItem(hwnd, IDC_lbList),
LB_ADDSTRING, 0, (LPARAM)cstr);
}
delete res;
}
break;
However, if I wanted to be tidy and put it inside a function, like this, it would no longer populate the list, even though the function itself DOES run.:
VOID fRefreshListID()
{
res = stmt->executeQuery("SELECT id FROM tremreg ORDER BY id DESC");
while (res->next())
{
int i = res->getInt("ID");
std::string str = boost::lexical_cast<std::string>(i);
char *cstr = new char[10];
strcpy_s(cstr, 10, str.c_str());
SendMessage(GetDlgItem(g_hMnDialog, IDC_lbList), LB_ADDSTRING, 0, (LPARAM)cstr);
}
delete res;
}
BOOL CALLBACK MnDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
fRefreshListID();
}
break;
For some reason, SendMessage() refuses to work while inside a function. g_hMnDialog is a global handle for the main dialog which contains ALL of the controls, listbox included.
PROBLEM: SendMessage(), works and populates the listbox outside a function. However, the function does not populate the listbox once called, even though it contains the same code.
QUESTION: Why does my SendMessage() does not work inside a function, and what steps should I take to make it work?
BONUS QUESTION: Where would be the best place to call this function (once/if) it works to constantly refresh the listbox?
The obvious explanation would be that g_hMnDialog is not yet initialised at the time when you call fRefreshListID(). Personally, I would avoid using global variables where possible. When you call fRefreshListID(), you have the window handle to hand to it makes sense to pass it as a parameter to fRefreshListID(). The function would look like this:
void fRefreshListID(HWND hwndDialog)
{
....
}
You should be checking for errors when you call API functions. My expectation is that GetDlgItem returns NULL because g_hMnDialog is not valid. You then blindly pass that NULL on to SendMessage. I recommend that you add some error checking.
Your string handling is all off too. You are making it much more complex than needed. You can write it like this:
HWND hwndList = GetDlgItem(hwndDialog, IDC_lbList);
if (hwndList == NULL)
// deal with this error
std::string str = ...;
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)str.c_str());
If you want the function to repopulate the list you will need to clear the list, or at least replace items. As it stands, each time you call your function you will add more items to the existing ones.
What is the best time to call the function while the application is running? That cannot be answered in detail with the information you provided. It depends on what your application is doing. What do you want to trigger a refresh? Do you want it to be based on a timer and so be a polling approach? Or do you want to listen for an event that tells you that the list contents are out of date. Only you can really answer those questions.

How to pass arguments to SetWindowsHookEx's callback? (if even possible)

I'm using SetWindowsHookEx with WH_CALLWNDPROC to catch all WndProc messages - and it works fine.
I was wondering if there is a way to store a parameter somewhere where the callback function can read and use it.
I assume that because the callback function is inside the "other" process, there's no connection to the process calling the SetWindowsHookEx, so just storing a value in a static variable wont do any good..
For example:
void Hook(DWORD dwThread, int randomNumber)
{
MagicallyStore(randomNumber);
SetWindowsHookEx(WH_CALLWNDPROC, hookProc, GetModuleHandle(L"WNDProcHooks"), dwThread);
...
...
}
LRESULT CALLBACK hookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
int randomNumber = MagicallyRestore();
DoSomthing(randomNumber);
return CallNextHookEx(0, nCode, wParam, lParam);
}
I already thought about having the "MagicallyStore" function write the parameter to a file and reading it once in the "MagicallyRestore" function - but there must be a better way..
Thanks in advance :)

codeblock vs VS2010

I have got following code:-
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char ay[256]={0};//HWND hwnd= GetForegroundWindow();
if( GetClassName(hwnd,ay,256))
{
char x[70]={0};
GetWindowText(hwnd,x,70);
if(IsWindowVisible(hwnd))
{
// CaptureAnImage(hwNd,hwnd);
HINSTANCE hins= (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);
WNDCLASSEX lpwcx;
GetClassInfoEx(hins,ay,&lpwcx);
if (MessageBox(0,
strcat(strcat(x, "\r\n"), lpwcx.lpszClassName),
"Info", 0x06L) == IDTRYAGAIN)
{
return false;
}
}
}
return true;
}
void cstm()
{
EnumWindows(EnumWindowsProc,0);
}
This runs fine on Codeblocks (with VS 2010 compiler(cl)) but VS2010 gives a corrupted lpwcx value, I have tried the Unicode as well as Ascii to tackle this but no good result at
all. The first lpwcx is correct but later they return class not found(1411) ,although the hinstance and class name is correct.
Please help.
strcat(strcat(x, "\r\n"), lpwcx.lpszClassName),
The odds that this will overflow the x buffer and stomp some local variable values, like *lpwcx", are very high. 70 chars is unreasonably frugal. If you don't want to use strcat_s() then at least make it bigger. And yes, initialize lpwcx.cbSize
Always fill in the cbSize member of data blocks before calling any API functions. Many of them rely upon this value to know which version of the data structure they should fill in.

Why does this window subclassing code crash?

I am trying to subclass the window that currently has focus. I do this by monitoring for HCBT_ACTIVATE events using a CBT hook, and set and unset the WndProc of the focused and previously focused windows.
The problem is that it only works whenever I have a breakpoint set somewhere in the code.
If there is no breakpoint, once my application exits, all the windows that I have subclassed crashes in order, even though I have removed the subclassing and restored the original WndProc.
I have verified that Unsubclass() is called whenever my application shuts down.
// code extracts
HINSTANCE hInst;
HHOOK hHook;
#pragma data_seg(".shared")
HWND hWndSubclass = 0;
FARPROC lpfnOldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,rws")
void Unsubclass()
{
// if the window still exists
if (hWndSubclass != 0 && IsWindow(hWndSubclass))
{
SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
hWndSubclass = 0;
}
}
static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MOVING)
{
// this is just test code so I can see it works (it does)
RECT* r = (RECT*)lParam;
r->right = r->left + 500;
r->bottom = r->top + 500;
return TRUE;
}
else if (message == WM_DESTROY)
{
Unsubclass();
}
return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}
void SubclassWindow(HWND hWnd)
{
// remove the subclassing for the old window
Unsubclass();
// subclass the new window
lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
hWndSubclass = hWnd;
}
static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_ACTIVATE)
{
SubclassWindow((HWND)wParam);
}
return 0;
}
// ... code that initializes the CBT proc
__declspec(dllexport) BOOL Setup()
{
hHook = SetWindowsHookEx(WH_CBT, CBTProc, hInst, 0);
}
__declspec(dllexport) BOOL Teardown()
{
UnhookWindowsHookEx(hHook);
Unsubclass();
}
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
Unsubclass();
return TRUE;
}
return TRUE;
}
Your problems hinge on several fronts:
UnHookWindowsHook does not unload injected dlls, all it does is remove the hook proc. If the dlls need to be unloaded its up to them to invent some kind of unloading mechanism.
SetWindowLongPtr typically fails when called from a process other than the process that owns the window.
The nett result of this is, its very difficult to safely remove windows hooks. First thing, your OldWindowProc pointer should not be stored in the shared data area. Next, in order to remove the subclass, you need to be able to co-erce the (currently) subclassed process to perform the un-subclassing.
What you could do is, first, register a new unique message id and place it in your shared area using RegisterWindowMessage. WM_REMOVE_HOOK.
UINT idWM_REMOVE_HOOK = RegisterWindowMessage("WM_REMOVE_HOOK");
Now, whenever you need to remove a hook,
SendMessage(hWndSubClass,idWM_REMOVE_HOOK,0,0);
In your subclass proc:
if(uMsg == WM_DESTROY || uMsg == idWM_REMOVE_HOOK)
{
Unsubclass(hwnd);
}
Remove the call to UnSubClass in DLL_PROCESS_DETATCH. Its a dangerous race condition thats going to cause your dll being unloaded in some random process to trash the hook data of a potentially valid hook in another process.
lpfnOldWndProc and hWndSubclass are global pointers. Seems like you've got only one per process. What if a process creates more than one window?
Then you will unsubclass only the last one.
EDIT: Also, why do you tear down in Process DETACH?
You are creating a global system-wide hook in a DLL. You need to store the HHOOK handle and your subclassing information in a block of shared memory so all instances of your DLL in all running processes can have access to them. Your variables are declared global in code, but each individual instance of the DLL will have its own local copy of them, and thus they will not be not initialized in all but 1 of your DLL instances (the one that calls Setup()). They need to be shared globally within the entire system instead.
You also should not be calling TearDown() in DLL_PROCESS_DETACH, either. Every instance of the DLL is going to call TearDown() when their respective processes terminate, but only the single instance that actually called Setup() should be the one to call Teardown().
If the debugger will cause the process to succeed by adding a breakpoint then most likely, this is a timing issue.
What possibly happens is that your main application is closing itself and freeing resources just before the subclassed windows get the messages they need to remove the subclass again. You might want to give them a few processing cycles to handle their own messages between the unhooking and the unsubclassing. (In Delphi you could do this by calling Application.ProcessMessages but in your C++ version? Don't know the answer to that.

Resources