调用C ++ dll的C#程序

时间:2018-11-10 22:33:37

标签: c# c++ dll

我有一个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 :(得分:0)

注释,如果您不想在这里阅读全部内容,可能想看看this question here。)

首先,我将尝试从历史的角度来回答这个问题,因为您要问的是随着时间的推移而产生的需求,但是C ++和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#语言定义一起使用)。如果您不遵循这种方法,那么首先要做的就是检查使用该名称的可能定义中的哪个是您想要的,然后检查编译器如何 mangles 完整原型定义以知道链接器获得的最终名称,并用于访问该函数,最后,要尊重堆栈框架,函数激活记录等的完整环境,如果不遵循它们,它们将使程序挂起。如果您对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访问原始套接字或访问底层操作系统功能。

如果将来库界面发生更改,还可以更改 glue模块使其与新版本再次兼容。

注意

接下来是用于构建示例代码的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