利用C++开发运动控制卡的动态链接库,在类的定义有包括了一些WINDOWS的信号量操作,但有些用户在使用QT环境调用时就发生了问题,如下图所示 。
该动态链接库的部分原始代码(头文件)如下所示:
class myNetSocket;
class CSerialPort;
class EXPORTS_DEMO MoCtrCard
{
public:
MoCtrCard();
~MoCtrCard();
// RS232 接口
McCard_UINT16 MoCtrCard_Initial(McCard_UINT8 ComPort);
McCard_UINT16 MoCtrCard_Unload();
// 四轴的接口
McCard_UINT16 MoCtrCard_MCrlAxisMove(McCard_UINT8 AxisId, McCard_INT8 SpdDir);
McCard_UINT16 MoCtrCard_MCrlAxisRelMove(McCard_UINT8 AxisId, McCard_FP32 DistCmnd, McCard_FP32 VCmnd, McCard_FP32 ACmnd);
private:
CRITICAL_SECTION mSemCommSync; ///< 互斥通讯操作
McCard_UINT8 nCmndCnt; ///< 指令计数器,每下发指令自增1
enum ePortLinkType nPortType; ///< 物理链路类型
myNetSocket* cTCPSocket; ///< 网络接口对像
CSerialPort* cRSPort; ///< RS232接口
};
为了实现对 private 部分进行隐藏,达到对用户不可见的目的,上网调研后发现有两种方法可以实现: 1)利用pimpl技术,2)纯虚函数形成接口类
通过对这两种方式的研究,决定使用pimpl方法进行对现有的DLL进行改造;那什么是pimp技术呢?参考MSDN
pimpl idiom 是一种新式 C++ 技术,用于隐藏实现、最小化耦合和分离接口。 Pimpl 是“指向实现的指针”的缩写。细节就不介绍了,而是直接套用实现。impl的实现有固定格式,如下所示:
// my_class.h
class my_class {
// ... all public and protected stuff goes here ...
private:
class impl; unique_ptr<impl> pimpl; // opaque type here
};
// my_class.cpp
class my_class::impl { // defined privately here
// ... all private data and functions: all of these
// can now change without recompiling callers ...
};
my_class::my_class(): pimpl( new impl )
{
// ... set impl values ...
}
在标头实现时,采用了c++的智能指针,因此在头文件中要加入
#include "memory"
在我们的DLL库中,具体实现方式为:
#ifndef _MYCODE_H_
#define _MYCODE_H_
#include "memory"
#ifdef MCC4DLL_EXPORTS
#define EXPORTS_DEMO _declspec( dllexport )
#else
#define EXPORTS_DEMO _declspec(dllimport)
#endif
class EXPORTS_DEMO MoCtrCard
{
public:
MoCtrCard();
~MoCtrCard();
// RS232 接口
McCard_UINT16 MoCtrCard_Initial(McCard_UINT8 ComPort);
McCard_UINT16 MoCtrCard_Unload();
McCard_UINT16 MoCtrCard_MCrlAxisMove(McCard_UINT8 AxisId, McCard_INT8 SpdDir);
McCard_UINT16 MoCtrCard_MCrlAxisRelMove(McCard_UINT8 AxisId, McCard_FP32 DistCmnd, McCard_FP32 VCmnd, McCard_FP32 ACmnd);
private:
class MoCtrCardImpl;
std::unique_ptr<MoCtrCardImpl> m_pImpl;
};
#endif
在CPP源文件中实现的Impl如下:
class myNetSocket;
class CSerialPort;
class MoCtrCard::MoCtrCardImpl
{
public:
MoCtrCardImpl() {};
CRITICAL_SECTION mSemCommSync; ///< 互斥通讯操作
McCard_UINT8 nCmndCnt; ///< 指令计数器,每下发指令自增1
enum ePortLinkType nPortType; ///< 物理链路类型
myNetSocket* cTCPSocket; ///< 网络接口对像
CSerialPort* cRSPort; ///< RS232接口
};
MoCtrCard::MoCtrCard():pimpl(new MoCtrCardImpl())
{
pimpl->nCmndCnt = 0x00;
pimpl->nPortType = ePortNone; ///< 物理链路类型
pimpl->cTCPSocket = new myNetSocket(); ///< 网络接口对像
pimpl->cRSPort = new CSerialPort(); ///< RS232接口
InitializeCriticalSection(&pimpl->mSemCommSync); ///< 收发信号量
}
经过以上改造后,DLL可以直接用在现有的应用程序中,在现有的DEMO程序上进行测试,原始程序不做任何修改,测试成功。