Why multiple CWnd can't attach same HWND - winapi

I'm trying to manipulating a remote window in another process. I create a wrapper class of CWnd, and am trying to make constructor & copy constructor. So my application may have multiple instances of wrapper class attaching same HWND.
The error says:
Debug Assertion Failed!
Here is the sample code:
CWnd wnd1, wnd2;
wnd1.Attach((HWND)1);
wnd2.Attach((HWND)1); // Error happens in this line

MFC is (in part) a resource management wrapper around the Windows API. It maintains a strict ownership relation between HWNDs and CWnds. A native HWND can only ever be owned by at most one CWnd object1.
That explains the error you get.
Now on to the solution: Since you do not own the foreign HWND anyway, attaching it to a CWnd is the wrong approach already. And since a CWnd merely offers resource management (which you don't need for resources you don't own), it doesn't buy you anything.
Solution: Drop MFC. Use the HWND with the Windows API.
1 MFC checks this invariant by keeping a list of all HWNDs, that are currently attached to CWnd objects. CWnd::FromHandlePermanent is a lookup into that list. If it returns a non-NULL value, the assertion fires.

Related

How do I know if my MFC Application use COM / OLE?

I come from Win7 to Win10 and recently ran in an "Improper argument error" by opening an file.
The error occurs very rare and randomly.
No further info or call stack info are provided. I had do search deep in MFC code. The DocManager opens the document and then calls CDocument::SetPathName(..,TRUE) to add the current file to the recent file list too.
It seems this function use now an OLE/COM function.
void CRecentFileList::Add(LPCTSTR lpszPathName, LPCTSTR lpszAppID)
{
:
:
Add(lpszPathName);
HRESULT hr = S_OK;
CComPtr<IShellItem> psi = NULL;
hr = _AfxSHCreateItemFromParsingName(lpszPathName, NULL, IID_IShellItem, reinterpret_cast<void**>(&psi));
ENSURE(SUCCEEDED(hr)); // Remark Tom: This throws an AfxInvalidException()
}
hr ErrorCode is -2147221008, which means CoInitialize has not been called.
I am surprised, because I never used COM / OLE Stuff.
Do overcome this error, I must add AfxOleInit() in InitInstance.
My question is, how Do I know in advance if my application needs to use OLE / COM?
Additional question, do I have any drawback if I use COM / OLE in my application. (memory & speed)?
My question is, how Do I know in advance if my application needs to use OLE / COM?
MFC applications are supposed to always initialize OLE/COM, specifically with a concurrency model of STA (single thread apartment). This is done automatically in the wizard-generated templates, otherwise it must be done explicitly in user's code. From the CWinApp documentation:
MFC applications must be initialized as single threaded apartment (STA).
If you call CoInitializeEx in your InitInstance override, specify COINIT_APARTMENTTHREADED (rather than COINIT_MULTITHREADED).
Having OLE/COM always initialized to STA also avoids the curse of the implicit MTA mentioned by #IInspectable in a comment.

recreate eglCreateWindowSurface with same native window

I have written a library using EGL APIs. This library has 'init' and 'deinit' fuctions.
In 'init' function Native window id passed by user, using that native window id eglCreateWindowSurface gets created. In 'deinit' function surface is destroyed using eglDestroySurface.
Now user call 'init' function again to create another eglCreateWindowSurface but he passed the same window id as earlier(as he has not closed his window), here eglCreateWindowSurface failed with error EGL_BAD_ALLOC.
I read EGL specs
If there is already an EGLSurface associated with win (as a result of
a previous eglCreateWindowSurface call), then an EGL_BAD_ALLOC error
is generated
I don't get this when i have already distroyed surface using eglDestroySurface why it is bothered to create again using same window id.
This problem can happen when xserver re-use earlier closed window id?
Did eglDestroySurface return an EGL_TRUE value?
If not, the destruction may have failed.
Note that EGL_BAD_ALLOC may also be generated if there are not enough resources to allocate the new surface.
Your EGLSurface may not be destroyed even after eglDestroySurface or eglTerminate. You probably called eglMakeCurrent( display, surface... context) - this binds your surface to the default framebuffer of the GL context. You need to unbind so that the surface can be truly deleted. Call eglMakeCurrent(display, EGL_NO_SURFACE... EGL_NO_CONTEXT) - this results in the the active thread releasing the context and the surface(s). Now you will find that EGL has forgotten/deleted the surface and you can reuse the Window ID. eglReleaseThread should do the same.
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); will unbind your surface and allow it to be deleted, it is not immediate, eglMakeCurrent will cause a glFlush on that context. So a small bit of time might be needed to complete the draws you have called and resolve the image to the buffer. Otherwise the GPU can crash. Call glFinish() before eglMakeCurrent to ensure completion, or create fence and wait for it. It doesn't matter if you call eglDeleteSurface before or after eglMakeCurrent(... EGL_NO_SURFACE...), but all gl commands must happen before eglMakeCurrent(... EGL_NO_SURFACE..., EGL_NO_CONTEXT). Because that makes no context current, and gl commands will not execute without a current context.
You can try calling eglGetCurrentContext and eglGetCurrentSurface to check if the surface still exists - you get a handle not EGL_NO_SURFACE. But the surface handle is definitely not useful after eglDeleteSurface. See EGL spec 1.5 section 3.7.4 - but as I said above, it exists only because its bound to the current context. See section 3.7.3

Life time of icon handles return from ExtractIconEx

