4

I'm calling C++ method from C# code. Everything works fine except returning multiple parameters back to C#.

In my case those parameters are: int x, y, width, height;

What I want to do is to return a class or struct from c++ code to c#.

I have provided an example so it would be much more clear what is on my mind. I know that one way to go is to use Marshal class, maybe the only way.

C# code

public class ImageMatch
{
    //method that is used to call pass string parameters and call c++ method
    [System.Runtime.InteropServices.DllImport("ImageComputingWrapper.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
    static extern ImageComputingWrapper.ImageParams ComputeImage(string imgPath, string templPath);

    public  GetImgParams(string imgPath, string templPath)
    {
        //a class from C++ code
        ImageComputingWrapper.ImageParams imgParams;
        //retreive all the data
        imgParams = ComputeImage(imgPath, templPath);
    }
}

C++ code

//ImageComputingWrapper.cpp
extern "C" __declspec(dllexport) ImageComputingWrapper::ImageParams ComputeImage(const char* imgPath, const char* templPath)
{
    computeImage* compImage = new computeImage(imgPath, templPath);
    ImageComputingWrapper::ImageParams imageParams;

    imageParams.x = compImage->x;
    imageParams.y = compImage->y;
    imageParams.width = compImage->width;
    imageParams.height = compImage->height;

    return imageParams;
}

//ImageComputingWrapper.h
//class to return back to c#
public ref class ImageParams
{
    public:
        ImageParams(){}
        int x;
        int y;
        int width;
        int height;
};

I do know that it is not possible to return class from C++ code to C# as it's in this example. It is just to easily understand what I meant.

One thing to point out, I am a C# programmer so there may be something wrong in that C++ code (pointers).

Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
Mr. Blond
  • 1,113
  • 2
  • 18
  • 41
  • possible duplicate http://stackoverflow.com/questions/315051/using-a-class-defined-in-a-c-dll-in-c-sharp-code – DLeh Feb 02 '15 at 20:33
  • 1
    That's a System::Drawing::Rectangle. The point of using C++/CLI is to **not** have to use [DllImport] in your C# program. Simply add a reference to your C++/CLI project. You'll only see ImageParams now, you already know how to write a real ImageComputingWrapper – Hans Passant Feb 02 '15 at 21:04

1 Answers1

5

You cannot return a ref class using p/invoke. What you can do is declare a ref class in your C++/CLI assembly and simply consume that from C#.

First of all you need a C++/CLI class library. For instance:

// ClassLibrary1.h

#pragma once

using namespace System;

namespace ClassLibrary1 
{
    public ref class Class1
    {
    public:
        int x;
        int y;
        int width;
        int height;
    public:
        Class1() : x(42), y(666), width(24), height(13) {}
    };
}

You can then consume this class library like any other managed assembly:

using System;
using ClassLibrary1;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Class1 instance = new Class1();
            Console.WriteLine(instance.x);
            Console.WriteLine(instance.y);
            Console.WriteLine(instance.width);
            Console.WriteLine(instance.height);
        }
    }
}

And that's all there is to it.


You ask in comments how to pass string arguments to the C++/CLI code. Use System::String^ on the C++/CLI side. That's the C++/CLI way of referring to the .net string type. So your constructor might become:

public ref class Class1
{
....
public:
    Class1(System::String^ imgPath, System::String^ tempPath)
    {
        ....
    }
};

On the C# side you would create the instance like this:

string imgPath = "...";
string tempPath = "...";
Class1 instance = new Class1(imgPath, tempPath);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Yes this is really nice solution +1 @David Heffernan. But one reason why I'didn't chose the class constructor is because I couldn't figure out how to pass string parameters **imgPath** and **templPath** from c# to c++. The only way that I found was using dllimport. I should highligh this issue more. – Mr. Blond Feb 02 '15 at 21:36
  • 1
    In C++/CLI use `System::String^`, in C# use `string`. Both of those refer to the same type, the .net string type. I'll show you in an update. – David Heffernan Feb 02 '15 at 21:38
  • 1
    You are dam right man! I always like to make things more complicated than they are, But I searched for this some time and couldn't find. Maybe this post will help someone else to not do the same mistake that I did. – Mr. Blond Feb 02 '15 at 21:48
  • Thanks. Sometimes it is hard to see the wood for the trees! – David Heffernan Feb 02 '15 at 22:03