Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

[Windows Forms] Form.Close crash application #7994

Closed
kant2002 opened this issue Feb 18, 2020 · 8 comments · Fixed by #8128
Closed

[Windows Forms] Form.Close crash application #7994

kant2002 opened this issue Feb 18, 2020 · 8 comments · Fixed by #8128

Comments

@kant2002
Copy link
Contributor

If I show Form using Form.Show or Form.ShowDialog then after close form, application crashed.

@jkotas
Copy link
Member

jkotas commented Feb 18, 2020

Could you please share the code to reproduce this crash (e.g. a scratch github repo that one can just run dotnet publish -c Release -r win-x64 to get a repro), and stacktrace of the crash that you see?

@kant2002
Copy link
Contributor Author

Sample as simple as application which create new form. and I manually close that form. I have slightly more complicated example, and this is exception message which I manage to capture.

Method '[System.Windows.Forms]System.Windows.Forms.UnsafeNativeMethods.UiaReturnRawElementProvider(HandleRef,native int,native int,IRawElementProviderSimple)' requires marshalling that is not yet supported by this compiler.

It seems to be COM related ☹️ . See https://github.com/kant2002/CoreRTWinFormsTestBed for application where I experience issue. Press "Press Me" button. Close opened form.

@MichalStrehovsky
Copy link
Member

Can you check if there's any exceptions being thrown before going down that paths? (Here's the doc on how to set up breakpoints on exceptions just in case, but I guess you know about it).

From a quick look at WinForms source, this is already related to the error window, so I wonder if this is just WinForms trying to pop a dialog because something before that went wrong.

@kant2002
Copy link
Contributor Author

Before that was System.Drawing.NativeMethods+BITMAPINFOHEADER is missing structure marshalling data. To enable structure marshalling data, add a MarshalStructure directive to the application rd.xml file. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=393965 but its happens during startup of the application and once form loaded, everything working fine. After these exceptions I press button, and again everything is fine. Only on Form close exception System.Windows.Forms.UnsafeNativeMethods.UiaReturnRawElementProvider happens

@MichalStrehovsky
Copy link
Member

Callstack:

>	WinForms.exe!System_Windows_Forms_System_Windows_Forms_UnsafeNativeMethods__UiaReturnRawElementProvider�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Control__ReleaseUiaProvider�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Control__WmDestroy�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Control__WndProc�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_ScrollableControl__WndProc�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_ContainerControl__WndProc�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Form__WndProc�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Control_ControlNativeWindow__OnMessage�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_Control_ControlNativeWindow__WndProc�()	Unknown
 	WinForms.exe!System_Windows_Forms_System_Windows_Forms_NativeWindow__Callback�()	Unknown

We are actually in luck: the COM parameter is a null reference. We can marshal a null reference. It marshals into a null pointer.

Adding a COM marshaller that can only marshal a null (and throws for everything else) shouldn't be too difficult.

@kant2002
Copy link
Contributor Author

kant2002 commented Mar 5, 2020

I try to debug this issue, to better understand how CoreRT make it's magic and have a set of questions

  • If I properly understand error which I seeing coming because PInvokeILEmitter cannot emit methods which has MethodImplAttributes.PreserveSig.
  • This part of runtime is not well known for me, but I always thought that this attribute flag not only for COM interop (I may be wrong here).

Next set of questions coming from my attempt to understand magic.
Quick glance on the PInvokeILEmitter lead me to thinking that you are make PInvoke magic by emitting IL.

  • Can I assume that this is some limited subset of IL ?
  • Can I assume that this IL will be lately transformed to native code?
  • Does this mean that all marshalling in CoreRT is just custom IL code which follow the rules of runtime as explained in docs.

@MichalStrehovsky
Copy link
Member

If I properly understand error which I seeing coming because PInvokeILEmitter cannot emit methods which has MethodImplAttributes.PreserveSig.

PreserveSig is one of the things CoreRT doesn't support. This is a pretty rare flag and mostly matters in COM. I don't think it's a problem for the Forms.Close crash.

I'm pretty sure that in this case, the compiler is actually throwing because we can't determine the marshaller kind to marshal an interface type (we take this return statement because none of the if above match an interface).

Yes, P/invoke is done by emitting IL - it's not a limited subset: all IL is available. The IL is then passed to the native compiler (e.g. RyuJIT) to compile into native code.

It works along the lines of this:

[DllImport("foo", CharSet = Unicode)]
extern static void Greet(string name);

This is converted (within the compiler) into what logically looks like this:

[DllImport("foo", CharSet = Unicode, EntryPoint="Greet")]
extern static void rawPinvoke_Greet(char* name);

static void Greet(string name)
{
    fixed (char* pName = name)
        rawPinvoke_Greet(pName);
}

Basically, the marshalling IL generator goes over all parameters/return types, converts them into the representation expected by the native side, and marshals them forward (managed to native) or backward (native to managed, if it's a return value).

What's missing here is the handling for interfaces. We'll only be able to handle the null value of an interface. The rest is very complex.

Basically we'll want to convert this:

[DllImport("foo", CharSet = Unicode)]
extern static void Greet(IFooer nameProvider);

Into:

[DllImport("foo", CharSet = Unicode, EntryPoint="Greet")]
extern static void rawPinvoke_Greet(IntPtr nameProvider);

static void Greet(IFooer nameProvider)
{
    if (nameProvider != null)
        throw new NotSupportedException();
    rawPinvoke_Greet(null);
}

@jkotas
Copy link
Member

jkotas commented Mar 18, 2020

If dotnet/runtime#33485 goes through, we are going to have an official API to inject prebuilt COM interop into the app. It can be then used to supply the required COM interop needed by WinForms.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants