1. 程式人生 > >C++ 封裝DLL遇到的一些坑爹問題與解決方案

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 
版權宣告:本文為博主原創文章,轉載請