C # программа, вызывающая C ++ dll - PullRequest
0 голосов
/ 11 ноября 2018

У меня есть файл DLL и соответствующий ему файл .h. Я хочу позвонить из программы на C #. Я подозреваю, что это либо невозможно, либо очень сложно.

Вот часть файла .h, который я хочу использовать

    int SelfTest();

    /******************************************************
     * C++ functions
    ******************************************************/
    int CPP_Init(std::string baseDirectory);
    std::vector<std::string> CPP_GetKeys();
    std::string CPP_ProcessData(std::string plaintext,
                            std::string keyName,
                            std::string sourceStation,
                            std::string destinationStation,
                            int encryptData,
                            int extendedChecksumHeader);
    std::vector<std::string> CPP_Decrypt(std::string cipherText);

1 Ответ

0 голосов
/ 13 ноября 2018

( примечание , если вы не хотите читать все здесь, возможно, вы захотите взглянуть на этот вопрос здесь .)

Прежде всего, я попытаюсь дать этот ответ с исторической точки зрения, поскольку вы спрашиваете о необходимости, которая приходит со временем, но оригинальный дизайн ни C ++, ни C # не заключался в том, чтобы сделать их совместимыми языками. Я буду привлекать C к середине, так как требования к дизайну C ++, чтобы быть совместимым с C, приняли много проектных решений для решения проблемы (и это даст много света в этой теме). Таким образом, это единственный языковой дизайн, для которого исходная (а не двоичная) совместимость была первоначальным требованием. Есть две причины следовать этому подходу:

  • C # был разработан позже, чем C ++, поэтому, если какое-либо из них имеет некоторые свойства совместимости в своем дизайне, вероятно, что C # имеет их, так как C ++ не имел бинарных решений по проектированию совместимости, принятых для C # (его не было на время определения C ++)
  • Поскольку C ++ был разработан с учетом требований "совместимости" , чтобы иметь возможность использовать обширный набор библиотечных подпрограмм, доступных в C, это язык, который имеет некоторый способ express это заявления о совместимости каким-то образом, так что возможно иметь некоторую точку отсчета, которая позволяет вам искать в документации.

Компиляция C-программы с помощью C-компилятора имеет некоторые недостатки, когда речь идет о взаимодействии C и C ++-процедур между собой. Когда C ++ разрабатывался, он имел в виду C-совместимость. Но обратное неверно, никто не проектировал C с «как C ++-совместимый» . Единственный способ получить доступ к функциям C ++ из C - это скомпилировать их с помощью компилятора C ++ и выполнить модификации compatibility , чтобы устранить внутренние несовместимости обоих языков (или использовать компилятор C, а затем связать двоичные файлы вместе с помощью компоновщика C ++) Последний момент очень важен связать их с компоновщиком C ++ (или связать в режиме C ++)

