簡體   English   中英

調用C ++ dll的C#程序

[英]C# program calling 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);

請注意 ,如果您不想在這里閱讀全部內容,則可能要在這里看一下這個問題 。)

首先,我將嘗試從歷史的角度來回答這個問題,因為您要問的是隨時間而來的需求,但是C ++和C#的原始設計都不是為了使它們兼容的語言。 我將在中間涉及C,因為與C兼容的C ++的設計要求針對該問題做出了許多設計決策,以解決該問題(這將為該主題帶來很多啟發)。 因此,這是唯一對源(而不是二進制)兼容性進行最初要求的語言設計。 采用此方法有兩個原因:

  • C#的設計晚於C ++,因此,如果其中任何一個在其設計中都具有某些兼容性屬性,則C#很有可能具有它們,因為C ++沒有對C#進行二進制兼容性設計的決定(在C ++時不存在)定義)
  • 由於C ++的設計考慮到了“兼容性”的要求,因此能夠使用C語言提供的大量庫例程,這是一種可以某種方式表達此兼容性聲明的語言,因此可能有一點文檔中可參考的參考文獻。

當談論C和C ++例程之間的接口時,使用C編譯器編譯C程序存在一些缺點。 設計C ++時,要牢記C兼容性。 但是事實並非如此,沒有人在設計C時考慮到“與C ++兼容” 能夠從C中訪問C ++函數的唯一方法是使用C ++編譯器進行編譯,並進行兼容性修改以處理兩種語言固有的不兼容性(或使用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM