Application hangs when hosting managed control as CWnd - c#

My application has ATL-based GUI (CWnd, CDialog,...) and it consists of multiple pages (CDialog). One of these pages is otherwise empty but it has a placeholder frame (CWnd) that resizes with the dialog. Everything is built as x64.
When the page loads, it asks for a control handle from managed (C#) side of the application using COM-interop, and adds the control to the dialog as CWnd that is created from that handle:
Managed implementation simplified:
// Class "ManagedControlProvider"
private Control myUserControl;
public long CreateControl()
{
myUserControl = /*Create some new inheritant of UserControl */
myUserControl.Dock = DockStyle.Fill;
return myUserControl.Handle.ToInt64();
}
Native side simplified:
// Call the managed class. Lifetime of m_pManagedControlProvider
// is ensured elsewhere.
LONGLONG lHandle = m_pManagedControlProvider->CreateControl();
// m_pUserCtrlAsCWnd is CWnd*
m_pUserCtrlAsCWnd = CWnd::FromHandle((HWND)lHandle);
m_pUserCtrlAsCWnd->SetParent(this);
// m_ControlFrame is just a native helper-CWnd the dialog that
// resizes with it a so gives us the size we want to set for the
// managed control. This code is also call in every resize -event.
RECT winRect;
m_ControlFrame.GetWindowRect(&winRect);
ScreenToClient(&winRect);
m_pUserCtrlAsCWnd->SetWindowPos(NULL,
winRect.left, winRect.top, winRect.right - winRect.left,
winRect.bottom - winRect.top, 0);
I have done this multiple times and it usually works exactly as is should. But sometimes, like now, I'm experiencing application hangs without any clear reason. With my current control this seems to happen roughly 5s after the focus is set to some other desktop application.
I have verified that the issue is not in the managed control's lifetime or GC. Also it's reproducible in debug build so optimizations are not to blame. When the hang occurs, I can attach debugger and see that some ATL loop keeps on going but that's the only piece of code I'm able to see in stack (imo this indicates that the message loop is somehow caught in infinite loop without interacting with my code).
Now for the dirties fix ever: I added a separate thread to my managed control that invokes this.Focus() every second on the UI thread. Obviously this is a ridiculous hack but it works as long as I pause the focusing everytime user opens combos etc (otherwise they get closed every second).
What am I doing wrong or what could cause this somewhat unpredictable behavior?

I don't know why or what it has to do with anything, but the application hang somehow originated from WM_ACTIVATE. So the solution was to override WINPROC at the main CDialog and block forwarding of that message. Everything has been working without any issues since then.
I'll not mark this as answer because I don't know why this solution works.

Related

Performance implications when hiding a window instead of closing it

I'm making a program that has a login window and the main window. I am wondering if hiding windows would have more impact on performance than closing them.
I've written down both these options, let me know if there's a better way to do it.
private void Login()
{
DataMatrixWindow dmWindow = new DataMatrixWindow(); // Creates new Datamatrix window
dmWindow.Show();
Close(); // Closes login window
}
Or keeping Login window alive the whole time, and just show / hide it when needed.
private void Login()
{
DataMatrixWindow dmWindow = new DataMatrixWindow(); // Creates new Datamatrix window
dmWindow.Show();
Visibility = Visibility.Collapsed; // Hides login window
}
MSDN makes no note of a potential performance hit if windows are not closed.
Window.Close Method: Unmanaged resources created by the Window
are disposed.
Window.Closing Event: If you want to show and hide a window multiple
times during the lifetime of an application, and you don't want to
re-instantiate the window each time you show it, you can handle the
Closing event, cancel it, and call the Hide method. Then, you can call
Show on the same instance to re-open it.
Some more details
I am closing and creating a new Datamatrix window, though the same arguments could be made whether to show or hide it like the login screen
I do not expect the user to constantly log in and out, so these switches shouldn't occur very often (Hence why I'm leaning to closing the login window instead of hiding it)
When you use Visibility.Collapsed, it does not remove your window from the visual tree, therefore there is performance implication on leaving it unseen.
To see this performance implications yourself you can do 2 things:
Open Snoop and you will be able to see the entire window on the visual tree.
Create a tester and fill it with buttons (for example) through code - place 10 thousand and set them with Visibility.Collapsed. Then check the performance there as apposed to a clean application. see how long does it take for it to show when starting the exe.
Hope this helps.
I am wondering if hiding windows would have more impact on performance than closing them.
Probably. Closing and showing a simple window such as a login form shouldn't impact the performance noticeably so I would recommend you to close the window when you have logged in and show another instance of it when and if you need to log in again. This is certainly better than keeping the window open in the background after you have logged in. At least in my opinion.

Odd behaviour when opening a WPF Window from WinForms

When displaying a WPF window from an Excel addin, I'm encountering odd behaviour whenever I show it with myWindow.Show() rather than myWindow.ShowDialog(). Thus far everything has worked fine when using the latter. However, it would be nice to be able to display a window such that the user can interact with Excel at the same time - i.e. the behaviour I'd expect from Show().
The problem is that controls in my form start acting very oddly quite quickly. ComboBox dropdowns collapse immediately, and textbox input ends up in whatever cell is selected in the Excel worksheet that's active.
I've noticed that with ShowDialog, Snoop is able to attach to my window as well, whereas with Show, I get an error amounting to "Could not find a PresentationSource to attach to". I'm not, however, completely sure if that's related.
Obviously one solution would be to stop directly showing a WPF window from WinForms; I expect the problem to largely go away if I change my window into a UserControl and chuck it into an ElementHost. However, I'd rather avoid that if I can.
Current code (roughly)
public void DoOpenWindow(Office.IRibbonControl button)
{
var myWindow = new myWindow();
// This hasn't addressed the issue, though may be sensible to include:
//ElementHost.EnableModelessKeyboardInterop(myWindow);
// This *also* didn't work, and essentially set my window to
// be always on top of Excel
//var hwSrc = HwndSource.FromVisual(myWindow );
//var ownerHelper = new WindowInteropHelper(myWindow );
//ownerHelper.Owner = (IntPtr)Globals.ThisAddIn.Application.Hwnd;
// with ShowDialog() this works fine...
myWindow .Show();
}
Current thoughts are:
I'm getting window messages from Excel forwarded to myWindow, some of which it isn't expecting.
Excel is intercepting messages meant for my window (keyboard and mouse), which is probably what ElementHost.EnableModelessKeyboardInterop(myWindow) is intended to solve (but either I'm using it wrong, or it's not the whole solution).

Redraw WPF UI when minimized

I have a C# WPF application that is software captured using Open Broadcaster Software. When the application is in the foreground or even hidden by another application, it works just fine. However, if it is actually minimized, it stps updating. This makes sense, because usually, why would you need to redraw it if nobody can see it, but this is not true for this case. Is it possible to somehow force the UI to redraw? It would be possible to call a certain method every time a UI update is needed, because the code where I would need the UI to update/redraw looks something like this (simplified):
private void displaySomething(int something)
{
someRectangle.Fill = new SolidColorBrush(...);
// Redraw UI here
}
EDIT: "Update" might have been a misleading term here. "Redraw" may be better.
So if it works even the form is behind the other form, then do not let user to Minimize the form. Set yourWindow.ResizeMode = ResizeMode.NoResize;

How can you tell if you're on the Main UI thread? (In CF)

