Category Archives: Windows

Windows向WinCE移植串口通信程序出现的问题

我所编写的一个串口通讯程序需要从Windows平台移植到WinCE平台,在移植的过程中遇到了几个问题,现记录下来:

1、显示串口打开失败

原因:WinCE中的串口名称为”COM#:“,而Windows中的没有冒号。

2、WaitCommEvent()事件不被触发,错误代码87:错误的参数 Invalid Parameter

原因:每一次事件发生后,都需要重新设置事件掩码SetCommMask(RX_CHAR)。

3、WinCE中无法使用异步串口模式。

解决方法:使用同步模式。

WinCE开发遇到的两个问题

1. eVC切换SDK时死机

今天在安装公司的WinCE SDK后,无法在eVC中切换SDK,一切换就死机,之前使用mini2440 SDK时也遇到相同的问题。

解决方法:下载安装sp4补丁,可能是eVC版本与SDK兼容性问题。

2. PB5运行时弹出错误

我的PB5.0每次安装后都会弹出pbce.exe错误,并且无法正常运行。期间下载了多个不同的PB5安装程序,都会发生同样的错误。

解决方法:下载安装了“WinCEPB50-071231-Product-Update-Rollup-Armv4I”补丁,问题得到解决。现在终于可以自己定制WinCE系统了。

VC | VC配合批处理完成文件操作

VisualC++ Application Note
By Automatic.dai @ YunFei Studio
N003  VC配合批处理完成文件操作

Background()

{

今天遇到一个问题,需要从模板库里拷贝一个模板文件,并根据当天的日期生成目标文件。本人不才,除了把模板文件打开、载入缓冲,再复制到目标文件外,没有想到其他更简单的方法。而用DOS命令行完成此工作却十分简单,一个copy就行了。

}

Solution()

{

使用VC+批处理混合编程。

}

Main:

批处理相当于Linux中的bash文件,由一系列命令行构成,可以很方便的进行文件复制、拷贝。通过在VC中调用批处理文件,可以大大简化文件复制、移动的难度。

批处理中比较重要的特殊符号有:

%    变量引导符

@    取消回显

echo  显示内容

>     输出重定向(覆盖原文件)

>>    输出重定向(追加原文件)

If     条件判断

goto  程序跳转

:     跳转标号引导符,如 :label

call   调用其他批处理文件

pause 暂停,出现“按任意键继续”

在VC中调用bat的语句为:

UINT WINAPI WinExec(

__in          LPCSTR lpCmdLine,

__in          UINT uCmdShow

);

其中lpCmdLine为文件名加参数,nCmdShow为窗口参数,此处为SW_SHOW即显示窗口。

这里,lpCmdLine = “.\\bin\\CreateTodayLog.cmd 2011-11-12”

其中”2011-11-12”为传递的参数,此参数根据当天日期添加。在批处理文件中读取这个参数,从而让文件名中包含该日期。

批处理文件如下:

CreateTodayLog.cmd

1   @echo off

2   copy “.\Reports\Daily\Template.xls”,”.\Reports\Daily\生产日报表%1.xls”

3   start .\Reports\Daily\生产日报表%1%.xls

第一步关闭了回显,第二步将模板Template.xls拷贝,并重命名为生产日报表+日期,%1表示传递的第一个参数。最后一步打开了这个新建的文件。

VC | 重载窗口消息函数屏蔽按键操作

VisualC++ Application Note
By Automatic.dai @ YunFei Studio
N002  重载窗口消息函数屏蔽按键操作 

Background()

{

在编写诸如输入密码才可以继续操作的界面时,即使屏蔽了窗体的关闭按钮,用户仍可以通过按ESC跳过。这时,即使重载OnClose()也依然无效,这就造成了程序安全上的隐患。

}

Solution()

{

    重载消息处理函数,将ESC按键进行屏蔽。

}

 

Main:

假设你的窗体名是Cxxxx,在Cxxxx.cpp中加入以下代码:

BOOL   Cxxxx::PreTranslateMessage(MSG*   pMsg)

{ 

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

     {

         return  TRUE;

     }   

     else

     {

         return   CDialog::PreTranslateMessage(pMsg); 

     }

}

该函数发生在TranslateMessage和DispatchMessage之前,通过重载该函数可以对窗口消息进行过滤。在if中我们判断了消息的类型是WM_KEYDOWN,即有键按下,同时通过&&判断消息的参数是否为VK_ESCAPE,即按下的键是ESC。当然还需要调用基类的CDialog::PreTranslateMessage(pMsg),因为加速键(Accelerator-key)依赖原函数实现。

最后不要忘了在Cxxxx.h中增加该函数的声明:

BOOL   PreTranslateMessage(MSG*   pMsg);

灵活应用这种方法可以实现其他的功能,比如屏蔽所有的字母等:

if( pMsg->message==WM_KEYDOWN && ( pMsg->wParam >= 'A' && pMsg->wParam <= 'Z') )

*注意WM_KEYDOWN不区分大小写,所有的字母都是以大写对应的ASC码表示的,所以以上操作是屏蔽所有字母,而不是只屏蔽大写字母。只有在该消息Translate为WM_CHAR之后,字母才能区分大小写。

摘抄一段关于WM_KEYDOWN和WM_CHAR的区别的短文:

Read more »

VC | MFC的线程

VisualC++ Application Note
By Automatic.dai @ YunFei Studio
N001 MFC的线程
Background()
{
在编写用户界面时,需要长时间阻塞等待,这会造成界面的假死,此时不能响应任何用户消息。
}

Solution()
{
使用多线程编程,增加一个工作者线程。
}

Main:
在Win32 API中并不区分线程的种类,只需知道线程的地址即可执行线程,而MFC区分工作者线程(Worker Thread)和用户界面线程(User Interface Thread)。这两种线程的区别在于工作者线程不能接收消息,而用户界面可以处理消息。所以,用户界面线程一般用于需要与用户交互,实现对用户产生的事件做出应答的情况;而工作者线程一般用于耗时计算。在MFC中创建线程,需要使用AfxBeginThread函数,根据线程的不同创建方式不同,AfxBeginThread的参数也不同,具体如下:
一、创建工作者线程
此方式比较简单,只需提供线程函数,即可执行。可以指定CREATE_SUSPENDED参数使得线程创建时挂起,并在合适时通过ResumeThread恢复。

CWinThread* AfxBeginThread(
		   AFX_THREADPROC pfnThreadProc,                   // 线程函数地址
		   LPVOID pParam,                                  // 传递的参数
		   int nPriority = THREAD_PRIORITY_NORMAL,         // 优先级
		   UINT nStackSize = 0,                            // 堆栈大小
		   DWORD dwCreateFlags = 0,                        // 挂起参数
		   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL    // 安全属性
		   );

 

二、创建用户界面线程
1)由CWinThread派生出子类,该类需要有动态创建能力,即需使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏;
2)覆盖CWinThread的一些虚函数;
3)创建线程,同样可以指定CREATE_SUSPENDED。

CWinThread* AfxBeginThread(
		   CRuntimeClass* pThreadClass,                    // 派生类名称
		   int nPriority = THREAD_PRIORITY_NORMAL,         // 优先级
		   UINT nStackSize = 0,                            // 堆栈大小
		   DWORD dwCreateFlags = 0,                        // 挂起参数
		   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL    // 安全属性
		   );

 

三、删除线程
删除线程使用AfxEndThread函数,它调用CWinThread的虚函数Delete()。它将清理本线程创建的MFC对象,释放线程分配的内存空间,但不关闭线程句柄。
CWinThread::Delete()的实现过程是:若m_bDelete为TRUE,将销毁MFC线程对象本身,即调用线程的析构函数,析构函数检测线程句柄是否为空,并使用CloseHandle()来关闭它。有时在线程结束后保留MFC线程对象是有用的,注意此处MFC对象与线程对象的区别。

四、线程的消息循环
一般情况下,线程使用缺省的消息循环:CWinThread::Run,但是有些情况下,需要自己实现消息循环,如在用户界面线程中进行耗时计算,如果此时不手动进行消息循环,会造成假死的状况,实现形式如下:

while ( bDoingBackgroundProcessing )	// 开始耗时计算
{
	MSG msg;
	while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE) )	// 如果有消息请求
	{
		if ( !PumpMessage() )
		{
			bDoingBackgroundProcessing = FALSE;				// 停止计算
			::PostQuitMessage();							// 发送消息
			break;
		}
	}
	LONG lIdle = 0;
	while ( AfxGetApp()->OnIdle(lIdle++) );
	// 进行后台线程处理
	// 。。。。。。
}