Почему так важно использовать компоновщик C ++ для связывания объектов C и C ++? Главным образом потому, что компоновщик C не готов включить потребности языка C ++ (создание экземпляров глобальных объектов, вызывая их конструкторы, не поддерживается компоновщиком C) В случае Java или C ++ экземпляры объектов всегда (по крайней мере, в Java, не может гарантировать, что происходит в C #), управляемый по ссылке. C # был разработан как язык для спецификации виртуальной машины .Net (как Sun сделал и с Java), и, поскольку у вас есть компиляторы C ++ для этой платформы, C ++ был спроектирован без учета виртуальной целевой платформы и, вероятно, возможности такого организация использования C ++ из C # будет как минимум трудной.

Давайте поговорим о случае C / C ++:

Во-первых, для того, чтобы функции C ++ были доступны из кода C, они должны иметь "C" интерфейс вызова (возможно, что какое-то расширение языка необходимо для того, чтобы сделать вызовы методов C ++ и функции совместимыми с C #). Это означает, что функции C ++, которые вы вызываете из C, должны быть объявлены как

extern "C" void my_cplusplus_function(type1 arg1, type2 arg2);

или положить все в

extern "C" {
    ...
}

блок. Причина этого заключается в том, что компиляторы C ++ искажают внешние идентификаторы (включая типы параметров и другие атрибуты функций, например атрибут const в методах ...), чтобы разрешить разные версии для одного и того же имени функции с разными интерфейсами и это не совместимо с вызывающим интерфейсом C. Конечно, это также верно для C # (возможно, в компиляторе Visual C ++ есть расширение extern "C#", позволяющее работать с именами в определениях языка C #). Если вы не придерживаетесь этого подхода, первое, что у вас есть, это проверить, какое из возможных определений с этим именем является тем, которое вам нужно, а затем проверьте, как компилятор искажает полное определение прототипа, чтобы узнать окончательное имя, которое получает компоновщик, и которое используется для доступа к функции и, наконец, для уважения всей среды стековых фреймов, записей активации функций и т. д., которые приведут к зависанию программы, если за ней не следовать. Если у вас есть интерес к способу Java / C ++ (полностью задокументирован на www.java.com), вы увидите ограничения, налагаемые вызовами функций Java / C ++ (это документ под эпиграфом JNI , возможно, если вы ищете C точный собственный интерфейс , вы получите что-то) Это связано с разной организацией памяти исполняемых файлов, даже для одной и той же целевой платформы.

Пример "режима совместимости с C" (извините, у меня нет эквивалента для C # или Java под рукой) проиллюстрируем это:

main.c

#include <stdio.h>  /* for printf */
#include <stdlib.h> /* for EXIT_SUCCESS */
#include <math.h>   /* for normal sqrt() */

#include "cpp_sqrt.h"  /* for the prototype of cpp_mysqrt() */

int main()
{
    /* you are forced to use stdio here, as you are in plain C */
    printf("cpp_sqrt(3.0) => %lg\n"
           "sqrt(3.0)     => %lg\n",
           cpp_sqrt(3.0), /* <== this is a c++ function */
           sqrt(3.0));    /* <== this is the math C function */
    return EXIT_SUCCESS;
}

cpp_mysqrt.cc

#include "cpp_sqrt.h"
#include <iostream>

double cpp_sqrt(const double x)
{
    /* you can use all C++ stuff here, as this is C++ */
    std::cout << "in cpp_sqrt()" << std::endl;

    double     g = 1.0, /* geometric mean */
               a = x;   /* arithmetic mean */
    const double epsilon = 1.0E-6;

    while ((a - g) > epsilon) {
            a = (a + g) / 2.0;
            g = x / a;
    }

    return g;
}

cpp_mysqrt.h

/* this construction is required to be able to include this
 * file in C and C++ source code.  C++ compilers always define
 * the __cplusplus macro, so they will include the
 * extern "C" block, while C compilers don't.  This makes it
 * possible to use the same prototype header file in both C and
 * C++ source files without problems.
 */
#ifdef __cplusplus
extern "C" {
#endif

double cpp_sqrt(double X);

#ifdef __cplusplus
} /* extern "C" */
#endif

Если вы удалите части extern "C" { и } файла заголовка (или просто переименуете __cplusplus в __cplusplus1, чтобы компилятор C ++ не использовал его), компилятор C ++ будет искажать имя для cpp_sqrt(double) функция, делающая ее недоступной из кода C, как вы увидите, если попытаетесь ее скомпилировать:

$ ed cpp_sqrt.h <<'EOF'
1,$s/__cplusplus/__cplusplus1/g
w
EOF
$ make
cc -O -pipe -c main.c -o main.o
c++ -O -pipe -c cpp_sqrt.cc -o cpp_sqrt.o
c++  -o cppfromc main.o cpp_sqrt.o -lm
main.o: In function `main':
main.c:(.text+0x10): undefined reference to `cpp_sqrt'
c++: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1

Stop.
make: stopped in /home/user/cfromcpp

Итак, наконец, какова проблема, которую вы решили в наше время? Разработчик, сталкивающийся с такой проблемой, обычно создает «интерфейсный набор функций» , который соответствует ограничениям интерфейса для обоих миров и действует как клейкий код (конечно, вы слышали, что последний термин), чтобы сделать интерфейс возможным. Это позволяет иметь Java доступ к необработанным сокетам или доступ к функциональности операционной системы низкого уровня.

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

Примечание

Далее Makefile используется для построения примера кода, просто выполните make для сборки программы:

Makefile

RM ?= rm -f

targets = cppfromc
toclean=$(targets)

cppfromc_objs = main.o cpp_sqrt.o
toclean += $(cppfromc_objs)
cppfromc_libs = -lm

all: $(targets)
clean:
    $(RM) $(toclean)

# NOTE: you must use the C++ linker because C++ programs have a different memory map,
# to allow for constructors of global object instances to be constructed before main()
# is executed.
cppfromc: main.o cpp_sqrt.o
    $(CXX) $(LDFLAGS) -o $@ $($@_objs) $($@_libs)

main.o cpp_sqrt.o: cpp_sqrt.h
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...