C#调Qt C++封装的dll以及C++回调

3 篇文章 0 订阅

C#调Qt C++封装的dll以及C++回调

最近这几天一直在研究用C++封装成dll然后在winform里调用,包括调用C++里的回调函数,记录一下,学习的过程
我这个工程是用Qt C++封装一个往Linux上传文件的dll,然后在winform里调用,过程中遇到很多坑,简单记录一下:
1. 需要注意的就是C#和C++的字段对应关系,C#里用string,这里传参的时候是char*。
2. C#默认编码是GB2312,Qt默认编码是UTF-8,导致中文传递时出现乱码,我在处理方法是:在本地把string转成utf8编码的byte数组,然后再用vs的默认编码转成string,这样传递过去之后,就是正确的中文了,见文档最下方的ConvertToCppStr方法和ConvertFromCppStr方法
3. DllImport时,dll设置相对路径时提示找不到文件,百度了一下,dll依赖其他dll,寻找其他依赖dll时回去三个地方找:
exe文件所在路径 windows/system32 系统设置的环境变量路径
4. 第二点关于中文转换的问题,部分中文还是无法正确转换,后来百度发现了另外两种处理方式,见另一篇博文:C#调用C++封装的dll返回字符串

windows报出的错误基本都能百度到,C++那边报错就很费解了,我是通过百度和写日志的方法,定位问题语句,花了很多时间

Qt怎么新建dll工程很简单,这里省略了,直接上代码:
C++部分 主类 .h文件 关键代码

#ifndef UPLOADFILE_H
#define UPLOADFILE_H

#include "uploadfile_global.h"
#include <QMap>

//定义上传进度方法 和C#保持一致,回头C#定义的委托要传入给此对象
typedef void (*SetProgress)(int, double, const char*);

class UPLOADFILESHARED_EXPORT UploadFile : public QObject
{
    Q_OBJECT

public:
    UploadFile();
    ~UploadFile();

    //添加文件
    void addFile(int index, const char* filePath);
    //打开连接
    bool OpenConn(const char* host, const char* username, const char* pwd, short port, const char* rootpath, char* &error);
    //上传
    bool Upload();

    //设置回调函数 C#的委托作为参数传入
    void setCallBackFunc(SetProgress);

private:
    //上传进度回调函数 C#的委托赋给该变量 然后执行该方法
    SetProgress ProgressCallBackFunc;

public slots:
    //接收上传进度
    void setProgress(int index, curl_off_t unow, curl_off_t utotal, double speed);
};

对应的.cpp文件 关键代码

#include "UploadFile.h"
#include "qmd5.h"
#include <qfileinfo.h>
#include <QMetaType>

//构造函数
UploadFile::UploadFile()
{
    CUrlFtp::initalCurl();

    m_ftp = new CUrlFtp();

	//注册Curl里的数据类型
    qRegisterMetaType<curl_off_t>("curl_off_t");
    //连接Curl上传进度的信号槽
    bool b = connect(m_ftp, &CUrlFtp::sendProgress, this, &UploadFile::setProgress, Qt::DirectConnection);
}

//析构函数
UploadFile::~UploadFile()
{
    CUrlFtp::clearCurl();
    if(m_ftp != NULL)
    {
        delete m_ftp;
        m_ftp = NULL;
    }
}

//打开远程连接
bool UploadFile::OpenConn(const char* host, const char* username, const char* pwd, short port, const char* rootpath, char* &error)
{
    if(m_connInfo == NULL) m_connInfo = new ConnInfo();

    m_connInfo->hostName = host;
    m_connInfo->userName = username;
    m_connInfo->password = pwd;
    m_connInfo->rootPath = rootpath;

    if(m_ssh == NULL)
    {
        m_ssh = new SshTools();
    }

    if(m_ssh->connectToServer(host, username, pwd) != 0)
    {
        m_ssh->closeSsh();
        delete m_ssh;
        m_ssh = NULL;
        const char* c = m_ssh->getLastError();
        strcpy(error, c);

        return false;
    }

    if(m_ftp->connect(username, pwd, host, port, 5) != 0)
    {
        const char* c = m_ftp->getLastError();
        strcpy(error, c);
        return false;
    }

    return true;
}

