views:

273

answers:

1

I am creating a tool in c# to retrieve messages of a CAN-network (network in a car) using an Dll written in C/C++. This dll is usable as a COM-interface.

My c#-formclass implements one of these COM-interfaces. And other variables are instantiated using these COM-interfaces (everything works perfect).

The problem: The interface my C#-form implements has 3 abstract functions. One of these functions is called -by the dll- and i need to implement it myself. In this function i wish to retrieve a property of a form-wide variable that is of a COM-type.

The COM library is CANSUPPORTLib

The form-wide variable:

private CANSUPPORTLib.ICanIOEx devices = new CANSUPPORTLib.CanIO();

This variable is also form-wide and is retrieved via the devices-variable:

canreceiver = (CANSUPPORTLib.IDirectCAN2)devices.get_DirectDispatch(receiverLogicalChannel);

The function that is called by the dll and implemented in c#

    public void Message(double dTimeStamp)
    {
        Console.WriteLine("!!! message ontvangen !!!" + Environment.NewLine);

        try
        {
            CANSUPPORTLib.can_msg_tag message = new CANSUPPORTLib.can_msg_tag();
            message = (CANSUPPORTLib.can_msg_tag)System.Runtime.InteropServices.Marshal.PtrToStructure(canreceiver.RawMessage, message.GetType());
            for (int i = 0; i < message.data.Length; i++)
            {
                Console.WriteLine("byte " + i + ": " + message.data[i]);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

The error rises at this line:

message = (CANSUPPORTLib.can_msg_tag)System.Runtime.InteropServices.Marshal.PtrToStructure(canreceiver.RawMessage, message.GetType());

Error:

Unable to cast COM object of type 'System.__ComObject' to interface type CANSUPPORTLib.IDirectCAN2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{33373EFC-DB42-48C4-A719-3730B7F228B5}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Notes: It is possible to have a timer-clock that checks every 100ms for the message i need. The message is then retrieved in the exact same way as i do now. This timer is started when the form starts. The checking is only done when Message(double) has put a variable to true (a message arrived).

When the timer-clock is started in the Message function, i have the same error as above

Starting another thread when the form starts, is also not possible.

Is there someone with experience with COM-interop ?

When this timer

A: 

I wonder if Message is called on a thread different from the one that created the canreceiver.

Do you know the threading model of CANSUPPORTLib.CanIO? If it's apartment-threaded, you may need to marshal a reference from the main UI thread to the thread called by Message somehow.

Alternatively, assuming you can change the source code of the C++ dll, and depending on your other threading requirements and constraints, you could change it to be free-threaded, in which case an object can be simultaneously accessed from multiple threads without marshalling.

Kim Gräsman
Yes, it is a different thread i noticed. When i print the thread-id in message() and in sendmessage(), it's different. I can see the dll-source, but i am not allowed to change the code.The fact is: what i am implementing now, is needed to be implemented in c++. Will i have the same problem ?
brecht
Yes, depending on the threading model you choose for the C++ implementation. The threading model dictates how the COM runtime protects the object from concurrent access.I think the default threading model for a WinForms app is Apartment (governed by the `[STAThread]` attribute on the main method) -- this means that only one thread at a time can call any one object in the same apartment, e.g. your form.
Kim Gräsman
So, either you need to marshal the interface pointer from the main thread to the worker thread before using it, or you need to get the C++ implementation to have the same threading model as the C# code.Unfortunately I can't find any info on marshalling interface pointers in managed code.A simpler solution might be to have the C++ code pass its `this` pointer as an argument to `Message` at which point the COM runtime will have to sort out the marshalling for you. Since a marshalled interface pointer is sent to the method, you don't need to access `canreceiver` from the callback.
Kim Gräsman
True, but the problem is, the c++ code (a dll) is not to be changed. It is supplied as-is. Another optioun would be to make it possible for another thread to access 'canreceiver'. I put [MTAThread] for the function that runs my form, it changed nothing, should it change something?
brecht
Not necessarily -- if the COM object is not FreeThreaded, it makes no difference.Have you tried moving the devices.get_DirectDispatch call into the Message method?
Kim Gräsman