I'm using ExtractIconEx to extract icons handles (and later use them):
ExtractIconEx("c:\\progra~1\\winzip\\winzip32.exe", 0, &hIconLarge, &hIconSmall, 1);
I store the handles in an object and later use the icons handles with DrawIcon. when the object is freed I destroy both handles via DestroyIcon.
My question: is it guaranteed both handles are available and owned by my application (for it's entire lifetime), or should I use CopyIcon for this purpose?
From the documentation of ExtractIconEx:
You must destroy all icons extracted by ExtractIconEx by calling the DestroyIcon function.
This implies, that the lifetime ends, when you call DestroyIcon. In other words, you own the icons returned by ExtractIconEx.

Are COM objects responsible for keeping their own module in memory?

Say you do the following:
1) Load foo.dll using LoadLibrary.
2) Get a pointer to a function using GetProcAddress.
3) Invoke the function, giving you a reference to a COM object implememented in that module.
4) Free foo.dll by calling FreeLibrary.
5) Call a method on the COM object.
Would you expect that step 5 succeeds and doesn't AV? That is, is the COM object itself responsible for calling LoadLibrary (again) to increment the reference count that Windows keeps for each module, ensuring that it doesn't outlive the module?
Certainly not. The module reference count is maintained by the normal methods of use - what you are doing is a backdoor into the runtime scheme. Normally, you use CoCreateInstance and so on to create your objects - these are a wrapper around calling CoGetClassObject and DllGetClassObject. CoGetClassObject calls CoLoadLibrary which maintains a reference count on the dll. Additionally, you can call LockServer on the class object to maintain a reference count on the class object for performance, but this is not required to ensure that the dll remains loaded.
I'd expect an AV. Even if it didn't AV today, this is probably a crash waiting to happen.
For a case like this, where a COM object is returned via a custom DLL export, I would not expect the COM object to increment the reference count of the DLL - as long as the application is using resources from a DLL it explicitly loaded, it's the application's responsibility to keep that DLL loaded.
For in-proc COM objects that are created via the normal CoCreateInstance route, the DLL will typically export DllCanUnloadNow, which should return S_FALSE as long as there are outstanding references.
However, there is nothing preventing the COM object from incrementing the DLL's reference count via LoadLibrary - there is nothing illegal or unsafe about doing so.

Attach window to running application

I have customer that uses older, custom built ERP application for which they don't have source code and company that developed it does not exist any more. Application is developed in 2000 and it's built with Delphi. Since last executable is from 2003, it could be D6 or D7.
On one important form there are some fields where customer would like to display additional data from other databases and asked me if it's possible to "duct tape" data over existing form.
First idea I got is to build application that will:
browse list of windows target application creates and find controls on form
attach "some" event when to get notified when target form is displayed
attach "some" event when to get notified when field on target form is changed
display additional information in small window overlaying target form
Are there any examples on how to do something like this. I searched google with variations of this question title, but without success.
Note - rewriting of ERP application is not planned.
About language - I can do it with C# or Delphi.
I'm going to answer this from a purely C & Win32 point of view, because I don't know Delphi or its libraries. Converting this to C# can be accomplished via p/invoke, but some parts may/will need to be unmanaged.
First off, no guarantees. If the target application is doing windows-less controls (if there isn't an HWND under every on-screen control) you're pretty much out of luck. This isn't all that uncommon, so yeah...
Step 1, register a window hook listening for new windows created by the target process*:
//dllHMod is an HMODULE that refers to the DLL containing ShellHookProc
HHOOK hook = SetWindowsHookEx(WH_SHELL, ShellHookProc, dllHMod, 0);
// error handling, stashing hook away for unregistering later, etc...
LRESULT CALLBACK ShellHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam);
if(nCode == HSHELL_WINDOWCREATED)
{
WindowCreate((HWND)wParam);
}
return 0;
}
WindowCreated(HWND) should stash the HWND away if the correct process (determined via GetWindowThreadProcessId) owns it. At this point, you'll be able to get every top-level window owned by the target process. Note that registering a global hook carries a notable performance penalty, not that it really matters in your case but you should expect it.
Now for the fun part. There's no reliable way to tell when a window is fully constructed, or when its done rendering (there are ways to tell when it STARTS rendering, but that doesn't really help). My advice, guess. Just throw some arbitrary wait in there and then try and enumerate all the child windows.
To enumerate child windows (if you know enough about the target window, there are better ways to do this; but I'm assuming a search is easiest):
//targetHWND is an HWND of one of the top-level windows you've found
EnumChildWindows(targetHWND, ChildWindowCallback, NULL);
//more code...
BOOL ChildWindowCallback(HWND window, LPARAM ignored)
{
if(IsTargetWindow(window)) { /* Do something */ }
return TRUE;
}
Implementing IsTargetWindow is another tricky part. Hopefully you'll find some reliable test for doing so (like checking the class name, window name, style, something; look at GetWindowInfo).
Once you have the window you want to monitor, you can use SetWindowLongPtr and GWLP_WNDPROC to watch all messages it receives. This will require code injection (and thus unmanaged code) and is awfully low level. I'd advise against it if you could possibly avoid it, but lacking the source...
I think this answers is a decent starting point, but once again this is going to be incredibly painful if its even possible at all. Good luck.
*Alternatively, if you know that the target app doesn't create windows except at startup (or at detectable/predictable points in time) you can use EnumWindows.

Resources