Some cheating problems and solutions encountered by C++ package DLL

1. The string type is not compatible

This is the most common and primary problem. In order to facilitate cross-language interface calls, if the amount of data is not particularly large, we can choose json strings as the imported parameters and return values ​​of the method when encapsulating the DLL. However, due to the C++ string Type (actually STL) memory allocator compatibility is funny, basically cross-platform calls will definitely cause exceptions. So avoid passing STL containers in dynamic library interfaces.

solution:

The more conventional method is to use char * to pass pointers, but at the same time there will be a problem, that is, after returning this pointer, can the calling layer still receive it? In order to ensure that this piece of memory will not be released when the calling method ends, we can Take methods such as global variables.

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;
	}		
}

2. Memory exceptions caused by x86 and x64

solution:

Change setting

3.__stdcall and __cdecl

__stdcall and __cdecl are two function name decorations. Both __stdcall and __cdecl function parameters can be pushed from the left to the stack, and the stack operation is completed by the caller. For the __stdcall method, the callee clears the stack before the function returns; while __cdecl maintains the memory stack by the caller, so the assembly code generated by the caller function is longer than the previous method.

__stdcall method: _FuncName@sizeofParameters

For example: int __stdcall test(int a, double b) after compilation, the complete function name is _test@12

__cdecl method: _FuncName

For example: int __stdcall test(int a, double b) after compilation, the complete function name is _test

If you use VS for dll development without modifiers, the default method is __cdecl. Although __cdecl stipulates that the function can only be called by C/C++. But other languages ​​can also be called, such as delphi. But the default method of C# or VB is __stdcall, so it cannot be called directly through the method of Public Declare Function add Lib. Otherwise an error will be reported:

调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。

solution:

Some people say that it can be solved by changing [DllImport("Dll.dll")] to [DllImport("Dll.dll", CallingConvention=CallingConvention.Cdecl)], that is, changing to 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

And DllImport looks taller than Declare.

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

But I don’t know if it’s my VB version problem. It’s embarrassing that the attribute CallingConvention can’t be found.

At the same time, in order to improve the scalability of the DLL, it is recommended to use the _stdcall method to encapsulate:

extern "C"
{
	__declspec(dllexport) int __stdcall add(int a, int b)
	{
		return a + b;
	}		
}

4. The entry point of the method cannot be found & C++ calls GetProcAddress to get the function pointer exception

solution:

1. Use tools (such as dependency) to check whether there is a corresponding interface in the encapsulated DLL

2. If not, you can import the def file when encapsulating the DLL

The name under EXPORTS corresponds to the C++ interface

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;
	}
		
}

Import the module definition file in the project properties

VB code:

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

The call was successful!

Five. C++ calling convention declaration conflict

solution:

It is also caused by the default import modifier of __cdecl, so __stdcall should also be added to the definition type of the C++ call layer

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;
}

 

Guess you like

Origin blog.csdn.net/sm9sun/article/details/84986875