C++ 如何使用我的C++;带有C应用程序的DLL?

C++ 如何使用我的C++;带有C应用程序的DLL?,c++,c,visual-studio,dll,C++,C,Visual Studio,Dll,环境是Windows,MS Visual Studio 2012。如何在C(非C++)应用程序中使用C++编写的DLL库?在我的DLL中,我使用了名称空间、类等。例如,我写道: DLL项目 EXE项目(使用DLL) 两种都是用C++编写的。有人能给我看一下类似C++的应用程序吗?p> 谢谢。您必须创建C包装函数,它可以将不透明指针传递给类实例。 以下是一个合理的例子: 引自链接: typedef Foo* FOO_HDL; extern "C" { __declspec(dllexpor

环境是Windows,MS Visual Studio 2012。如何在C(非C++)应用程序中使用C++编写的DLL库?在我的DLL中,我使用了名称空间、类等。例如,我写道:

  • DLL项目
  • EXE项目(使用DLL)

两种都是用C++编写的。有人能给我看一下类似C++的应用程序吗?p>


谢谢。

您必须创建C包装函数,它可以将不透明指针传递给类实例。
以下是一个合理的例子:

引自链接:

typedef Foo* FOO_HDL;
extern "C" { 
__declspec(dllexport) FOO_HDL FooCreate() {
  return new Foo();
};
__declspec(dllexport) void FooDestroy(FOO_HDL obj) {
  delete obj;
};
__declspec(dllexport) double FooGetValue(FOO_HDL obj) {
  return obj->GetValue();
};
__declspec(dllexport) void FooSetValue(FOO_HDL obj, double NewValue) {
  obj->SetValue(NewValue);
};
};

解决方案是在C++中编写一个C接口到你的DLL(作为DLL的一部分,或者作为一个单独的DLL),然后从C代码中使用C接口。 确保您的C接口由所有函数组成,这些函数都是

extern“C”

Edit:正如SigTerm在评论中指出的那样,让函数
stdcall
不是一个好主意(默认情况下不是这样,但有些人会从WinAPI函数复制),因为这会在尝试使用其他语言的函数时造成麻烦

以下是挑战,以及如何解决这些挑战:

您的类位于命名空间中

通常,您可以通过声明不透明的<代码>结构类名称> /COD>,在C中导出C++类。但是,这对于命名空间中的类是不可能的,因为C不知道命名空间的概念。有几种方法可以解决这个问题:

  • 简单的方法:只需让您的C接口分发并接受void指针,可能通过typedef稍微伪装成实际的类型

    优点:实施起来微不足道。您只需要在每个入口点进行类型转换

    缺点:C接口不是类型安全的。很容易将错误的指针传递到您的接口,并将事情搞砸

  • <> >强>入侵方式:C++ C++中的,为每个C++类定义全局范围基类,并从中派生出实际类。请注意,全局作用域基类可能为空,因为在使用它之前,您将
    静态\u强制转换为实际类型

    优点:易于实施,类型安全

    缺点:如果你有很多课程,你需要做很多工作。此外,如果您不能以这种方式更改所有要顶级公开的类,那么这可能是不可能的。此外,作为基类,您不能将它们分开,以便只有C客户机需要它们。每个C++客户端都将得到全局类,即使它不包含C接口。

  • 使用句柄:使用整数句柄,它实际上包含转换为int的指针。如果您的编译器支持它,请使用
    stdint.h
    中的
    intptr\t
    ,因为无论您在什么平台上,它都保证足够大。否则,为了安全起见,最好使用
    long

    优点:易于实现,比
    void*
    稍安全一些,这是任何处理过操作系统文件处理等低级功能的C程序员都熟悉的界面风格

    缺点:不太安全;您仍然可以向函数传递错误类型的句柄

  • 使用“封装”句柄:对于要公开的每种类型,在全局范围内定义一个C结构,其中只包含一个
    void*
    或一个整数句柄作为成员

    优点:相对简单的实现(尽管不如
    void*
    或原始句柄那么简单),类型安全(只要C代码不与结构成员混淆)

    缺点:可能使C编译器更难优化。但这在很大程度上取决于C编译器

我的建议是使用封装的句柄,除非它出现性能问题,在这种情况下,我会使用整数句柄

类接口接受并返回
std::string
C代码无法处理
std::string
,而且您肯定不想将接口包装到C中的接口(而且您的C接口用户无论如何也不希望您这样做)。因此,在C接口中必须使用
char*
/
char const*

将字符串传递到您的库 将字符串传递到库的情况很简单:只需传入一个
char const*
,然后让
std::string
的构造函数完成其余的工作

从库中返回字符串 这是一个复杂的案例。您不能只返回
c_str
的结果,因为当临时
std::string
被破坏时,它会给您留下一个悬空的指针。因此,您基本上有以下选项:

  • 存储一个静态的
    std::string
    (因此它不会在返回时被释放),将函数调用的结果复制到其中,然后返回
    c_str

    优点:易于实现,保证为您提供完整的字符串

    缺点:它不是线程安全的
    ,用户还必须记住立即将内容复制到某个地方,否则下一次调用该函数将使数据无效,甚至使指针无效

  • 在包装函数中分配一个大小合适的字符数组,将字符串的内容复制到该数组中,并返回指向该数组的指针

    优点:保证返回完整字符串(假设内存分配没有失败)

    缺点:众所周知的一个问题是,在另一个DLL中释放内存(而不是分配内存的DLL)无法正常工作。所以你不会
    /* somelib_c_interface.h */
    #ifndef SOMELIB_C_INTERFACE_H
    #define SOMELIB_C_INTERFACE_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <stdint.h>
    #include <stdlib.h>
    
    /* typedef for C convenience */
    typedef struct
    {
      intptr_t handle;
    } some_namespace_info;
    
    typedef struct
    {
      intptr_t handle;
    } some_namespace_employee;
    
    /* error codes for C interface */
    typedef int errcode;
    #define E_SOMELIB_OK 0              /* no error occurred */
    #define E_SOMELIB_TRUNCATE -1       /* a string was created */
    #define E_SOMELIB_OUT_OF_MEMORY -2  /* some operation was aborted due to lack of memory */
    #define E_SOMELIB_UNKNOWN -100      /* an unknown error occurred */
    
    /* construct an info object (new)
    errcode some_namespace_info_new(some_namespace_info* info, char const* name, char const* number);
    
    /* get rid of an info object (delete) */
    errcode some_namespace_info_delete(some_namespace_info info);
    
    /* Some_namespace::Info member functions */
    errcode some_namespace_info_get_name(some_namespace_info info, char* buffer, size_t length);
    errcode some_namespace_info_set_name(some_namespace_info info, char const* name);
    
    errcode some_namespace_info_get_number(some_namespace_info info, char* buffer, size_t length);
    errcode some_namespace_info_set_number(some_namespace_info info, char const* name);
    
    /* the employee class */
    
    /* replicated enum from employee */
    enum some_namespace_employee_sex { male, female };
    
    errcode some_namespace_employee_new(some_namespace_employee* employee,
                                        char const* name, char const* surname, int age,
                                        some_namespace_employee_sex sex);
    
    errcode some_namespace_employee_delete(some_namespace_employee employee);
    
    errcode some_namespace_employee_get_name(some_namespace_employee employee, char* buffer, size_t length);
    errcode some_namespace_employee_set_name(some_namespace_employee employee, char const* name);
    
    errcode some_namespace_employee_get_surname(some_namespace_employee employee, char* buffer, size_t length);
    errcode some_namespace_employee_set_surname(some_namespace_employee employee, char const* name);
    
    /* since ages cannot be negative, we can use an int return here
       and define negative results as error codes, positive results as valid ages
       (for consistency reason, it would probably be a better idea to not do this here,
       but I just want to show another option which sometimes is useful */
    int some_namespace_employee_get_age(some_namespace_employee employee);
    
    errcode some_namespace_employee_set_age(some_namespace_employee employee, int age);
    
    errcode some_namespace_employee_get_set(some_namespace_employee employee,
                                            enum some_namespace_employee_sex* sex);
    errcode some_namespace_employee_set_sex(some_namespace_employee employee,
                                            enum some_namespace_employee_sex sex);
    
    typedef struct
    {
      intptr_t handle;
    } info_iter;
    
    info_iter_delete(info_iter iterator);
    
    some_namespace_info info_iter_get(info_iter iterator);
    void info_iter_next(info_iter iterator);
    bool info_iter_finished(info_iter iterator);
    
    info_iter some_namespace_employee_phones(some_namespace_employee employee);
    info_iter some_namespace_employee_emails(some_namespace_employee employee);
    info_iter some_namespace_employee_sites(some_namespace_employee employee);
    
    errcode some_namespace_print(FILE* dest, some_namespace_employee employee);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif // defined(SOMELIB_C_INTERFACE_H)