0

I have a third-party Windows app that supports a C plugin/driver (see spec below) to be a passive data receiver once initialized. Now I found a C# package that does the data gathering and Pushing. The only missing piece here is a bridge between the C dll and the C# dll.

The data flow is like this: When the app is launched, it loads and calls the C dll which contains several exported functions including an init function. In this init function, I like to establish a call to the C# to setup some network connection and prepare for incoming data. Once that done, according to the driver spec, the C# dll will gather data and stream it to the receiving driver. To accommodate this, I have two thoughts (you may come up with more):

1) to wrap the C# with C++/Cli and call the expose C#-like methods from the driver. Declare an object with gcroot, then instantiate it with gcnew then call the method(s). Tried that and I got stackoverflowexception. I am new to this mix-mode programming and can't figure out why that happened.

2) to wrap the C dll in some way (like using C++/Cli to import the C functions then interact with the C# data streamer) to be callable from C#. What is the best way to do this?

I have done some reading and it seems C++/Cli is the easy route but I am open to other not so complicated options as well. What are the project settings I have to add/modify to make it work should I choose C++/Cli or any way you suggest?

As I am new to tackle this kind of problem, any samples or related links are helpful. So I appreciate if you could demonstrate how things work one way or the other.

Here is piece of the skeletal C# dll referenced (other methods are omitted):

public class Client {

    public Client()
    {
        //reset();
    }

    public void test()
    {

        Console.WriteLine("test");
    }

    /// <summary>
    /// Connect connects the Client to the server.
    /// Address string has the form host:port.
    /// </summary>
    public void Connect(string address)
    {
        Disconnect();
        // validate address
        int sep = address.IndexOf(':');
        if (sep < 0)
        {
            throw new ArgumentException("Invalid network address");
        }
        // set host and port
        host = address.Substring(0, sep);
        toPort(ref port, address.Substring(sep + 1));
        // connect
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Connect(host, port);
        rw.Set(socket, CLIENT_DEFAULT_BUFFER_SIZE); 
    }

    /// <summary>
    /// Disconnect disconnects the Client from the server.
    /// </summary>
    public void Disconnect()
    {
        backlog.Clear();
        try
        {
            if (Connected)
            {
                write("close");
            }
        }
        catch (Exception e)
        {

        }
        //reset();
        //rw.Close();
    }

}

Here is the skeletal spec of the C dll:

//APIs
#ifdef __cplusplus
extern "C"{
#endif

#define DLL __declspec(dllexport)

////////////////////////////////////////////////////////////////////////////
// Initialization: do some prep for receiving data from C#
// params:
//      hWnd            handle
//      Msg             message
//      nWorkMode       work mode
// return:
//       1              true    
//      -1              false
DLL int Init(HWND hWnd, UINT Msg, int nWorkMode);


// Quitting and closing
// Param:
//      hWnd            same handle as Init
//  return:
//       1              true    
//      -1              fals
DLL int Quit(HWND hWnd);

#ifdef __cplusplus
}
#endif
Shuang Liang
  • 71
  • 1
  • 9
  • C++/CLI makes it easy to call C code from a managed program. But that's not what you are trying to do, you are trying to go the opposite way. You can't get anywhere until the CLR is loaded and initialized, using C++ is pretty important to make that less painful. – Hans Passant Feb 13 '15 at 18:43
  • @Hans Passant. You are right. What you suggest is to wrap the C dll in C++? I have left with little choice for two reasons: 1) the C dll has spec with it and 2) the data streamer comes as a C# package. – Shuang Liang Feb 13 '15 at 19:09

1 Answers1

0

To call C functions in an external DLL, you can use C++/CLI as you've already mentioned, or use P/Invoke (Platform Invoke).

With P/Invoke, you define a static extern method in your C# assembly then decorate it with appropriate attributes. After that, you can call the method as per any other C# method, and the .NET Framework plumbing will handle the loading of the DLL and marshalling the method parameters back and forth.

For example:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);

The method is declared with the static and extern keywords, and decorated with the DllImport attribute which identifies the DLL that contains the C function. That's generally the minimum you will need.

The hardest part is determining which C types map to which .NET types for the method parameters and return type: this is a bit of a black art! If you visit pinvoke.net you can see how the Windows APIs have been translated, which may help. Searching the web for "pinvoke" should also turn up a number of useful resources.

Steven Rands
  • 5,160
  • 3
  • 27
  • 56
  • Thanks, @Steven Rands! As I mentioned in my description, my app will load the C dll in which the init function is what I want is to use to launch the data streamer in C# by calling its connect method., not the other way around. I tried several ways to wrap the C# using C++/Cli, unmanagedexport package by Robert Giesecke etc . without success. You mentioned P/Invoke in your response. I believe SWIG can generate C# codes using the P/Invoke mechanism. But isn't that to expose C functions to be called by C#, not the other way around? Do I read you wrong? – Shuang Liang Feb 13 '15 at 19:03
  • @ShuangLiang Ah, I understand what you mean now: you want to call C# code from a C application, not the other way around. In that case P/Invoke won't help. An answer to [this SO question](http://stackoverflow.com/q/4428267/4265041) (*Calling C# from C*) lists a few possible options. – Steven Rands Feb 16 '15 at 09:27