//上传文件的方法
bool UploadFile::Upload()
{
    if(m_connInfo == NULL)
    {
        return false;
    }

    foreach(int index, m_filePathMap.keys())
    {
        UploadFileInfo *info = new UploadFileInfo();
        info->index = index;
        info->filePath = m_filePathMap[index];
        QFileInfo f(QString::fromStdString(info->filePath));
        info->remoteFilePath = m_connInfo->rootPath;
        info->remoteFilePath.append("/");
        info->remoteFilePath.append(f.fileName().toStdString());
        info->ftp = m_ftp;
        info->ssh = m_ssh;

        m_uploadFileInfoMap.insert(index, info);
		//多线程上传
        TransferFileThread* thread = new TransferFileThread(info);
        
        m_uploadThreadMap.insert(index, thread);
        thread->start();
    }
}

//上传进度槽函数
void UploadFile::setProgress(int index, curl_off_t unow, curl_off_t utotal, double speed)
{
    if(!m_uploadFileInfoMap.contains(index) || utotal == 0) return;
    UploadFileInfo* info = m_uploadFileInfoMap[index];
    //进度
    double progress = unow*1.0/utotal*100.0;
    if(progress - info->progress > 0.1 || progress == 100)
    {
        info->progress = progress;

        ProgressCallBackFunc(index, progress, sp.c_str());
    }
}

//设置回调函数 参数是C#定义的委托
void UploadFile::setCallBackFunc(SetProgress progressFunc)
{
    ProgressCallBackFunc = progressFunc;
}

以上是C++中主类的关键代码,下面新建一个C++的类文件,作为C#和C++通信的接口,看代码:
.h文件

#ifndef FILEUPLOADCALLER_H
#define FILEUPLOADCALLER_H

#include <QObject>
#include "UploadFile.h"

extern "C" {

//创建文件上传类 C#端调用该方法,获取主类的对象指针
extern Q_DECL_EXPORT UploadFile* CreateUploadFileClass();

//清除文件上传类 释放C++里创建的对象 主类对象指针作为参数传递进来,在C++端销毁
extern Q_DECL_EXPORT void DisposeUploadFileClass(UploadFile*);

//添加要上传的文件
extern Q_DECL_EXPORT void AddFilePath(UploadFile*, int index, char* filePath);

//打开连接
extern Q_DECL_EXPORT bool OpenCon(UploadFile*, char* host, char* userName, char* pwd, short port, char* uploadRootPath, char* &error);

//上传文件
extern Q_DECL_EXPORT bool UploadF(UploadFile*);

//设置回调函数 外部调用 C#端创建的委托作为参数传进来
extern Q_DECL_EXPORT void SetCallBackFunc(UploadFile*, SetProgress progressCallBackFunc);
}

#endif // FILEUPLOADCALLER_H

.cpp代码

#include "FileUploadCaller.h"

//创建文件上传类
UploadFile* CreateUploadFileClass()
{
    UploadFile* f = new UploadFile();

    return f;
}

//清除文件上传类
void DisposeUploadFileClass(UploadFile* uploadClass)
{
    if(uploadClass != NULL)
    {
        delete uploadClass;
        uploadClass = NULL;
    }
}

//添加文件
void AddFilePath(UploadFile* uploadClass, int index, char* filePath)
{
    uploadClass->addFile(index, filePath);
}

//打开连接
bool OpenCon(UploadFile* uploadClass, char* host, char* userName, char* pwd, short port, char* uploadRootPath, char* &error)
{
    return uploadClass->OpenConn(host, userName, pwd, port, uploadRootPath, error);
}

//上传文件
bool UploadF(UploadFile* uploadClass)
{
    return uploadClass->Upload();
}

//设置回调函数 外部调用
void SetCallBackFunc(UploadFile* uploadFile, SetProgress progressCallBackFunc)
{
    uploadFile->setCallBackFunc(progressCallBackFunc);
}

C#端代码 使用时创建这个类对象 调用其方法就行了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Tamir.SharpSsh;

namespace ORBP.Archive.USL
{
    /// <summary>
    /// 通过调用Qt C++封装的dll进行文件上传的类
    /// </summary>
    public class UploadFileClass
    {
        #region 调用c++上传dll相关方法

        /// <summary>
        /// 上传进度委托
        /// </summary>
        /// <param name="index">文件索引</param>
        /// <param name="progress">进度百分比</param>
        /// <param name="speed">速度</param>
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void ProgressCallBack(int index, double progress, string speed);

        /// <summary>
        /// 文件完整性委托
        /// </summary>
        /// <param name="index"></param>
        /// <param name="complete"></param>
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void CompleteCallBack(int index, bool complete);

