C++ 封装DLL遇到的一些坑爹问题与解决方案

一.string类型不兼容
这个算是最常见也是最初级的问题了,为了方便跨语言接口调用,如果数据量不是特别大的话,我们可以在封装DLL时选择json串来作为方法的引入参数和返回值,但由于C++的string类型(其实是STL)内存分配器兼容性很滑稽,基本上跨平台调用就一定会出现异常。所以要避免在动态库接口中传递STL容器。

解决方案:
比较常规的方法是使用char *进行指针传递,但同时有会存在一个问题,就是返回这个指针之后,调用层还能不能收的到,为了保证这块内存不随着调用方法结束而释放,我们可以采取全局变量等方法。

extern "C"
{
    char res[255];
    __declspec(dllexport)  const char * __stdcall change_str(const char* s)
    {
        string ss = s;
        strcpy_s(res, ("change_str::" + ss).c_str());
        return res;
    }        
}
二.x86和x64引起的内存异常


解决方案:
修改配置

三.__stdcall和__cdecl
__stdcall和__cdecl是两种函数名字修饰。__stdcall和__cdecl函数参数都是从可向左入栈的,并且由调用者完成入栈操作。对于__stdcall方式被调用者自身在函数返回前清空堆栈;而__cdecl则由调用者维护内存堆栈,所以调用者函数生成的汇编代码比前一种方式长。

__stdcall方式:_FuncName@sizeofParameters

例如:int __stdcall test(int a,double b)编译之后完整的函数名为_test@12

__cdecl方式:_FuncName

例如:int __stdcall test(int a,double b)编译之后完整的函数名为_test

使用VS进行dll开发如果不加修饰符的话,默认是采取__cdecl的方式,__cdecl虽然约定的函数只能被C/C++调用。但其他语言也是可以调用的,如delphi。但是C#或者VB的默认方式是__stdcall,所以直接不能直接通过 Public Declare Function add Lib的方式调用。否则会报错:

调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。
解决方案:
有人说可以通过[DllImport("Dll.dll")]改为[DllImport("Dll.dll", CallingConvention=CallingConvention.Cdecl)]也就是换成Cdecl方式可以解决

Imports System.Runtime.InteropServices
<DllImport("tset.dll", CallingConvention = CallingConvention.Cdecl)> Private Shared Function add(ByVal a As Integer, ByVal b As Integer) As Integer
End Function
而且DllImport看起来也比Declare要高大上。

https://cloud.tencent.com/info/06698cf1621639b6be6d87fe705a9795.html

但是不知道是不是我的VB版本问题,提示找不到CallingConvention这个属性,很尴尬。

同时,为了提高DLL的扩展性,还是建议使用_stdcall方式来封装:

extern "C"
{
    __declspec(dllexport) int __stdcall add(int a, int b)
    {
        return a + b;
    }        
}
四.找不到方法的入口点&C++调用GetProcAddress获取函数指针异常


解决方案:
1.使用工具(例如dependency)查看封装的DLL是否存在相应的接口

2.如果没有的话,封装DLL时可以导入def文件

EXPORTS下面的名与C++接口对应

LIBRARY "Test"
EXPORTS
add  @1
change_str @2
print_creater @3
#include "stdafx.h"
extern "C"
{
    char res[255];
 
    __declspec(dllexport) int __stdcall add(int a, int b)
    {
        return a + b;
    }
    __declspec(dllexport)  const char * __stdcall change_str(const char* s)
    {
        string ss = s;
        strcpy_s(res, ("change_str::" + ss).c_str());
        return res;
    }
    __declspec(dllexport) const char * __stdcall print_creater()
    {
 
        strcpy_s(res, "nine_sun666");
        return res;
    }
        
}
在项目属性中导入模块定义文件即可

VB代码:

Module Module1
    Public Declare Function add Lib "test.dll" (ByVal a As Integer, ByVal b As Integer) As Integer
    Public Declare Function print_creater Lib "test.dll" () As String
    Public Declare Function change_str Lib "test.dll" (ByVal s As String) As String
 
    Sub Main(ByVal args As String())
        Console.WriteLine("Hello World!")
        Console.WriteLine(add(10, 20))
        Console.WriteLine(print_creater())
        Console.WriteLine(change_str("6666666"))
    End Sub
End Module
调用成功!

五.C++调用约定声明冲突


解决方案:
还是由于默认的导入修饰符为__cdecl造成的,所以在C++调用层定义类型也要加上__stdcall

pSubtract1 add = pSubtract1(GetProcAddress(hDLL, "add"));        

#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{    
    HMODULE hDLL = LoadLibrary(_T("test.dll")); //加载dll文件 
    if (hDLL != NULL)
    {    
        typedef int (__stdcall *pSubtract1)(int a, int b);
        pSubtract1 add = pSubtract1(GetProcAddress(hDLL, "add"));        
        
        typedef const char * (__stdcall *pSubtract2)();
        pSubtract2 print_creater = pSubtract2(GetProcAddress(hDLL, "print_creater"));
                
        typedef const char * (__stdcall *pSubtract3)(const char * a);
        pSubtract3 change_str = pSubtract3(GetProcAddress(hDLL, "change_str"));
 
        cout << add(10, 20) << endl;
        cout << print_creater() << endl;
        cout << change_str("hello word") << endl;
 
        FreeLibrary(hDLL);
    }
    else
    {
        std::cout << "Cannot Find " << "testdll" << std::endl;
    }
    system("pause");
    return 0;
}
 
--------------------- 
作者:九日王朝 
来源:CSDN 
原文:https://blog.csdn.net/sm9sun/article/details/84986875 
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://my.oschina.net/u/4000302/blog/3029379

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值