Now unfortunately due to the fact that WinCE Usb Device Arrival / Removal exposes itself via WindowsMessages I have to ensure that a certain (non-UI) component is not created on a background thread. I would like to assert this via an exception but am lacking the code for the assertion.
This component creates a MessageWindow* and uses it to receive usb arrived/removed messages. The issue is if someone creates this component on a background thread (not necessarily; IsBackground = true) when the thread exits the window will be destroyed.
Any ideas?
*as an aside I still don't know why Form doesn't inherit from this class
Update
I think my version 1 wasn't very clear. So this is v2.
When you create a MessageWindow or a Form for that matter on a thread, when that thread exits the Window/Form is destroyed.
My component is creating a "hidden" message window to intercept some important events, ergo I do not wish for it to be destroyed. Therefore I must somehow ensure that the code that creates the form is running on the "Main UI" thread.
If possible i'd like to avoid passing down a reference to the "main" form to this component as it is (architecturally speaking) supposed to be miles away from the UI.
Update
Moving logging question to a separate Q.
Ok, I understand that you don't want for your component to "know" about the main window -- makes sense.
How about this: How about if you make sure that you always instance your component on the main thread? You component will create it's listener window on the constructor's thread.
If you do that, then you just need to make sure that you call the constructor from the main thread. I'm making some assumptions about your code, but I'm guessing that you must have some class in your architecture that knows about both the UI and your component. Create your component there, using a callback, and the main form's InvokeRequired/Invoke methods.
In forms, you use the InvokeRequired property.
Why not create the non-UI component on a background thread and when you go to update any UI component just look to see if invokeRequired then get back on the main thread to actually do the update.
You should have nothing really tying up the main event thread, IMO.
You can use it in this way:
void MyCallback()
{
if (form1.InvokeRequired) { // form1 is any existing gui control
form1.Invoke(new Action<>(MyCallBack));
return;
}
// your logic here
}
Hey there: I had an idea about your problem. This is just a random thought, and I don't know for sure whether it will work (I have not tested, nor even compiled this -- it just hit me):
What if you get the window handle of the main window of your app, then build a Control around it (I'm assuming that you have a gdi-based app, like Winforms)?
this code might not compile, but it's close (it would go into your component -- note that it would make your component require a gdi windows/winform app, as opposed to a console or WPF app).
If you do try it, I'd love to hear whether it worked for you.
using System.Diagnostics;
using System.Windows.Forms;
void Init()
{
// get handle to the main window
intPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
Control mainWindow = Control.FromHandle(mainWindowHandle);
if(mainWindow.InvokeRequired)
mainWindow.Invoke(SetupMessageWindow);
else
SetupMessageWindow();
}
void SetupMessageWindow()
{
// do your thing...
}

Load a form without showing it

Short version: I want to trigger the Form_Load() event without making the form visible. This doesn't work because Show() ignores the current value of the Visible property:
tasksForm.Visible = false;
tasksForm.Show();
Long version: I have a WinForms application with two forms: main and tasks. The main form is always displayed. The user can either click a button to open the tasks form, or click some buttons that just run a task directly without opening the tasks form.
When a user asks to run a task directly, I'd like to just call some public methods on the tasks form without showing it. Unfortunately, the task logic depends on stuff that happens in the Form_Load() event. The only way I can find to trigger Form_Load() is to call Show(). The best I've been able to do is to show the form in the minimized state:
tasksForm.WindowState = FormWindowState.Minimized;
tasksForm.Show();
I suppose the cleanest solution would be to pull the tasks logic out of the tasks form and into a controller class. Then I can use that class from the main form and from the tasks form, and only load the tasks form when I need it visible for the user. However, if it's an easy thing to load the form without displaying it, that would be a smaller change.
Perhaps it should be noted here that you can cause the form's window to be created without showing the form. I think there could be legitimate situations for wanting to do this.
Anyway, good design or not, you can do that like this:
MyForm f = new MyForm();
IntPtr dummy = f.Handle; // forces the form Control to be created
I don't think this will cause Form_Load() to be called, but you will be able to call f.Invoke() at this point (which is what I was trying to do when I stumbled upon this SO question).
It sounds to me like you need to sit down and re-think your approach here. I cannot imagine a single reason your public methods need to be in a form if you are not going to show it. Just make a new class.
I totally agree with Rich B, you need to look at where you are placing your application logic rather than trying to cludge the WinForms mechanisms. All of those operations and data that your Tasks form is exposing should really be in a separate class say some kind of Application Controller or something held by your main form and then used by your tasks form to read and display data when needed but doesn't need a form to be instantiated to exist.
It probably seems a pain to rework it, but you'll be improving the structure of the app and making it more maintainable etc.
From MSDN:
Form.Load
Occurs before a form is displayed for the first time.
Meaning the only thing that would cause the form to load, is when it is displayed.
Form.Show(); and Form.Visible = true; are the exact same thing. Basically, behind the scenes, Show checks for various conditions, then sets Visible to true. So obviously, setting visible to false (which it already is) before showing the form is meaningless.
But let's forget the technicalities. I completely agree with Rich B and Shaun Austin - the logic shouldn't be in that form anyway.
Sometimes this would be useful without it being bad design. Sometimes it could be the start of a migration from native to managed.
If you were migrating a c++ app to .NET for example, you may simply make yourwhole app a child window of the .NET form or panel, and gradually migrate over to the .NET by getting rid of your c++ app menu, status bar, toolbar and mapping teh .NEt ones to your app using platform invoke etc...
Your C++ app may take a while to load, but the .NET form doesn't..in which you may like to hide the .NEt form until your c++ app has initialised itself.
I'd set opacity=0 and visible=false to false after calling show, then when your c++ app loads, then reverse.
If you make the method public, then you could access it directly.... however, there could be some unexpected side effects when you call it. But making it public and calling it directly will not draw the screen or open the form.
Move mandatory initialization code for the form class out of the Load event handler into the constructor. For a Form class, instantiation of an instance (via the constructor), form loading and form visibility are three different things, and don't need to happen at the same time (although they do obviously need to happen in that order).
None of the answers solved the original question, so, add the below, call .Show() to load the form without showing it, then call .ShowForm() to allow it to be visible if you want to after:
private volatile bool _formVisible;
protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(_formVisible);
}
public void ShowForm()
{
_formVisible = true;
if (InvokeRequired)
{
Invoke((Action) Show);
}
else
{
Show();
}
}

Categories