由于工作缘故,经常要与串口使用打交道,但每次新建一个工程,都得重新创建一个关于串口的对话框,
如此多次之后,甚为繁琐,所以最近打算琢磨如何将其制作成DLL以重复使用,研究了许久,进展甚慢
-----------------------------------------------------------------------------------------------
DLL使用regular MFC dll方式,并且自己添加了对话框并生成该类,以下是主要代码// CPortDlg dialog
INT_PTR GetSerialPort(CStringArray &arrCom)
{
arrCom.RemoveAll();
HKEY hkey;
LONG32 lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
NULL,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS|KEY_READ,&hkey);
if (lRes == ERROR_SUCCESS)
{
TCHAR tchKey[MAX_PATH];
TCHAR tchValue[20];
DWORD dwIndex = 0;
DWORD dwType = REG_SZ;
while (lRes ==ERROR_SUCCESS)
{
DWORD dwCnt = MAX_PATH;
DWORD dwVCount = 20;
lRes = RegEnumValue(hkey,dwIndex++,tchKey,&dwCnt,NULL,&dwType,(LPBYTE)tchValue,
&dwVCount);
if (lRes == ERROR_SUCCESS)
{
if ((dwVCount>0)&&(dwCnt>0))
{
arrCom.Add(tchValue);
}
}
}
}
RegCloseKey(hkey);
return arrCom.GetSize();
}// CPortDlg message handlers
void CPortDlg::ScanPort(UINT nID)
{
((CComboBox*)GetDlgItem(nID))->ResetContent();
CStringArray arrayStrCom;
for (int i=0;i<GetSerialPort(arrayStrCom);i++)
{
((CComboBox*)GetDlgItem(nID))->AddString(arrayStrCom.GetAt(i));
}
int nIndex = 0;//((CComboBox*)GetDlgItem(IDC_CMB_COM_NO))->FindStringExact(-1,strComm);
((CComboBox*)GetDlgItem(nID))->SetCurSel(nIndex);
}
void CPortDlg::OnDropdownComboPort() 
{
// TODO: Add your control notification handler code here
ScanPort(IDC_COMBO_PORT);

}void CPortDlg::OnSelchangeComboPort() 
{
// TODO: Add your control notification handler code here
int nSel = ((CComboBox*)GetDlgItem(IDC_COMBO_PORT))->GetCurSel();
CString str;
((CComboBox*)GetDlgItem(IDC_COMBO_PORT))->GetLBText(nSel,str);
str = str.Right(str.GetLength() - 3 );
m_nPort = _ttoi(str.GetBuffer(str.GetLength()));
str.ReleaseBuffer();
}
int CPortDlg::GetSelectedPort()
{
return m_nPort;
}BOOL CPortDlg::OnInitDialog() 
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}void CPortDlg::OnClose() 
{
// TODO: Add your message handler code here and/or call default
theApp.DecreseRef();
CDialog::OnClose();
}void CPortDlg::OnOK() 
{
// TODO: Add extra validation here
theApp.DecreseRef();
  CDialog::OnOK();
}...
class CMFCDlgDll1App : public CWinApp
{
public:
CMFCDlgDll1App();
int GetRef(){ return m_nRef; }//获取引用计数器
void AddRef(){ m_nRef++; }//递增
void DecreseRef(){ if(m_nRef>0)m_nRef--;}//递减
CPortDlg* GetDlg(){ return m_pDlg; }
BOOL IsDlgNull(){ if(m_pDlg == NULL)return TRUE;else return FALSE; }
void DestroyDlg(){ m_pDlg->DestroyWindow();delete m_pDlg;m_pDlg = NULL;}
void CreateDlg(){ m_pDlg = new CPortDlg; m_pDlg->Create(IDD_DLG_PORT); }
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMFCDlgDll1App)
//}}AFX_VIRTUAL //{{AFX_MSG(CMFCDlgDll1App)
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
int m_nRef;//引用计数器
CPortDlg* m_pDlg;
};以下是2张效果图,简陋为之 然后接下来是导出函数CMFCDlgDll1App::CMFCDlgDll1App()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
m_nRef = 0;
m_pDlg =  NULL;
}int ShowDlg()
{
//此处创建后dlg为0
//使用模态对话框无法成功
//只能使用非模态进行显示
AFX_MANAGE_STATE(AfxGetStaticModuleState());
int i = 0; 
if (theApp.GetRef()>=1)//已经创建了
{
i = -1;
}
else
{
   if(theApp.IsDlgNull()==TRUE)
{
theApp.CreateDlg();
}
theApp.GetDlg() ->CenterWindow();
theApp.GetDlg() ->ShowWindow(SW_SHOWNORMAL);

theApp.AddRef();
i = 0;
}
return i;
}
int GetPortNum()
{    //切换资源使用
AFX_MANAGE_STATE(AfxGetStaticModuleState());
int nPort = theApp.GetDlg()->GetSelectedPort();
return nPort;
}
在def文件中
输出2个函数
EXPORTS
    ; Explicit exports can go here
