An issue with threads

Aug 14, 2009 at 11:30 AM

Hi.

First of all - a big thank you. Your work has helped me tremendously.

I need to copy files to the device and use CopyFileToDevice. This worked fine until I started using it in a BackgroundWorker thread. The same code now throws a COMException  - Interface not registered (Exception from HRESULT: 0x80040145). Any thoughts about that?

A second issue is using the dll in an application compiled for .net 3.5. The extension attribute you've put in RemoteDevices.cs causes a warning because the extension attribute is defined twice. This code needs to be removed (and rebuilt of course) if one is working with 3.5.

Anyway, thanks again.

Vadim.

Coordinator
Aug 16, 2009 at 5:58 AM

On your first issue, the only thing I can think is if you have created your instance of RemoteDeviceManager outside the thread you might have problems. Other than that, your guess is a good as mine.

On your second issue, if you will change the access modifier for the ExtensionAttribute class (in RemoteDevice.cs) from public to internal, the problem will go away. If you you just include the files in your project (not the dll) then you will have to remove that class entirely.

Nov 24, 2009 at 11:42 PM

I can confirm that creating the RemoteDeviceManager instance from the thead where you call CopyFileToDevice works, which means that you can't use BackgroundWorker, since it uses threadpool threads. You'll need to create a Thread instance and manage it from there.

This does seem to be odd behavior, though, since it appears that the RAPI COM object is free threaded...

Feb 10, 2010 at 12:27 PM

I just ran in a related problem:

 

If you hook up a device while your application is running the IRAPIDevice COM instance is created in a MTA thread.

If the device is hooked up when you start your application and try to access the device the IRAPIDevice instance is created in a STA thread as the .NET main thread by default is in the STA (check Program.cs).

 

Later on my application tried to retrieve an instance of IRAPISession by calling IRAPIDevice::CreateSession in the main thread which happened to be in the STA.

This call returns an IRAPISession. That works fine as long as the IRAPIDevice was created in a STA thread. Because as the main thread is in the STA there is no need to marshal interface pointers.

 

A problem occured when I plugged in the device during runtime. As explained IRAPIDevice then was created in an MTA thread.

When I then tried to execute IRAPIDevice::CreateSession in the main (STA) thread I got a COMException "interface is not registered". The reason seems to be that 

the GUID of IRAPISession is not registered in the system (just check your registry and look for that GUID). But it has to marshal the IRAPISession pointer from the MTA to the STA. That does not work without an interface definiton. => boom.

 

However, you can access the IRAPIDevice instance in the STA thread as an interface definition is available and apartment marshalling is possible.

 

 

=> I suggest only to access all RAPI objects in MTA threads (wrap your RAPI2 wrapper with some Begin/End Invoke calls).

 

Hope that helps.

 

Best Regards,

Kevin

 

 

Coordinator
Mar 1, 2011 at 5:16 AM

Please see [discussion:243560] for an example of how to use RemoteDeviceManager with threads.

Developer
Mar 1, 2011 at 12:31 PM

The thread the COM object is created with needs to live during the life of the COM object. If your thread terminates, the COM object is no longer valid.

Programming under Winforms, use the BackgroundWorker component. Any threading model is fine.

Here's an example. Create a form with a label control called DeviceLabel and add a BackgroubdWorker component. Drop this code in and note it plays well with COM. Also note that sometimes an ActiveSync connections takes a second or two to return good results.

        delegate void SetTextThreadSafeCallback(string Text, Color Color);
        private void SetDeviceLabel(string Text, Color Color)
        {
            if (this.InvokeRequired)
            {
                SetTextThreadSafeCallback d = new SetTextThreadSafeCallback(SetDeviceLabel);
                this.Invoke(d, new object[] { Text, Color });
            }
            else
            {
                this.DeviceLabel.Text = Text;
                this.DeviceLabel.BackColor = Color;
            }
        }


        RemoteDeviceManager DeviceManager = new RemoteDeviceManager();
        RemoteDevice Device = null;
        private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            DeviceManager.DeviceConnected += new System.EventHandler(this.DeviceManager_DeviceConnected);
            DeviceManager.DeviceDisconnected += new System.EventHandler(this.DeviceManager_DeviceDisconnected);

            while (true)
            {
                try
                {
                    if (Device != null)
                    {
                        Device = DeviceManager.Devices.FirstConnectedDevice;
                        SetDeviceLabel(Device.OSVersion.ToString(), Device.OSVersion.ToString().Equals("0.0.0.0") ? Color.Red : Color.Chartreuse);
                    }
                    else
                        SetDeviceLabel("Not Connected", SystemColors.Control);
                }
                catch (System.Exception ex)
                {
                    SetDeviceLabel(ex.Message, Color.Yellow);
                }
                Thread.Sleep(200);
            }
        }

        private void DeviceManager_DeviceConnected(object sender, System.EventArgs e)
        {
            Device = DeviceManager.Devices.FirstConnectedDevice;
        }

        private void DeviceManager_DeviceDisconnected(object sender, System.EventArgs e)
        {
            Device = null;
        }