        /// <summary>
        /// 创建上传类
        /// </summary>
        /// <returns></returns>
        [DllImport(@"UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr CreateUploadFileClass();

        /// <summary>
        /// 销毁上传类
        /// </summary>
        /// <param name=""></param>
        [DllImport("UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern void DisposeUploadFileClass(IntPtr ptr);

        /// <summary>
        /// 添加文件
        /// </summary>
        /// <param name="filePath"></param>
        [DllImport("UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        //public static extern void AddFilePath(IntPtr ptr, string filePath, ref string err);
        public static extern void AddFilePath(IntPtr ptr, int index, string filePath);

        /// <summary>
        /// 打开连接
        /// </summary>
        /// <param name="host"></param>
        /// <param name="username"></param>
        /// <param name="pwd"></param>
        /// <param name="port"></param>
        /// <param name="rootpath"></param>
        /// <returns></returns>
        [DllImport("UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool OpenCon(IntPtr ptr, string host, string username, string pwd, short port, string rootpath, ref string error);

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="ptr"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        [DllImport("UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool UploadF(IntPtr ptr);

        /// <summary>
        /// 上传进度进度和完整性委托函数
        /// </summary>
        /// <param name="cb1"></param>
        /// <param name="cb2"></param>
        [DllImport("UploadFile.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetCallBackFunc(IntPtr ptr, ProgressCallBack cb1);

        #endregion 调用c++上传dll相关方法

        #region 变量

        /// <summary>
        /// 表示c++ dll中的上传类指针
        /// </summary>
        private IntPtr m_uploadFileClass = IntPtr.Zero;

        /// <summary>
        /// 上传进度委托 这里定义成static 否则会报错
        /// </summary>
        private static ProgressCallBack m_progressCallBackDel;

        #endregion 变量

        #region 构造函数

        /// <summary>
        /// 上传文件处理类构造函数
        /// </summary>
        public UploadFileClass()
        {
        	//创建C++类对象指针
            m_uploadFileClass = CreateUploadFileClass();
			//new委托对像
            m_progressCallBackDel = new ProgressCallBack(GetProgress);
			//委托传递给C++
            SetCallBackFunc(m_uploadFileClass, m_progressCallBackDel);
        }

        #endregion 构造函数

        #region 公共方法

        /// <summary>
        /// 销毁c++ dll中类对象指针
        /// </summary>
        public void DisposeUploadClass()
        {
            DisposeUploadFileClass(m_uploadFileClass);
            m_uploadFileClass = IntPtr.Zero;
        }

        /// <summary>
        /// 添加文件
        /// </summary>
        /// <param name="index">文件索引 不能重复</param>
        /// <param name="filePath"></param>
        public void AddFile(int index, string filePath)
        {
            filePath = this.ConvertToCppStr(filePath);

            AddFilePath(m_uploadFileClass, index, filePath);
        }

        /// <summary>
        /// 连接远程服务器
        /// </summary>
        /// <param name="host"></param>
        /// <param name="username"></param>
        /// <param name="pwd"></param>
        /// <param name="port"></param>
        /// <param name="rootpath"></param>
        /// <returns></returns>
        public bool Connect(string host, string username, string pwd, short port, string rootpath)
        {
            try
            {
                string str = this.ssh_conn(host, username, pwd, "");
                string error = "";
                bool s = OpenCon(m_uploadFileClass, host, username, pwd, port, rootpath, ref error);
                if (!s)
                {
                    error = this.ConvertFromCppStr(error);
                    throw new Exception(error);
                }

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        public void Upload()
        {
            try
            {
                bool s = UploadF(m_uploadFileClass);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion 公共方法

        #region 私有方法

        /// <summary>
        /// 将字符串转成C++可识别的字符串(文件名带中文要做转换)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string ConvertToCppStr(string str)
        {
            byte[] b = System.Text.Encoding.UTF8.GetBytes(str);
            str = System.Text.Encoding.Default.GetString(b);

            return str;
        }

        /// <summary>
        /// 将C++字符串转成VS可识别的字符串(C++传出参数要做转换)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string ConvertFromCppStr(string str)
        {
            byte[] b = System.Text.Encoding.Default.GetBytes(str);
            str = System.Text.Encoding.UTF8.GetString(b);

            return str;
        }

        #endregion 私有方法
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值