Access Windows Controls from another class or window - windows

In an MFC program I am trying to access controls that are in one window (class) from another (sibling or daughter) window with code in a different .cpp file. Typically you access a control with a DDX_Control variable defined in the class .cpp file.
DDX_Control(pDX, IDC_STATUS, m_Status);
The code for the other window is in a different file, so I don’t have access to the control variable (m_Status). What I have done is used FindWindow to find the main window, then GetDlgItem to find the control I am interested in:
CWnd * tmpWnd = CWnd::FindWindow(NULL,"MainWindow"); // find the main dialog box
CStatic * tmpStatus = (CStatic*) tmpWnd->GetDlgItem(IDC_Status);
tmpStatus->SetWindowText(“Status Report);
This works fine in the debugger, but fails when executed outside the debugger. FindWindow gets the window ID correctly, but GetDlgItem returns null.
Ideally I’d like to access the control variable (m_Status) from the other window, but I don’ know how to do so. I understand the GetDlgItem is less than ideal under any circumstance.
One solution would be to send a message to the main window class and tell it what to do, but I’d have to have a routine to handle each control and know how to handle whatever kind of data I am sending.
Is there a “right” way to do this?
Thank you
The ultimate answer is to cast to the original class:
((CspDlg *)AfxGetMainWnd())->m_Status.SetWindowText("Report");

Since you created the "main" window you have an object or pointer for it. Or, you can call AfxGetMainWnd() to get a pointer to it. Then you can use that to access a public member such as m_Status. All of your windows are interconnected and it should not be necessary to use FindWindow to find any window in your own program.
The fact that some variables may be defined in another file is not relevant. That can be handled with suitable use of #include "theotherfile.h" and object pointers/references.

Related

How do you change the window class for a CDialogImpl?

I noticed that when I call DoModal() on a CDialogImpl, atlwin.h calls:
return ::DialogBoxParam(_AtlBaseModule.GetResourceInstance(),
MAKEINTRESOURCE(static_cast<T*>(this)->IDD),
hWndParent,
T::StartDialogProc,
dwInitParam);
Which retrieves the dialog box information from the DIALOGEX description in the .rc file.
I can't see how to add a CLASS item to this structure to set the window class to a custom one, as I need the T::StartDialogProc. When I put in the T::StartDialogProc for ATL::CDialogImpl<MyClass>, DialogBoxParam() throws an Access Violation exception. I have to assume this is some kind of thunking problem.
My motivation is, I have a specific issue with the IDC_WAIT cursor being shown for a few milliseconds (it "flashes up" when I call DoModal()) and my working theory right now is that my DIALOGEX doesn't have a default cursor. The reason I think this is because when I do the following in my WM_INITDIALOG handler:
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG) ::SetCursor(::LoadCursor(0, IDC_ARROW)));
The problem is minimized, ie the cursor sometimes briefly flashes to wait. I think if it had a default cursor before it arrived at WM_INITDIALOG, ie from the window class, the problem will disappear completely.
So, is it possible to add a custom window class to a CDialogImpl, or to add a default cursor to the class that CDialogImpl uses? If so, how?

How to get the parent structure as Spy++ gives it?

