lepoco / wpfui

WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.
https://wpfui.lepo.co
MIT License
7.72k stars 755 forks source link

Managed Debugging Assistant 'PInvokeStackImbalance' : 'A call to PInvoke function 'Wpf.Ui!Wpf.Ui.Interop.User32::SetWindowLong' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.' #738

Open MickaelBNeron opened 1 year ago

MickaelBNeron commented 1 year ago

Describe the bug

I have this incredibly simple code that throws an exception:

using WpfUi = Wpf.Ui;

namespace Redacted.Replay.Gui.Wpf.ViewModels
{
    public class PatientViewModel
    {
        public void ShowError()
        {
           new WpfUi.Controls.MessageBox().Show();
        }
    }
}

The exception:

Managed Debugging Assistant 'PInvokeStackImbalance' : 'A call to PInvoke function 'Wpf.Ui!Wpf.Ui.Interop.User32::SetWindowLong' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.'

Interestingly, I didn't throw any exception initially. Not sure what change made this error appear.

To Reproduce

Create project Redacted.Replay.Gui.Wpf. Add following code:

using WpfUi = Wpf.Ui;

namespace Redacted.Replay.Gui.Wpf.ViewModels
{
    public class PatientViewModel
    {
        public void ShowError()
        {
           new WpfUi.Controls.MessageBox().Show();
        }
    }
}

Call new PatientViewModel().ShowError();

Expected behavior

No error. The MessageBox should display.

Screenshots

No response

OS version

Windows 11.

.NET version

4.8.1

WPF-UI NuGet version

2.1.0

Additional context

For what it's worth, both WPF UI and my project have Wpf in the namespace. Changing .Show() to .ShowDialog() works properly.

syntax-tm commented 1 year ago

The WPF UI MessageBox control is actually a WPF Window which is why it has both the Show and ShowDialog functions. Looking at MessageBox.cs the Show and ShowDialog functions both throw an InvalidOperationException saying to use the ShowDialogAsync function.

[Obsolete($"Use {nameof(ShowDialogAsync)} instead")]
public new void Show()
{
    throw new InvalidOperationException($"Use {nameof(ShowDialogAsync)} instead");
}

[Obsolete($"Use {nameof(ShowDialogAsync)} instead")]
public new bool? ShowDialog()
{
    throw new InvalidOperationException($"Use {nameof(ShowDialogAsync)} instead");
}

The Win32 MessageBox function in winuser.h is a modal dialog that is shown activated and blocks other Windows from activating within the application until it is closed and a value is returned. That behavior is the difference between Show and ShowDialog (or in this case ShowDialogAsync).

Both the System.Windows.MessageBox and System.Windows.Forms.MessageBox classes are just wrappers for the native Win32 MessageBox functions (source). The reason why those MessageBox classes only have the Show function is because they can only ever be a modal dialog. Unlike a Window which can be modeless when using Show or modal when using ShowDialog.

Basically, use ShowDialogAsync. If you're on an older version and ShowDialog is not yet deprecated then use that, but either way don't use Show since a MessageBox is not a modeless dialog.

MickaelBNeron commented 1 year ago

Right, I was using 2.1.0. Thanks for the info.

On Sat., Aug. 26, 2023, 5:32 p.m. Trey, @.***> wrote:

The WPF UI MessageBox control is actually a WPF Window which is why it has both the Show and ShowDialog functions. Looking at MessageBox.cs the Show and ShowDialog functions both throw an InvalidOperationException saying to use the ShowDialogAsync function.

[Obsolete($"Use {nameof(ShowDialogAsync)} instead")]public new void Show(){ throw new InvalidOperationException($"Use {nameof(ShowDialogAsync)} instead");} [Obsolete($"Use {nameof(ShowDialogAsync)} instead")]public new bool? ShowDialog(){ throw new InvalidOperationException($"Use {nameof(ShowDialogAsync)} instead");}

The Win32 MessageBox https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox function in winuser.h is a modal dialog that is shown activated and blocks other Windows from activating within the application until it is closed and a value is returned. That behavior is the difference between Show and ShowDialog (or in this case ShowDialogAsync).

Both the System.Windows.MessageBox and System.Windows.Forms.MessageBox classes are just wrappers for the native Win32 MessageBox functions ( source https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/MessageBox.cs#L399C67-L399C67). The reason why those MessageBox classes only have the Show function is because they can only ever be a modal dialog. Unlike a Window which can be modeless when using Show or modal when using ShowDialog.

Basically, use ShowDialogAsync. If you're on an older version and ShowDialog is not yet deprecated then use that, but either way don't use Show since a MessageBox is not a modeless dialog.

— Reply to this email directly, view it on GitHub https://github.com/lepoco/wpfui/issues/738#issuecomment-1694253066, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMBJ6D2X6AFIFVE73MDTGWDXXHGEDANCNFSM6AAAAAA32Z2ZDU . You are receiving this because you authored the thread.Message ID: @.***>