QT调用C#的DLL失败

0.参考链接

Qt程序中调用C#编写的dll(MingW版)-附源码:https://blog.csdn.net/weixin_42420155/article/details/81538889?utm_source=distribute.pc_relevant.none-task

Java的native方法:https://blog.csdn.net/wike163/article/details/6635321

什么是Native方法:https://www.jianshu.com/p/22517a150fe5

VS.chart:http://vschart.com/compare/vala/vs/native-c-c

Qt程序中调用C#编写的dll:https://blog.csdn.net/weixin_42420155/article/details/81060945

C#调用C++(QT5.5.1项目)的C++/CLI(CLR项目)项目技术笔记:https://blog.csdn.net/weixin_33878457/article/details/93697629

Native code:https://www.microsoft.com/china/msdn/x-platform/developing_07.html

利用VS2015将函数转化为dll文件并利用Qt调用dll文件:https://blog.csdn.net/qq_39308375/article/details/82492465

Visual Studio——调用约定与(动态)库详解:https://blog.csdn.net/qq_15859547/article/details/77658655

warning C4273: dll 链接不一致:https://blog.csdn.net/qc530167365/article/details/

带你玩转Visual Studio——调用约定__cdecl、__stdcall和__fastcall:https://blog.csdn.net/luoweifu/article/details/52425733

带你玩转Visual Studio——调用约定与(动态)库:https://blog.csdn.net/luoweifu/article/details/52456407

1.开发环境

①VS2015  ②qt5.9.4

2.当前问题

启用clr后,QT的控件会失效

3.解决方法

(1)我是已经有了现成的基于C#开发的DLL,这里我就不去搞C#的DLL了

(2)启用clr,然后开始编写相关的程序,代码如下:

//CString2StringClassLibrary.hpp

#pragma once

/*************************C++生成DLL****************************/

#ifndef MakeDLL
#define DLLAPI _declspec(dllexport)
#else
#define DLLAPI _declspec(dllimport)
#endif

/*************************自定义头文件************************************/

#ifndef CString2StringClassLibrary_H_
#define CString2StringClassLibrary_H_

/******************************引用外部C#的DLL*****************************************/

#ifdef _DEBUG
#using "../X64/Debug/String2StringClassLibrary.dll"
#else
#using "../X64/Release/String2StringClassLibrary.dll"
#endif

namespace CString2StringClassLibrary
{
	extern "C" DLLAPI bool Initialization();//调用String2StringClassLibrary中的Initialization函数
	extern "C" DLLAPI bool Dispose();//调用String2StringClassLibrary中的Dispose函数
	extern "C" DLLAPI System::String^ Input(System::String^ ts);//调用String2StringClassLibrary中的Input函数
	extern "C" DLLAPI System::String^ Output(System::String^ ts);//调用String2StringClassLibrary中的Output函数
}

#endif 

//CString2StringClassLibrary.cpp

/******************调用封装DLL宏定义*********************/
#define MakeDLL

/*********************调用自定义函数*******************************/

#include "CString2StringClassLibrary.h"

/********************Initialization函数************************/
bool CString2StringClassLibrary::Initialization()
{
	String2StringClassLibrary::S2S ^dll = gcnew String2StringClassLibrary::S2S();//启动代码托管
	bool flag = dll->Initialization();
	return flag;
}

/********************Dispose函数************************/
bool CString2StringClassLibrary::Dispose()
{
	String2StringClassLibrary::S2S ^dll = gcnew String2StringClassLibrary::S2S();
	bool flag = dll->Dispose();
	return flag;
}

/********************Input函数***********************/
System::String^ CString2StringClassLibrary::Input(System::String^ ts)
{
	String2StringClassLibrary::S2S ^dll = gcnew String2StringClassLibrary::S2S();
	System::String ^str = dll->Input(ts);
	return str;
}

/**********************Output函数****************************/
System::String^ CString2StringClassLibrary::Output(System::String^ ts)
{
	String2StringClassLibrary::S2S ^dll = gcnew String2StringClassLibrary::S2S();
	System::String ^str = dll->Output(ts);
	return str;
}

(3)

 

4.相关概念

(1)调用约定

函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的。它决定以下内容:(1)函数参数的压栈顺序,(2)由调用者还是被调用者把参数弹出栈,(3)以及产生函数修饰名的方法。我们知道函数由以下几部分构成:返回值类型 函数名(参数列表),如: void function();int add(int a, int b);

以上是大家所熟知的构成部分,其实函数的构成还有一部分,那就是调用约定。如下: void __cdecl function();int __stdcall add(int a, int b);

