Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The application called an interface that was marshalled for a different thread

Tags:

c#

.net

com

wmi

I'm using WMI for querying devices. I need to update UI when new device is inserted or removed (in order to keep list of devices up to date).

private void LoadDevices()
{
    using (ManagementClass devices = new ManagementClass("Win32_Diskdrive"))
    {
        foreach (ManagementObject mgmtObject in devices.GetInstances())
        {
            foreach (ManagementObject partitionObject in mgmtObject.GetRelated("Win32_DiskPartition"))
            {
                foreach (ManagementBaseObject diskObject in partitionObject.GetRelated("Win32_LogicalDisk"))
                {
                    trvDevices.Nodes.Add( ... );
                }
            }
        }
    }
}

protected override void WndProc(ref Message m)
{
    const int WM_DEVICECHANGE = 0x0219;
    const int DBT_DEVICEARRIVAL = 0x8000;
    const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
    switch (m.Msg)
    {
        // Handle device change events sent to the window
        case WM_DEVICECHANGE:
            // Check whether this is device insertion or removal event
            if (
                (int)m.WParam == DBT_DEVICEARRIVAL ||
                (int)m.WParam == DBT_DEVICEREMOVECOMPLETE)
        {
            LoadDevices();
        }

        break;
    }

    // Call base window message handler
    base.WndProc(ref m);
}

This code throws exception with the following text

The application called an interface that was marshalled for a different thread.

I put

MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());

in the beginning of LoadDevices method and I see that it is always called from the same thread (1). Could you please explain what is going on here and how to get rid of this error?

like image 495
axe Avatar asked Nov 03 '22 17:11

axe


1 Answers

Finally I solved it using new thread. I split this method, so now I have GetDiskDevices() and LoadDevices(List<Device>) methods and I have InvokeLoadDevices() method.

private void InvokeLoadDevices()
{
    // Get list of physical and logical devices
    List<PhysicalDevice> devices = GetDiskDevices();

    // Check if calling this method is not thread safe and calling Invoke is required
    if (trvDevices.InvokeRequired)
    {
        trvDevices.Invoke((MethodInvoker)(() => LoadDevices(devices)));
    }
    else
    {
        LoadDevices(devices);
    }
}

When I get either of DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE messages I call

ThreadPool.QueueUserWorkItem(s => InvokeLoadDevices());

Thanks.

like image 77
axe Avatar answered Nov 14 '22 23:11

axe