axe
axe

Reputation: 2371

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

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?

Upvotes: 5

Views: 4053

Answers (2)

Juan Pablo Gomez
Juan Pablo Gomez

Reputation: 5534

For UWP on w10 you could use:

public async SomeMethod()
{
    await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
             //   Your code here...
        });
}

Upvotes: 0

axe
axe

Reputation: 2371

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.

Upvotes: 2

Related Questions