调用约定是函数类型的一部分,因此函数的声明和定义处调用约定要相同,不能只在声明处有调用约定,而定义处没有或与声明不同。假设函数A调用函数B,我们称A函数为”调用者”,B函数为“被调用者”。函数调用过程可以这么描述:(1) 先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。 (2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。 (3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。 (4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值(可以这么做是因为这个值在函数调用前一步被压入堆栈)。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态。 

<1>__cdecl的特点

__cdecl 是 C Declaration 的缩写,表示 C 和 C++ 默认的函数调用约定。是C/C++和MFCX的默认调用约定。

按从右至左的顺序压参数入栈;由调用者把参数弹出栈。切记:对于传送参数的内存栈是由调用者来维护的,返回值在EAX中。因此对于像printf这样可变参数的函数必须用这种约定;编译器在编译的时候对这种调用规则的函数生成修饰名的时候,在输出函数名前加上一个下划线前缀,格式为_function。如函数int add(int a, int b)的修饰名是_add;

<2>__stdcall的特点

__stdcall是Standard Call的缩写,是C++的标准调用方式,当然这是微软定义的标准,__stdcall通常用于Win32 API中(可查看WINAPI的定义)。按从右至左的顺序压参数入栈;由被调用者把参数弹出栈。切记:函数自己在退出时清空堆栈,返回值在EAX中;__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_function@number。如函数int sub(int a, int b)的修饰名是_sub@8。

<3>__fastcall的特点

__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的。实际上__fastcall用ECX和EDX传送前两个DWORD或更小的参数,剩下的参数仍自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈;__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@number,如double multi(double a, double b)的修饰名是@multi@16。__fastcall和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第1个参数进ECX,第2个进EDX,其他参数是从右向左的入栈,返回仍然通过EAX。

<4>__thiscall的特点

__thiscall是C++类成员函数缺省的调用约定,但它没有显示的声明形式。因为在C++类中,成员函数调用还有一个this指针参数,因此必须特殊处理,thiscall调用约定的特点:参数入栈:参数从右向左入栈;this指针入栈:如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入栈;栈恢复:对参数个数不定的,调用者清理栈,否则函数自己清理栈。

这里主要总结一下_cdecl、_stdcall、__fastcall三者之间的区别:

要点__cdecl__stdcall__fastcall
参数传递方式右->左右->左左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数自右向左压栈传送
清理栈方法调用者清理被调用函数清理被调用函数清理
适用场合C/C++、MFC的默认方式; 可变参数的时候使用;Win API要求速度快
C编译修饰约定_functionname_functionname@number@functionname@number

(2)Native code

开发 Windows Runtime 的软件一般情况下都意味着开发的是托管代码。通常造成性能问题的不是托管代码和底层的runtime,而是因为糟糕的架构和软件的设计。在你要做的每一个方面都重新思考一下你的软件架构,并且仅仅通过转向另一种程序模型无法给你的应用程序带来显著的性能提升的时候,如果你认为用原生的C++来代替你Windows Phone/Windows8中的C#代码就能获得性能提升的话,请你最好三思。那绝对不是你应该做的事情,因为结果一定会让你大失所望。关于在托管和非托管代码之间的通信,就像在 Windows 8 中,Windows Runtime使您可以让不同的技术以非常自然的方式互相交流。特别是,我们可以通过创建 Windows Runtime的简单包装,实现将非托管 c + + 实现集成到托管的应用程序中。他们可以很容易的融合与C#,并且非常像.net中的东西。

 

_____image_12.png (313×366)

 native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。

5.失败原因

Qt程序是纯native c++代码(unmanaged代码),而CppDll.dll和CSharpDll.dll是managed代码,因此Qt程序通过动态链接到CppDll.dll时,Qt程序本身是以native c++代码执行,而在调用CppDll.dll中的代码时,CppDll.dll中代码的加载是以.Net运行时进行托管,也即CppDll.dll被加载时,是被.Net Framework进行host启动的,然后CppDll.dll中的代码可以进一步访问CSharpDll.dll(此时CSharpDll.dll也是被.Net Framework进行host启动的)。所以如果在Qt程序中将CppDll进行静态链接,其生成的Qt程序中最后必然包括maneged代码(即Qt程序中既有native又有managed代码),最终就会造成Qt程序在运行中无法明确其启动方式(按navtive启动,里面的执行都是navtive方式,遇到managed代码,无法正常调用;如果按.Net进行host启动,则其native代码无法被.Net host识别),从而导致链接失败,当然,至于是否有其他方式绕过managed和unmanaged的限制并能以这种静态链接方式进行构建,还需进一步查阅了。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值