ShowDlg//用于显示串口
GetPortNum//用于获取串口号然后如此处理之后,只能说是可以选择并确定好串口号,但这还不够,接下来的问题,是当选择串口完毕之后,如何通知程序呢?
因为在主窗口之中,我也能是进行显示,而不能在选择完毕之后,主动的将串口打开,望熟悉DLL操作的前辈指点一二void CDllTest1Dlg::OnOK() 
{
// TODO: Add extra validation here

ShowDlg() ;另,当这个完成的差不多时,应该会把代码都开出来,敝帚自珍也是无益。DLL非模态

解决方案 »

  1.   

    初始化时设置一个通知句柄(函数接口实现)
    在dll中的非模态对话框确定的时候, 通过消息传递串口号, 比如COM1中的数字1.
      

  2.   

    使用消息传递是一个比较方便的方法,主要是这个方法比较常用,函数指针很少用,等后续研究;
    于是在代码中添加了void CPortDlg::OnOK() 
    {
    // TODO: Add extra validation here
    theApp.DecreseRef();
    PostMsgToMain();
      CDialog::OnOK();
    }
    void CPortDlg::PostMsgToMain()
    {//protected:
    // HWND m_hMainWnd; //主窗口句柄 ::PostMessage(m_hMainWnd,WM_GETPORTNUM,0,(LPARAM)m_nPort);//将串口号传递出去
    }
    如此一来,在对话框确认之后,便将串口号发送给主窗口;
    然而现在便是3楼所说的句柄问题,
    在Portdlg.h中设置
    void SetHwnd(HWND hWnd){ m_hMainWnd = hWnd; }
    以下这个是导出函数
    void SetHwnd(HWND hMainWnd)
    {
    theApp.GetDlg()->SetHwnd(hMainWnd);
    }
    并且在def文件中
      SetHwnd @1
    ShowDlg @2然后DLL编译的时候提示.\MFCDlgDll1.def : warning LNK4022: cannot find unique match for symbol "SetHwnd"
    .\MFCDlgDll1.def : warning LNK4002: "void __cdecl SetHwnd(struct HWND__ *)" (?SetHwnd@@YAXPAUHWND__@@@Z) defined in .\Debug\MFCDlgDll1.obj
    .\MFCDlgDll1.def : warning LNK4002: "public: void __thiscall CPortDlg::SetHwnd(struct HWND__ *)" (?SetHwnd@CPortDlg@@QAEXPAUHWND__@@@Z) defined in .\Debug\MFCDlgDll1.obj看来参数传递进来的方式是错误的,但不知如何修改。
    若是在ShowDlg中传递参数,也是有错误,这点令我百思不得其解。
    感谢LS几位的回复
      

  3.   

    经过查找,编译器错误已经找到,关于LNK4022的问题,主要是导出声明不正确,错误说明看这里http://blog.csdn.net/zhangjinqing1234/article/details/7276058
    然后根据其指示,把句柄传递函数声明为
    在调用dll的dlg.cpp的头部
    extern "C" __declspec(dllexport) void   SetHwnd(HWND hMainWnd);
    同时还要在DLL文件中进行该声明然后主程序则修改为void CDllTest1Dlg::OnOK() 
    {
    // TODO: Add extra validation here
        ShowDlg() ;
        SetHwnd(this->GetSafeHwnd());
     }
    ON_MESSAGE(WM_GETPORTNUM,OnGetPortNum)
    LRESULT CDllTest1Dlg::OnGetPortNum(WPARAM w,LPARAM l)//串口选择窗口点击确认后,进入该处,那么就可以确定串口号了
    {
    UINT nPort = (UINT)l;
    if (nPort>0)
    {
    m_nPort = nPort;
    }
    return 0;
    }接下来已经确定串口号,然后创建serialport类,将其与窗口结合的部分。