基于CSerialPort修改类的串口调试助手编写过程中遇到的问题总结

来源:互联网 发布:linux帮助命令man 编辑:程序博客网 时间:2024/06/16 06:27

 

如需转载请标明出处:http://blog.csdn.net/itas109

本文是对 基于CSerialPort修改类的串口调试助手源代码(支持中文、自动保存等)该文的补充,

其中介绍了编写过程中遇到的问题

1、下拉控件

类型设置为下拉列表,在选择的时候就不可以编辑了

2_T("打开串口")

3Control ID可以对应2个不同的Type

4MFC显示图标

m_IconCtrl.SetIcon(AfxGetApp()->LoadIcon(IDI_ICON_OFF));

5、关于

void  *LDlg::OnAbout() ///关于

{

         CAboutDlg dlg;

         dlg.DoModal();

}

6、菜单问题

不用预留一行位置,其他控件直接放到最顶端即可。

7combobox清除下拉列表

ResetContent清楚下拉列表

 

换一种思维:
先读取文本框中的内容保存至一个字符串变量,然后用ResetContent清除,然后再将刚才保存的数据显示到文本框中,哈哈。

8CString::MakeUpper()将字符转为大写

9GetDlgItem(IDC_ReceiveEdit)->ModifyStyle(0,ES_UPPERCASE,0);

ModifyStyle

作用:Call this member function to modify a window's style.

函数原型:

BOOL ModifyStyle

(

  DWORD dwRemove,

  DWORD dwAdd,

  UINT   nFlags

);

参数含义:

dwRemove指定要删除的扩展属性,

dwAdd指定修改时要增加的扩展属性,

nFlag标志一般设置为0。

 

ES_AUTOHSCROLL,ES_AUTOVSCROLL 指明输入文字超出显示范围时自动滚动。

ES_CENTER,ES_LEFT,ES_RIGHT 指定对齐方式

ES_MULTILINE 是否允许多行输入

ES_PASSWORD 是否为密码输入框,如果指明该风格则输入的文字显示为*

ES_READONLY 是否为只读

ES_UPPERCASE,ES_LOWERCASE 显示大写/小写字符

 

10、OVERLAPPED异步I/O

typedef struct _OVERLAPPED {

    DWORD   Internal;

    DWORD   InternalHigh;

    DWORD   Offset;

    DWORD   OffsetHigh;

    HANDLE  hEvent;

} OVERLAPPED, *LPOVERLAPPED;

参数说明:

Internal: 预留给操作系统使用。它指定一个独立于系统的状态,当GetOverlappedResult函数返回时没有设置扩展错误信息ERROR_IO_PENDING时有效。

InternalHigh: 预留给操作系统使用。它指定长度的数据转移,当GetOverlappedResult函数返回TRUE时有效。

Offset: 该文件的位置是从文件起始处的字节偏移量。调用进程设置这个成员之前调用ReadFile或WriteFile函数。当读取或写入命名管道和通信设备时这个成员被忽略设为零。

OffsetHigh: 指定文件传送的字节偏移量的高位字。当读取或写入命名管道和通信设备时这个成员被忽略设为零。

hEvent: 在转移完成时处理一个事件设置为有信号状态。调用进程集这个成员在调用ReadFile、 WriteFile、TransactNamedPipe、 ConnectNamedPipe函数之前。

 

11、[转载]使用CSerialPort类编写串口通信程序的问题  

http://masust.blog.163.com/blog/static/14695408420127189452742/

2012-08-18 21:45:27|  分类: VC |  标签:cserialport  串口通信  windows  api  问题  |字号 订阅

原文地址:使用CSerialPort类编写串口通信程序的问题作者:阿杜

 

CSerialPort是一个很好的串口通讯类,但它没有关闭串口的方法,如果对这个类的实现原理不了解,自行编写串口关闭方法可能会带来如下问题:

    1、用closehandle方法关闭串口:由于调用类方法StartMonitoring后会生成一个串口通信线程,这个线程中要不停地访问串口,这种方法会带来明显的错误。

   2、先用StopMonitoring方法停止串口监听,然后用closehandle关闭串口:由于StopMonitoring只是将进程挂起,这样做将使程序结束时解构函数无法将中止事件发送到线程,可能导致程序不能完全退出,主窗口关闭后仍可在进程管理器中看到进程。

   3、先用SetEvent发送中止事件给线程,等待线程结束后再用closehandle关闭串口,程序如下:

void CSerialPort::ClosePort()
{
// if the thread is alive: Kill
if (m_bThreadAlive)
{
   do
   {
    SetEvent(m_hShutdownEvent);
   } while (m_bThreadAlive);
   TRACE("Thread endedn");
}

if(m_szWriteBuffer != NULL)
{
   delete [] m_szWriteBuffer;
   m_szWriteBuffer= NULL;
}

if(m_hComm)
{
   CloseHandle(m_hComm);
   m_hComm = NULL;
}
}
    这个程序在某些应用中可能会导致程序锁死,比如要将连续、大量接收到的数据进行实时显示或存盘时会发生这种情况,原因是:串口通信线程每接收到一个字符,都要用sendmessage通知主线程,而sendmail是阻塞式的,如果此时主线程正在关闭串口,会用do...while循环连续向串口通信线程,直到串口通信线程中止为止,这个过程也是阻塞式的,此时主线程在不断判断串口通信线程是否中止,通信线程发来的sendmessage消息进行处理,而通信线程则在等待sendmessage的返回,不会对主线程发来的中止信号进行处理,从而导致死锁,进入漫长的超时等待状态。由于消息处理及存盘、实时显示等过程比较耗时,在对连续、大量接收到的数据进行此类操作时极易导致锁死情况,导致这种情况的根本原因是sendmessage不是异步的。

     了解导致错误的原因,就可以采取针对性的措施进行避免,比如用postmessage替代sendmessage(可能会导致数据丢失),不使用do...while循环。

     将上面的ClosePort函数修改成以下形式:

void CSerialPort::ClosePort()
{
// if the thread is alive: Kill
if (m_bThreadAlive)
{
   MSG message;
   while (m_bThreadAlive)
   {
    if(::PeekMessage(&message,m_pOwner->m_hWnd,0,0,PM_REMOVE))
    {
     ::TranslateMessage(&message);
     ::DispatchMessage(&message);
    }

    SetEvent(m_hShutdownEvent);
   }
   TRACE("Thread endedn");
}

if(m_szWriteBuffer != NULL)
{
   delete [] m_szWriteBuffer;
   m_szWriteBuffer= NULL;
}

if(m_hComm)
{
   CloseHandle(m_hComm);
   m_hComm = NULL;
}
}

同时在ReceiveChar中加入对线程结束事件的判断:

void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
{
BOOL bRead = TRUE;
.......

for (;;)
{
   //add by lgb
   //防止死锁
if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)
    return;

......

}

    这样将解决死锁问题。

12、SerialPort类源代码分析 .

http://masust.blog.163.com/blog/static/146954084201271894031223/

2012-08-18 21:40:31|  分类:VC|  标签:serialport  api  串口通信  |字号大中小 订阅

转载于:http://hi.baidu.com/laoyang1018/blog/item/9949500f5e5f9a396059f3db.html

 

13VC 6.0中怎样设置才能生成Debug或Release版本的程序?

 

vc右上角菜单栏或工具栏的空白处右键,选上Build项,使build工具栏可见,在Build工具栏上就可以选择工程的Debug或Release版本
或者菜单栏Build->Batch Build,在弹出的对话框中选择编译哪个版本,或者两个版本都编译。

 

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

 

14CSerialPort类解析

http://xiaozhekobe.blog.163.com/blog/static/175646098201211075723333/

 

15MFC自动保存配置信息到注册表

 

http://freestyledqpi.blog.sohu.com/113406094.html

1、写MFC程序程时,向导会自动在注册表中建立本程序的键,用于保存程序的一些设置信息。但有时我们不希望将这些配置信息保存到注册表中,而要存在自己指定目录中的ini文件(或其它文件),我们不用自己重写一个CIniFile类,只需要要 CWinApp::InitInstance()中修改CWinApp的几个成员变量就可以了,如下:

 

//以下代码放到InitInstance
//
首先清除注册表键值变量所分配的内存空间 .
free((void*)m_pszRegistryKey);
//
再清除Ini变量
free((void*)m_pszProfileName);
//
改变Ini文件名.
m_pszProfileName=_tcsdup(d:\myPrj\iniName.ini);


进行完以上设置后我就可以直接使用 GetProfileString WriteProfileString 等函数进行操作了

2MFCCWinApp类提供了很容易的注册表访问函数~~以前从来没注意过~~还到处找读写注册表的办法~~ -_-!看下面几个成员函数~ 

SetRegistryKey

Causes application settings to be stored in the registry instead of .INI files.

SetRegistryKey 这个函数功能是设置MFC程序的注册表访问键,并把读写 ini文件的成员函数映射到读写注册表。只要调用一下 SetRegistryKey 并指定注册表键值,那么下面6个成员函数,就被映射到进行注册表读取了~

WriteProfileBinary

Writes binary data to an entry in the application's .INI file.

WriteProfileInt

Writes an integer to an entry in the application's .INI file.

WriteProfileString

Writes a string to an entry in the application's .INI file.

 

GetProfileBinary

Retrieves binary data from an entry in the application's .INI file.

GetProfileInt

Retrieves an integer from an entry in the application's .INI file.

GetProfileString

Retrieves a string from an entry in the application's .INI file.


MSDN上面写上面6个函数是写到INI文件的。所以俺就忽略了其访问注册表的功能。无意中看了其MFC实现才有所了解。

例子如下:
SetRegistryKey(_T("boli's app")); //这里是准备在注册表HKEY_CURRENT_USER\\software下面生成一个boli's app 分支~为什么说是准备呢?因为如果不调用相关函数,如上面提到的6个函数,它是不会真正读写注册表的。具体本文最最下面的MFC实现摘录。
CString strUserName,strPassword;
WriteProfileString("LogInfo","UserName",strUserName); //
向注册表HKEY_CURRENT_USER\\software\\boli's app\\LogInfo\\分支下写入 UserName字符串行键值~
WriteProfileString("LogInfo","Password",strPassword);//
同上~

strUserName = GetProfileString("LogInfo","UserName");//
这里是读取HKEY_CURRENT_USER\\software\\boli's app\\LogInfo\\分支下的 UserName字符串键值到 strUserName~
strPassword = GetProfileString("LogInfo","Password");
 

如果不是在CWinApp派生的类中读写注册表,可以直接用:
strUserName = theApp.GetProfileString("LogInfo","UserName");
strPassword = theApp.GetProfileString("LogInfo","Password");
 
strUserName = AfxGetApp()->GetProfileString("LogInfo","UserName");
条条大路通罗马。

16MFC按回车发送文本内容

方法一:

可以将发送按钮设置成默认按钮
也可以通过重载消息预处理函数截获enter键消息
发送按钮设置成默认按钮如下:
第一:在资源里找到对话框
第二:选择发送按钮
第三:将该按钮的属性中的Default Button设置为true

方法二:

重载消息预处理函数PreTranslateMessage()

BOOL CCOMTOOLDlg::PreTranslateMessage(MSG* pMsg)

{

        if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)

        OnSend();

    return CDialog::PreTranslateMessage(pMsg);

}

方法三:

void CMyDialog::OnOK() 
{
CWnd *pWnd = GetFocus();
if (pWnd)
{
switch (pWnd->GetDlgCtrlID())
{
case IDC_EDIT1:
case IDC_EDIT2:
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON1, BN_CLICKED), (LPARAM)GetDlgItem(IDC_BUTTON1)->m_hWnd);
return;
case IDC_EDIT3:
case IDC_EDIT4:
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON2, BN_CLICKED), (LPARAM)GetDlgItem(IDC_BUTTON2)->m_hWnd);
return;
}
}
CDialog::OnOK();
}

17、识别\n等特殊字符  和 汉字的识别代码

if(ch == '\n')

        {

            m_strReceive+=0x0D;

            m_strReceive+=0x0a;

        }

        ///汉字处理

        ///无法校验汉字前后是否匹配

        else

        {

            //ch的ASCII 汉字的ASCII是大于127的,因为最高位为1

            if(ch > 127 && ChD[0]== -1)///遇到的汉字的前半部分

            {

                ChD[0] = ch;//全局变量int ChD[2]={-1,-1};

            }

            else if(ch > 127 && ChD[1]== -1)///遇到的汉字的后半部分

            {  

                ChD[1] = ch;///

                m_strReceive+=ChD;///汉字赋给文本框

                ChD[0]=ChD[1]=-1;///汉字数组初始化

            }

            else

                m_strReceive+=ch;

        }

    }

    UpdateData(false);

18、编辑框显示文本过多闪烁问题

http://bbs.csdn.net/topics/300113125

每次要向编辑框添加内容时

我使用UpdateData(FALSE)或是SetWindowText来更新内容的时候

然后再使用 Setsel或LineScroll让总是显示最后一行

但是这样感觉很是不爽,每次刷新界面的时候,滚动条都是先跑上去,再跑下来

后来我在网上查了一些资料,有人说先在SetWindowTex之前SetRedraw(FALSE);

然后再在 Setsel或LineScroll之后SetRedraw(TRUE);

看起来好像很有道理,但是我试了很多次还是跟之前一样的效果,实在是郁闷!

目前无解,期待高手解答!谢谢!(代码如下)

      m_ctrlMsgList.SetRedraw(FALSE);

     m_ctrlMsgList.SetWindowText(m_strMessageList);

     m_ctrlMsgList.LineScroll(m_ctrlMsgList.GetLineCount());

     m_ctrlMsgList.SetRedraw(TRUE);

 

用SetSel、ReplaceSel插入;UpdateData每次都要全部更新,效率低且必然全部刷新,只适用于比较小的数据量;SetWindowText也是全部更新,UpdateData就用这个。

 

///以下是将接收的字符加在字符串的最后,这里费时很多

    ///不过采用末尾追加的方式,相对于UpdateData来说,闪烁不是很明显。因为UpdateData是全部更新使用会比较明显。

    //int nLen=m_ctrlReceive.GetWindowTextLength();

    m_ctrlReceive.SetSel(-1,-1);

    m_ctrlReceive.ReplaceSel(m_strReceive);

 

如需转载请标明出处:http://blog.csdn.net/itas109

0 0
原创粉丝点击