Search code examples
c#formsshowvisible

Form does not work unless .Show() is called


I have a form that represents a USB device Terminal that has been giving me some errors. After half a day of debugging strange errors with no known source I somehow found out that the Terminal does not function when it is instantiated but not shown. When I change the code and add usbTerminal.Show();, then it works properly.

USBTerminal usbTouchTerminal;

public MainForm()
{

InitializeComponent();

USBSettings usbTouchSettings = new USBSettings();
usbTouchTerminal = new USBTerminal(usbTouchSettings);           //Create Terminal with settings
usbTouchTerminal.StartUSB();                                    
usbTouchTerminal.Show();            //works ONLY when show is here

}

How is this possible and why? I've done a massive search and none of my code depends on the .Visible property on either my Terminal or main form?

I'm completely baffled on why some form would not work if it isn't shown. MSDN or google wasn't really a help either. I was certain it would function properly when instantiated but not shown.

PS. I added

usbTerminal.Show();
usbTerminal.Hide();

and the Terminal functioned correctly.

Thank you for any help!

EDIT:

I should also note that this usbTerminal uses the WndProc override. I'm not an expert on that, but I feel that it may have something to do with it.

I should note that this is LibUSBdotnet

    public class USBSettings
{
    /// <summary>
    /// This is the Vender ID Number. (0x0B6A)
    /// </summary>
    public ushort VID { get; set; }

    /// <summary>
    /// This is the Product ID Number. (0x5346)
    /// </summary>
    public ushort PID { get; set; }

    /// <summary>
    /// This is the optional Serial Name. ("")
    /// </summary>
    public string SerialName { get; set; }

    /// <summary>
    /// This is the Reader USB Endpoint. (ReadEndpointID.Ep02)
    /// </summary>
    public ReadEndpointID ReaderEndpoint { get; set; }

    /// <summary>
    /// This is the Writer USB Endpoint. (WriteEndpointID.Ep01)
    /// </summary>
    public WriteEndpointID WriterEndpoint { get; set; }

    /// <summary>
    /// This is the Registry Key for USB settings. ("SOFTWARE\\DEFAULT\\USBPROPERTIES")
    /// </summary>
    public string SubKey { get; set; }

    /// <summary>
    /// This is the default read buffer size for the USB Device.
    /// </summary>
    public int ReadBufferSize { get; set; }

    /// <summary>
    /// This constructor houses default values for all properties.
    /// </summary>
    public USBSettings()
    {
        VID = 0x0B6A;
        PID = 0x5346;
        SerialName = "";
        ReaderEndpoint = ReadEndpointID.Ep02;
        WriterEndpoint = WriteEndpointID.Ep01;
        SubKey = "SOFTWARE\\DEFAULT\\USBPROPERTIES";
        ReadBufferSize = 100;
    }

}

Solution

  • The question is poorly documented but this is fairly normal for code that works with devices. They tend to need to know about Plug & Play events and that requires a top-level window to be created that receives the WM_DEVICECHANGE notification message. Creating a .NET Form object isn't enough, you also have to create the native window for it. Which, in typical .NET lazy fashion, happens at the last possible moment, when you force the window to be visible. Either by calling the Show() method or setting the Visible property to true. The window doesn't actually have to be visible to get the Plug & Play notifications.

    You can get the window created without also making it visible. That requires modifying the USBTerminal class. Paste this code:

        protected override void SetVisibleCore(bool value) {
            if (!this.IsHandleCreated) {
                this.CreateHandle();
                value = false;
            }
            base.SetVisibleCore(value);
        }
    

    And call the Show() method as normal. Beware that the Load event won't fire until the window actually becomes visible so if necessary move any code in the event handler to this method. If this is not the primary window for the app, in other words not the one that's passed to Application.Run() in your Main() method, then you can make do with simply calling this.CreateHandle() as the last statement in the form constructor. In which case calling Show() is no longer necessary.