Управляемый C ++ для формирования моста между c # и C ++ - PullRequest
11 голосов
/ 01 декабря 2009

Я немного ржавый, на самом деле действительно ржавый с моим C ++. Не касался этого с первого года обучения в колледже, так что прошло уже много времени.

Во всяком случае, я делаю обратное тому, что делает большинство людей. Вызов кода C # из C ++. Я провел некоторые онлайн-исследования, и мне кажется, что мне нужно создать управляемый C ++, чтобы сформировать мост. Используйте __declspec (dllexport), а затем создайте dll из этого и используйте все это как оболочку.

Но моя проблема в том, что мне трудно найти примеры. Я нашел некоторые базовые вещи, в которых кто-то хотел использовать версию C # для String.ToUpper (), но это было ОЧЕНЬ просто и представляло собой небольшой фрагмент кода.

У кого-нибудь есть идеи, где я могу найти что-то более конкретное? Обратите внимание, я не хочу использовать COM. Цель состоит в том, чтобы вообще не трогать код C #.

Ответы [ 2 ]

11 голосов
/ 01 декабря 2009

Пока Lain побеждает меня писать пример, я все равно выложу его на всякий случай ...

Процесс написания оболочки для доступа к вашей собственной библиотеке аналогичен обращению к одной из стандартных библиотек .Net.

Пример кода класса C # в проекте с именем CsharpProject:

using System;

namespace CsharpProject {
    public class CsharpClass {
        public string Name { get; set; }
        public int Value { get; set; }

        public string GetDisplayString() {
            return string.Format("{0}: {1}", this.Name, this.Value);
        }
    }
}

Вы должны создать управляемый проект библиотеки классов C ++ (например, CsharpWrapper) и добавить свой проект C # в качестве ссылки на него. Чтобы использовать один и тот же заголовочный файл для внутреннего использования и в ссылочном проекте, вам нужен способ использовать правильный declspec. Это может быть сделано путем определения директивы препроцессора (в данном случае CSHARPWRAPPER_EXPORTS) и использования #ifdef для установки макроса экспорта в вашем интерфейсе C / C ++ в заголовочном файле. Заголовочный файл неуправляемого интерфейса должен содержать неуправляемый материал (или должен быть отфильтрован препроцессором).

Файл заголовка неуправляемого интерфейса C ++ (CppInterface.h):

#pragma once

#include <string>

// Sets the interface function's decoration as export or import
#ifdef CSHARPWRAPPER_EXPORTS 
#define EXPORT_SPEC __declspec( dllexport )
#else
#define EXPORT_SPEC __declspec( dllimport )
#endif

// Unmanaged interface functions must use all unmanaged types
EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue);

Затем вы можете создать файл внутреннего заголовка, чтобы иметь возможность включать его в файлы управляемой библиотеки. Это добавит операторы using namespace и может включать необходимые вспомогательные функции.

Файл заголовка управляемого C ++ интерфейса (CsharpInterface.h):

#pragma once

#include <string>

// .Net System Namespaces
using namespace System;
using namespace System::Runtime::InteropServices;

// C# Projects
using namespace CsharpProject;


//////////////////////////////////////////////////
// String Conversion Functions

inline
String ^ ToManagedString(const char * pString) {
 return Marshal::PtrToStringAnsi(IntPtr((char *) pString));
}

inline
const std::string ToStdString(String ^ strString) {
 IntPtr ptrString = IntPtr::Zero;
 std::string strStdString;
 try {
  ptrString = Marshal::StringToHGlobalAnsi(strString);
  strStdString = (char *) ptrString.ToPointer();
 }
 finally {
  if (ptrString != IntPtr::Zero) {
   Marshal::FreeHGlobal(ptrString);
  }
 }
 return strStdString;
}

Затем вы просто пишете код интерфейса, который выполняет перенос.

Исходный файл управляемого интерфейса C ++ (CppInterface.cpp):

#include "CppInterface.h"
#include "CsharpInterface.h"

std::string GetDisplayString(const char * pName, int iValue) {
 CsharpClass ^ oCsharpObject = gcnew CsharpClass();

 oCsharpObject->Name = ToManagedString(pName);
 oCsharpObject->Value = iValue;

 return ToStdString(oCsharpObject->GetDisplayString());
}

Затем просто включите неуправляемый заголовок в неуправляемый проект, скажите компоновщику использовать сгенерированный файл .lib при компоновке и убедитесь, что библиотеки .Net и оболочки находятся в той же папке, что и ваше неуправляемое приложение.

#include <stdlib.h>

// Include the wrapper header
#include "CppInterface.h"

void main() {
 // Call the unmanaged wrapper function
 std::string strDisplayString = GetDisplayString("Test", 123);

 // Do something with it
 printf("%s\n", strDisplayString.c_str());
}
10 голосов
/ 01 декабря 2009

Создайте новый проект C ++ / CLI в visual studio и добавьте ссылку на свою C # dll. Предположим, у нас есть C # dll с именем DotNetLib.dll с этим классом в:

namespace DotNetLib
{
    public class Calc
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
}

Теперь добавьте класс CLR C ++ в ваш проект C ++ / CLI:

// TestCPlusPlus.h

#pragma once

using namespace System;
using namespace DotNetLib;

namespace TestCPlusPlus {

    public ref class ManagedCPlusPlus
    {
    public:
        int Add(int a, int b)
        {
            Calc^ c = gcnew Calc();
            int result = c->Add(a, b);
            return result;
        }
    };
}

Это вызовет C # из C ++.

Теперь при необходимости вы можете добавить собственный класс C ++ в ваш проект C ++ / CLI, который может общаться с классом CLR C ++:

// Native.h
#pragma once

class Native
{
public:
    Native(void);
    int Add(int a, int b);
    ~Native(void);
};

и

// Native.cpp
#include "StdAfx.h"
#include "Native.h"
#include "TestCPlusPlus.h"

Native::Native(void)
{
}

Native::~Native(void)
{
}

int Native::Add(int a, int b)
{
    TestCPlusPlus::ManagedCPlusPlus^ c = gcnew TestCPlusPlus::ManagedCPlusPlus();
    return c->Add(a, b);
}

Вы должны иметь возможность вызывать Native-класс из любой другой нативной библиотеки C ++, как обычно.

Обратите внимание, что Managed C ++ отличается от C ++ / CLI и заменяет его. Википедия объясняет это лучше всего:

http://en.wikipedia.org/wiki/C%2B%2B/CLI

...