求助:VC调用易语言DLL问题
易语言DLL如下:VC调用
#include <string>
using namespace std;
void CaaaaDlg::OnBnClickedButton2() //这个有问题
{
HINSTANCE hMod = LoadLibrary("testdll.dll");
typedef string (WINAPI *pnfOrgMYFUNC)();
pnfOrgMYFUNC CallFuntionName = (pnfOrgMYFUNC)GetProcAddress(hMod,"test");
string s = CallFuntionName();
MessageBox(s.c_str());
FreeLibrary(hMod);
}
void CaaaaDlg::OnBnClickedButton3() //这个正常
{
HINSTANCE hMod = LoadLibrary("testdll.dll");
typedef char* (WINAPI *pnfOrgMYFUNC)();
pnfOrgMYFUNC CallFuntionName = (pnfOrgMYFUNC)GetProcAddress(hMod,"test");
char *s = CallFuntionName();
MessageBox(s);
FreeLibrary(hMod);
}
为什么使用string类似就有问题,出现异常,而使用char *就正常,最基本的原因是什么,求助各位大佬解惑。
上传下成品,可以直接看到效果。
https://52cn.lanzouj.com/iNzRkmr25ta
# 出现错误的原因
## 代码
```c
#include "stdafx.h"
#include <windows.h>
#include <string>
using namespace std;
void OnBnClickedButton2() //这个有问题
{
HINSTANCE hMod = LoadLibrary("testdll.dll");
typedef string (WINAPI *pnfOrgMYFUNC)();
pnfOrgMYFUNC CallFuntionName = (pnfOrgMYFUNC)GetProcAddress(hMod,"test");
CallFuntionName();
FreeLibrary(hMod);
}
int main(){
OnBnClickedButton2();
}
```
## 运行结果
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310124532091.png)
## 分析结果
结果内容为:
The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
翻译内容为:
ESP的值没有通过一个函数调用被妥善保存 ,这通常是用一个调用约定调用一个函数,而用另一个调用约定调用一个函数指针的结果
------
这里的重点为:different calling convention,不同的调用协定
也就是说声明的pnfOrgMYFUNC并不匹配实际获取到的功能
楼主也发现将char* 改为返回值的话就可以正常运行,所以归根结底问题出在string 和 char*上
于是问题转化为分析string 和 char*的不同
# 分析char* 和 string
想要了解char* 和 string 最快的办法就是分析其汇编
## 代码
```c
#include "stdafx.h"
#include <string>
void function(){
string a;
char* b;
a="this is a";
b="this is b";
}
int main(){
function();
}
```
## 反汇编代码
```assembly
25: string a;
004011BD lea eax,
004011C0 push eax
004011C1 lea ecx,
004011C4 call @ILT+105(std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_str
004011C9 mov dword ptr ,0
26: char* b;
27: a="this is a";
004011D0 push offset string "this is a" (00426040)
004011D5 lea ecx,
004011D8 call @ILT+35(std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=)
28: b="this is b";
004011DD mov dword ptr ,offset string "this is b" (00426034)
```
## 反汇编分析
直接看对应的赋值语句
### string的赋值
```assembly
27: a="this is a";
004011D0 push offset string "this is a" (00426040)
004011D5 lea ecx,
004011D8 call @ILT+35(std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=)
```
------
1.首先将要赋值的字符串作为参数压入堆栈
```assembly
004011D0 push offset string "this is a" (00426040)
```
------
2.然后将ebp-1Ch的地址传给ecx
```assembly
004011D5 lea ecx,
```
ebp-1Ch的地址是什么?观察内存窗口
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310130549281.png)
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310130709143.png)
可以发现ebp-1Ch其实就是a的地址
------
3.调用string相关的call
```assembly
004011D8 call @ILT+35(std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=)
```
### char*的赋值
相比string,char*的赋值就简单多了,直接赋值即可
```assembly
28: b="this is b";
004011DD mov dword ptr ,offset string "this is b" (00426034)
```
------
# 出错的反汇编分析
有了前面的铺垫大致了解了string 和 char*的区别后,现在来用反汇编分析出错的原因
## string反汇编
首先要明确出错的地方为调用函数处,即CallFuntionName这里,直接看反汇编:
```assembly
16: CallFuntionName();
004010AA mov esi,esp
004010AC lea ecx,
004010AF push ecx
004010B0 call dword ptr
004010B3 cmp esi,esp
004010B5 call __chkesp (00401460)
004010BA lea ecx,
004010BD call @ILT+30(std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_str
17:
18: FreeLibrary(hMod);
```
------
看到函数里的call __chkesp 就知道报错是这个函数引发的,也就是**堆栈不平衡**引起的错误
为什么使用string会引起堆栈不平衡?而使用使用char* 不会?
## 对比char* 反汇编
```assembly
16: CallFuntionName();
004010AA mov esi,esp
004010AC call dword ptr
004010AF cmp esi,esp
004010B1 call __chkesp (00401460)
17:
18: FreeLibrary(hMod);
```
------
可以看到无论是string还是char\* ,都调用了call dword ptr ,不同的是string还需要lea ecx和push ecx,压入string所需的参数,因而导致了后续的堆栈不平衡
# 总结
string和char\* 的区别在于要压入参数和调用string相关的call,而char* 则是直接赋值即可,前者破坏了堆栈的平衡而引发了错误 std::string 可以接收char* 参数直接构造对象。
如果你拿一个char* 内存直接 强转到std::string,应该会报错。 typedef string有这样写法吗??
一般都是typedef BOOL 或者typedef char*STRING
电脑没有vc,没法测试 易语言的 `文本型` 是 `const char *` 。`std::string` 这种C++的STL库中的内容是C++语言独有的,不要用来和其它语言的C接口做交互。
页:
[1]