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.
Related
How do I avoid using global variables when using EnumWindows (or FindChildWindows) Win32 API?
I've approximately the following code:
HWND prog_hwnd;
BOOL CALLBACK ProgEnumProc(HWND hwnd, LPARAM lParam) {
if (...) {
// found the right hwnd, assign it to prog_hwnd;
prog_hwnd = hwnd;
return FALSE;
}
return TRUE;
}
void FindProgHwnd()
{
EnumWindows(ProgEnumProc, 0);
}
int main()
{
FindProgHwnd();
if (prog_hwnd) {
// found prog_hwnd, but it's global variable
}
}
As you can see, to find the right hwnd, I've to use a global variable prog_hwnd. I want to avoid using the global variable. Is there a way to do it?
Pass a pointer to the variable (LPARAM)prog_hwnd as lParam. This is then passed to the callback each time it is invoked.
Within the callback you can assign to the passed variable by doing *(HWND *)lParam = ....
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.
I would like to write a program, which lets me change a values in text box of different program, or automatically copy a values from one program to another.
I found a way to get hWnd to most (no idea if all of them) of controls in targer program, and to point them with mouse cursor. I made a simple struct to do so, and an array of it
struct hWndpointer
{
HWND hWnd;
AnsiString text;
};
hWndpointer tbl[250];
The EnumWindowProc and EnumChildWindowProc loads handles and text of the window into the array and into the list control in my program, so i can click an item on the list (or select it with keyboard) and the cursor points the control (like button or textbox) like expected... Unfortunately there are some controls with no text (or rather GetWindowText returns no text) so there is no way to identify the control.
The question is:
Is there any way to get/read a NAME of the control?
Is there any way to get/read and set a specyfic value like 'enabled' or 'text' or 'value'?
Thanks in advance
PS: Sorry for my english ;)
You can use SendMessage and PostMessage to send WM_GETTEXT, WM_SETTEXT, WM_ENABLE to windows owned by other processes. (SendMessage for queries, PostMessage for write-only actions)
Often the child ID will be used to identify subwindows (especially in a dialog), but it's also possible for a program to rely purely on the dynamic HWND values, in which case you'll have to fall back to window positions to differentiate.
From the Win32 API's perspective, UI controls do not have Names, so you cannot ask the API to return the Name of a UI control in another process because such a value does not exist. Names are strictly a feature of the UI framework being used by the app (VCL in the case of C++Builder), and you cannot directly access frameworks across process boundaries. You would need cooperation from the control's owning app.
For instance, one way would be to have both apps call RegisterWindowMessage() to register a custom window message, then your app can post that message to the other app specifying the desired control's HWND and your own HWND as parameters. The other app can then SendMessage() the control's Name back to your app's HWND using the WM_COPYDATA message, which you can use to update your list accordingly.
In the VCL framework, you can convert an HWND to a TWinControl* pointer using the FindControl() function. It will return NULL if the HWND does not belong to the calling process, otherwise you can then copy the value from its Name property. For example:
const UINT WM_GETCONTROLNAME = RegisterWindowMessage("WM_GetControlName");
const UINT WM_GETCONTROLNAME_RESULT = RegisterWindowMessage("WM_GetControlName_Result");
#include <pshpack1.h>
struct sControlName
{
HWND hWnd;
int Length;
char Value[1];
};
#include <poppack.h>
void __fastcall TMyForm::WndProc(TMessage &Message)
{
if ((Message.Msg == WM_COPYDATA) && (WM_GETCONTROLNAME_RESULT != 0))
{
LPCOPYDATASTRUCT cds = (LPCOPYDATASTRUCT) Message.LParam;
if (cds->dwData == WM_GETCONTROLNAME_RESULT)
{
sControlName *pName = (sControlName*) cds->lpData;
AnsiString sName(pName->Value, pName->Length);
// locate pName->hWnd in your list and assign sName to it as needed...
return;
}
}
TForm::WndProc(Message);
}
void ___fastcall TMyForm::FillList()
{
...
if (WM_GETCONTROLNAME != 0)
{
HWND TheControlHWND = ...;
HWND OtherAppHWND = ...;
PostMessage(OtherAppHWND, WM_GETCONTROLNAME, (WPARAM)TheControlHWND, (LPARAM)this->Handle);
}
...
}
.
const UINT WM_GETCONTROLNAME = RegisterWindowMessage("WM_GetControlName");
const UINT WM_GETCONTROLNAME_RESULT = RegisterWindowMessage("WM_GetControlName_Result");
#include <pshpack1.h>
struct sControlName
{
HWND hWnd;
int Length;
char Value[1];
};
#include <poppack.h>
void __fastcall TMyForm::WndProc(TMessage &Message)
{
if ((Message.Msg == WM_GETCONTROLNAME) && (WM_GETCONTROLNAME != 0) && (WM_GETCONTROLNAME_RESULT != 0))
{
HWND hWnd = (HWND) Message.WParam;
TWinControl *Ctrl = FindControl(hWnd);
if (Ctrl)
{
AnsiString sName = Ctrl->Name;
std::vector<unsigned char> buffer((sizeof(sControlName) - 1) + sName.Length());
sControlName *pName = (sControlName*) &buffer[0];
pName->hWnd = hWnd;
pName->Length = sName.Length();
strncpy(pName->Value, sName.c_str(), pName->Length);
COPYDATASTRUCT cds = {0};
cds.dwData = WM_GETCONTROLNAME_RESULT;
cds.cdData = buffer.size();
cds.lpData = pName;
SendMessage((HWND)Message.LParam, WM_COPYDATA, (WPARAM)this->Handle, (LPARAM)&cds);
}
return;
}
TForm::WndProc(Message);
}
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.
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.