7

I have an industrial computer with some Digital I/O pins. The manufacturer provides some C++ libraries and examples to handle pin status change.

I need to integrate this events onto a C# application. AFAIK the most simple way to perform this is:

  1. Make a managed C++/CLI wrapper for the manufacturer libraries that fires events when interruptions are issued from the DIO pins.
  2. Reference that wrapper and handle the events in the C# part as it they were normal C# events.

I have tried to make this work with some mock objects with no luck. From the docs, the function EventHandler should do most of the "dirty work" in my case. Following info available in old threads and the EventHandler example in the MSDN docs I ended up with this test code:

C++/CLI

using namespace System;

public ref class ThresholdReachedEventArgs : public EventArgs
{
public:
    property int Threshold;
    property DateTime TimeReached;
};

public ref class CppCounter
{
private:
    int threshold;
    int total;

public:
    CppCounter() {};

    CppCounter(int passedThreshold)
    {
        threshold = passedThreshold;
    }

    void Add(int x)
    {
        total += x;
        if (total >= threshold) {
            ThresholdReachedEventArgs^ args = gcnew ThresholdReachedEventArgs();
            args->Threshold = threshold;
            args->TimeReached = DateTime::Now;
            OnThresholdReached(args);
        }
    }

    event EventHandler<ThresholdReachedEventArgs^>^ ThresholdReached;

protected:
    virtual void OnThresholdReached(ThresholdReachedEventArgs^ e)
    {
        ThresholdReached(this, e);
    }
};

public ref class SampleHandler
{
public:
    static void c_ThresholdReached(Object^ sender, ThresholdReachedEventArgs^ e)
    {
        Console::WriteLine("The threshold of {0} was reached at {1}.",
            e->Threshold, e->TimeReached);
        Environment::Exit(0);
    }
};

void main()
{
    return;
    CppCounter^ c = gcnew CppCounter(20);
    c->ThresholdReached += gcnew EventHandler<ThresholdReachedEventArgs^>(SampleHandler::c_ThresholdReached);

    Console::WriteLine("press 'a' key to increase total");
    while (Console::ReadKey(true).KeyChar == 'a') {
        Console::WriteLine("adding one");
        c->Add(1);
    }
}

C#

using System;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        CppCounter cc = new CppCounter(5);
        //cc.ThresholdReached += cs_ThresholdReached; //<--This is the offending line


        Console.WriteLine("press 'a' key to increase total");
        while (Console.ReadKey(true).KeyChar == 'a')
        {
            Console.WriteLine("adding one");
            cc.Add(1);
        }
    }

    static void cs_ThresholdReached(object sender, ThresholdReachedEventArgs e)
    {
        Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
        Environment.Exit(0);
    }
}

class Counter
{
    private int threshold;
    private int total;

    public Counter(int passedThreshold)
    {
        threshold = passedThreshold;
    }

    public void Add(int x)
    {
        total += x;
        if (total >= threshold)
        {
            ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
            args.Threshold = threshold;
            args.TimeReached = DateTime.Now;
            OnThresholdReached(args);
        }
    }

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}

  public class ThresholdReachedEventArgs : EventArgs
    {
        public int Threshold { get; set; }
        public DateTime TimeReached { get; set; }
    }
}

What am I doing wrong? Is it something I am missing?

Igb
  • 227
  • 2
  • 10
  • 1
    I'll make a very important question: do you really need C++/CLI? Can't you do in C#? From experience, C++/CLI is a pain unless you know exactly what you are doing. And there is not enough use around to build a body of knowledge. So often you are working by trial. – xanatos Jun 04 '18 at 07:27
  • At first glance, your code looks correct. What error do you get if you enable `This is the offending line`? – David Yaw Jun 04 '18 at 07:32
  • I've run your code and it works. I've decommented the `//<--This is the offending line` – xanatos Jun 04 '18 at 07:34
  • It is not neccesary to use C++/CLI in particular, but I think is the "less hard" approach. The libraries that handle DIO pins are written in C++ and they do not have its source code available. The application in c# that should receive events is quite big to rewrite from scratch in another language. – Igb Jun 04 '18 at 07:35
  • 2
    @lgb Are the libraries dll or lib? If they are dll, and have a C interface then you could try to use them directly from C#. Rarely a dll has a C++ interface, because there are compatibility problems in doing it. If you have a .lib to link (rare too) and no dll, then C++/CLI is the only solution. The only problem is that you'll have to convert the .h files to C#... but depending on how many methods there are and how many methods you really use, it could be feasible. – xanatos Jun 04 '18 at 07:36
  • The error I get when uncommenting that line is (roughly translated to english): No overload corresponding to "cs_ThresholdReached" matches delegate "EventHandler". Maybe I did not Reference the c++ project correctly? – Igb Jun 04 '18 at 07:41
  • @xanatos the libraries are in .lib format. The header file is relatively short, since this library's only task is to interact with DIO. – Igb Jun 04 '18 at 07:52
  • @lgb Then you have to use C++/CLI. I pity you :-) – xanatos Jun 04 '18 at 08:09
  • @lgb Probably you didn't reference it correctly, or you didn't recompile the C++/CLI project. – xanatos Jun 04 '18 at 08:10
  • @xanatos Thank you. Still did not manage to compile my code though. I have the c# and c++ projects on a solution, and added the cpp project as a reference to the c# one. Any more steps needed? Making a .dll with the cpp part and using dllimport seem to me like killing a fly with a cannonball. – Igb Jun 04 '18 at 08:29
  • @lgb From C# do you see the two C++ class? Does the visual studio autocomplete the class content when you press `.`? If yes, then you can take a look at what you see. – xanatos Jun 04 '18 at 08:31
  • 2
    @xanatos Thanks for your help, finally the problem was re-declaring a class with the same name on c#, plus some issues regarding building for AnyCPU instead of explicitly fixing target architecture. – Igb Jun 04 '18 at 11:47

1 Answers1

6
  public class ThresholdReachedEventArgs : EventArgs

The code is correct, except for this minor glitch. You accidentally re-declared this class in your C# code. Now there are two, one from your C++/CLI project and another from your C# project. That is a problem, type identity in .NET is not just determined by the namespace name and class name, it also includes the assembly it came from.

So these are two distinct types, the compiler tries to tell you that the C# version of it is not the correct one. That they have the same name doesn't exactly help you decode the error message :)

Very easy to fix, simply delete the class declaration from your C# code. Now the compiler will use the C++/CLI version of it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536