朱朱你堕落了 发表于 2021-3-10 09:33

求助: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

lyl610abc 发表于 2021-3-10 09:33

# 出现错误的原因

## 代码

```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* 则是直接赋值即可,前者破坏了堆栈的平衡而引发了错误

无痕软件 发表于 2021-3-10 09:49

std::string 可以接收char* 参数直接构造对象。

如果你拿一个char* 内存直接 强转到std::string,应该会报错。

Takitooru 发表于 2021-3-10 10:02

typedef string有这样写法吗??
一般都是typedef BOOL 或者typedef char*STRING
电脑没有vc,没法测试

qzhsjz 发表于 2021-3-10 11:05

易语言的 `文本型` 是 `const char *` 。`std::string` 这种C++的STL库中的内容是C++语言独有的,不要用来和其它语言的C接口做交互。
页: [1]
查看完整版本: 求助:VC调用易语言DLL问题