C#的WinForm中嵌入Qt界面库

C#的WinForm中嵌入Qt界面库

为何会有如此奇葩需求

  • 一处代码,多处运行

C#有两种写界面的框架,winFrom和WPF,这二者写的界面可以相互调用,但C#编写的界面组件在非托管C++虽然理论上可以,但是这种类似反射的机制使用其他来说特别不舒服。而对于编写组件式SDK而言,核心的界面组件可以在C++、C#、JAVA中使用而无需修改太多的代码是我们的终级目标。

  • ocx 编写难度太大

使用MFC编写的OCX控件可以嵌套在各种高级语言甚至浏览器中,但对于新时代的开发人员而言,会MFC开发的越来越少,开发难度也比较大,开发MFC的性价比并不高。对于使用C++编写界面的人员来说,用Qt是大多数人的选择。

  • 跨平台

ocx 不能跨平台,导致了无法在除windows操作系统下使用ocx控件,但是Qt的界面可以在跨平台环境下使用。

嵌入原理

界面的可视化本质上是使用绘图引擎绘制出来的,对于MFC、WinForm、Qt而言在编写界面库的本质上是没有太大的区别的,即都是基于windows的窗口句柄的绘制,windows SDK的很多函数都是需要指定窗口句柄(如控制窗口的消息循环、Hook钩子等)。其次解决消息发送问题。MFC、WinForm使用的是windows的消息机制,而Qt使用的信号槽机制(幸运的是Qt也可以接收windows的消息)

也就是说基于Qt编写界面dll理论上可以在MFC、Qt、JAVA、C# WinFrom以及WPF(这个还是要嵌入winfrom外壳)中使用,夸大点来说是可以在任何语言中调用

实现步骤

使用qtwinmigrate开源库

正常情况下,qt的界面库必须是qt的主程序调用才行,但是qtwinmigrate库在内部解决了qt主程问题。我们只需要使用即可.
qtwinmigrate 库的下载地址 https://github.com/qtproject/qt-solutions.git

使用步骤
  • 新建一个C++的dll,将qtwinmigrate库的三个类(qwinhost、qmfcapp、qwinwidget三个类对应的头文件和cpp文件)文件拷贝到自己的工程目录下,并加入到工程中
  • 在dll入口函数dllmain 中加入如下代码
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReserved*/ )
{
    static bool ownApplication = FALSE;

    if ( dwReason == DLL_PROCESS_ATTACH )
    {
        //在dll加载的时候完成qapplication的创建,在内部通过SetWindowsHookEx这个函数解决消息的发送问题
	   ownApplication = QMfcApp::pluginInstance( hInstance ); 
    }
	if (dwReason == DLL_PROCESS_DETACH && ownApplication)
	{
		delete qApp;//退出的时候delete qApp;
	}
    return TRUE;
}
  • 编写一个C++类来管理一个界面
class SampleWidget
{
public:
    SampleWidget(void* ptr)
    {
        _win_widget=new QWinWidget((HWND)ptr);//通过句柄创建一个QWinWidget类,这个
        QToolBar* tool_bar = new QToolBar(win);
       	tool_bar->addAction("open");
	    tool_bar->addAction("close");
	    QComboBox* combo_box = new QComboBox();
	    combo_box->addItem("zhang san");
	    combo_box->addItem("li si");
        QVBoxLayout* box_layout = new QVBoxLayout(win);
        box_layout->addWidget(tool_bar);
        box_layout->addWidget(combo_box);
        QListView* list_view = new QListView(win);
        box_layout->addWidget(list_view);
        win->setLayout(box_layout);
        win->move(0, 0);
        win->show();
    }
    ~ SampleWidget()
    {
        delete _win_widget;
    }
   private:
     QWinWidget* _win_widget;
}
  • 使用Swig生成对应的cxx文件和相应的C#文件

notes:在前面的一系列文章中,对swig有相应的使用说明

//## 建立对应的sample.i文件
%module(directors="1") sampledll
%{
    #include "SampleWidget.h"   
%}

/* turn on director wrapping Callback */
%apply void *VOID_INT_PTR { void * }
%include "SampleWidget.h"

建立好swig的i文件之后执行命令

swig -c++ -csharp  example.i //最简单的swig使用

执行之后将生成sample_wrap.cxx加入到dll工程中一起编译(smapledll是dll的名字),注意名字要跟你sample_wrap.cxx所在的工程名一致,负责会出现调用错误.

  • 新建C# winFrom程序,将生成C#文件纳入工程中
public partial class Form1 : Form
    {
        SampleWidget _sample_widget;
        public Form1()
        {
            InitializeComponent();
            _sample_widget=new  SampleWidget(this.Handle);
        }
    }

测试结果

image.png
可以看到窗体风格之类的也没有什么太大的问题,一个简单的类com组件的控件就完成了(窗口大小这样不会自动布满屏幕,需要重写winfrom中的大小改变的方法,还有一种方法是在C++截获From1这个的句柄消息如以下方式(这样对C#研发人员来说就只要写一句代码即可)

static LRESULT CALLBACK WindowsProc(HWND win_hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case  WM_SIZE:
	{
		int nHeight = HIWORD(lParam);
		int nWidth = LOWORD(lParam);
		if (_win_widget)
		{
			_win_widget->setFixedSize(nWidth, nHeight);
		}
		break;
	}
	default:
		return DefWindowProc(win_hwnd, message, wParam, lParam);
	}
	return 0;
}

SampleWidget(void* ptr,bool full_screen) //之前的Sample构造函数
{
    _win_widget=new QWinWidget((HWND)ptr);//通过句柄创建一个QWinWidget类,这个
    QToolBar* tool_bar = new QToolBar(win);
    tool_bar->addAction("open");
    tool_bar->addAction("close");
    if(full_screen)
    {
        SetWindowLongPtr((HWND)parent, -4, (LONG_PTR)WindowsProc);//parent是之前传入的窗口句柄
    }
    QComboBox* combo_box = new QComboBox();
    combo_box->addItem("zhang san");
    combo_box->addItem("li si");
    QVBoxLayout* box_layout = new QVBoxLayout(win);
    box_layout->addWidget(tool_bar);
    box_layout->addWidget(combo_box);
    QListView* list_view = new QListView(win);
    box_layout->addWidget(list_view);
    win->setLayout(box_layout);
    win->move(0, 0);
    win->show();
}

image.png
喜欢我就关注我吧

  • 10
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

揽月凡尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值