In our code, a C++ class that is derived from CMDIChildWnd is instantiated and its Create() function is called, which is actually CMDIChildWnd::Create(). The 5th parameter pParentWnd is being set to a CMDIFrameWnd object. This in turn (within the MFC code) sets up a CREATESTRUCT object, sets it's hwndParent to that parameter and then calls PreCreateWindow() with that object passed as one of the parameters.
However, when going up the parent window list using CWnd::GetParent() (original) GetAncestor(hwnd, GA_PARENT) (just a try, since the original didn't work and this code is 20+ years old) and even GetWindow(hwnd, GW_OWNER) just to see if it was setting the window's owner for some reason. In each case, I do not see the handle which is associated with the CMDIFrameWnd object. Instead, the parent shown is the desktop, and owner is NULL.
Using Spy++, it shows what I was expecting. How would I get the layout given by Spy++ programmatically?
In MDI interface CMDIChildWnd is usually passed to CMultiDocTemplate, then we create a new window through CDocManager::OnFileNew, that in turn calls CFrameWnd::LoadFrame which passes NULL for child window's parent (at least in VS 2017)
We usually don't call mdi_child->Create directly, unless we initialize everything else manually. Perhaps you mean the base class CMDIChildWnd::Create is called, or you are able to override it.
In child window,
::GetAncestor(m_hWnd, GA_ROOT) or ::GetAncestor(m_hWnd, GA_ROOTOWNER)
Should return a reference to the main frame, otherwise use AfxGetMainWnd()

What difference between control's window handle and controlID

I'm learning Win32 assembly. Have some question I search but not suitable result.
Anyone can explain for me What difference between control's window handle and controlID.
They have nothing in common. Every window has a handle, returned by CreateWindowEx(). Such a window can have a few extra properties attached, like a menu handle. The hMenu argument in CreateWindowEx(). If the window doesn't have a menu, a child window won't have one, then you can use that argument to pass an arbitrary other bit of data. It will be assigned to the GWLP_ID property (see GetWindowLongPtr). Also note the GWLP_USERDATA, an extra property that's entirely yours to use as you see fit.
Dialogs take advantage of this, a dialog template that you create in the resource editor gives you a way to number the child controls. With a helper function like GetDlgItem() to get the handle back for a control with a specific number. Which is pretty necessary for dialogs since it is Windows that create the child controls from the dialog template so you don't know the window handles for them yourself.

DestroyIcon after SHGetFileInfo?

I'm using SHGetFileInfo to get the icon of specific file type. The MSDN says about the SHFILEINFO:
hIcon
Type: HICON
A handle to the icon that represents the file. You are responsible
for destroying this handle with DestroyIcon when you no longer
need it.
To get the icon from HIcon, I use Icon.FromHandle. Again, MSDN says:
Remarks
When using this method, you must dispose of the original icon
by using the DestroyIcon method in the Win32 API to ensure
that the resources are released.
It is even more confusing as SGHFI_ICON description contains the following information:
SHGFI_ICON (0x000000100)
Retrieve the handle to the icon that represents the file and the
index of the icon within the system image list. The handle is
copied to the hIcon member of the structure specified by psfi,
and the index is copied to the iIcon member.
From this description, it seems like the handle is kept by the OS and I shouldn't actually destroy it.
My question is then: if, and if so, when should I dispose of the icon handle?
Immediately after Icon.FromHandle() ?
When I no longer need the icon created from Icon.FromHandle() ? (in such case I guess that I'd rather copy the icon, release the original and return the copy to avoid handle leaks)
Never (it will be done automatically somehow? Many examples of SHGetFileInfo - even on SO - does not include any code releasing the icon handle)
The system doesn't keep icon handles, it keeps image lists. When you use the SHGFI_ICON flag, SHGetFileInfo creates an icon by calling ImageList_GetIcon against the system image list. You can do this yourself by passing the SHGFI_SYSICONINDEX flag instead to retrieve just the icon index.
The icon should be destroyed when you're done with it.
No, you really do have to call DestroyIcon yourself. The Icon.FromHandle() method is pretty useless to help you with that, it still requires you to call DestroyIcon. You've got to implement your own garbage collector code to know that the icon is no longer in use so it is safe to pinvoke DestroyIcon.
This is cruel and unusual punishment. The Icon class does in fact have a constructor that can create an Icon object from a handle and owns the handle, automatically calling DestroyIcon when the Icon object is disposed or finalized. They however made it inaccessible for unphantomable reasons, major bummer. Reflection to the rescue, you can bypass this silly restriction and still use the constructor, find the code in this answer.

How to subclass Windows Explorer's window

I want to change color of listview of explorer.exe like this
I got the handle of listview window by GetTopWindow function and his family.
To subclass listview window of explorer.exe, I injected my dll code to explorer by following code.
SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll,
dwExplorerListviewThreadId);
My dll is loaed by explorer.exe well.
And I subclassed the window procedure by SetClassLongPtr(for global subclass) in injected code.
SetClassLongPtr returns success but my subclass function(SubclassProc) receives only WM_CREATE WM_DESTROY and WM_MOVE messages. What's wrong? I expected to get WM_NOTIFY and NM_CUSTOMDRAW.
The problem is that this is not a ListView in the first place; it's instead using Microsoft's internal "DirectlUI" framework, which is used in several places in explorer. It doesn't use any of the Common Control messages such as NM_CUSTOMDRAW. There's pretty much no reasonable way to change the colors it uses.
(Also, generally it's best to use SetWindowLongPtr instead of SetClassLongPtr for subclassing a HWND: SetClassLong only changes the underlying template that is used for creating new windows, but may not end up changing any instances that were based on that. And you should not be using the same function - GetMsgProc - for both the hook callback and the subclass proc; they need to handle the message in different ways, the hook callback needs to call CallNextHookEx while the subclass proc needs to call CallWindowProc with the original wndproc. But none of this really matters since the control isn't a ListView in the first place...)
I think and suppose that the OS has special protections for the explorer.exe process, because otherwise it would be an easy target for malicious code or just applications that think they are more important than they actually are (if some people insist on putting back a shortcut on their desktop every time you start the application, imagine what they would do when they had this sort of access to explorer.exe - everything in the shell).
EDIT: I was intrigued by the question and did some more research, I think there is a more mundane reason, see http://blogs.msdn.com/b/oldnewthing/archive/2005/09/07/461912.aspx. (basically: explorer.exe is the window manager so doesn't know about message routing yet when it receives certain messages, which is why they can't be intercepted with message hooks).

Resources