0

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

brecht
  • 41
  • 2
  • 7
  • What baffles me is that such an error could happen when casting the result of the call to get_DirectDispatch, not when calling PtrToStructure. Are you sure of the line causing the error ? – Timores Mar 18 '10 at 13:22
  • I am reaylly sure about this. In another function i am sending the message, and via the dll-functions catching the notify and getting the message. While debugging the send()-function, i can perfectly access the canreceiver-variable and see the value of the RawMessage, but not while i'm in the Message()-function (-> this is the implementation of the COM-interface. – brecht Mar 18 '10 at 14:06

1 Answers1

0

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
  • 7,438
  • 1
  • 28
  • 41
  • 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 Mar 19 '10 at 15:59
  • 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 Mar 23 '10 at 09:36
  • 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 Mar 23 '10 at 09:38
  • 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 Mar 25 '10 at 12:12
  • 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 Mar 26 '10 at 15:26