iTimothy

君看一叶舟,出没风波里


  • 首页

  • 分类

  • 归档

  • 项目

  • 关于

SQL数据的基本操作(时间和日期)

发表于 2005-08-08 | 分类于 技术控 | | 阅读次数:
字数统计: 2.6k 字 | 阅读时长 ≈ 9 分钟

日期和时间函数对建立一个站点是非常有用的。站点的主人往往对一个表中的数据何时被

更新感兴趣。通过日期和时间函数,你可以在毫秒级跟踪一个表的改变。

返回当前日期和时间

通过函数GETDATE(),你可以获得当前的日期和时间。例如,语句SELECT GETDATE()

返回如下的结果:

……………………………

NOV 30 1997 3:29AM

(1 row(s) affected)

显然,如果你将来使用这个函数,你得到的日期将比这个时间晚,或者更早。

函数GETDATE()可以用来作为DATEDIME()型字段的缺省值。这对插入记录时保存

当时的时间是有用的。例如,假设有一个表用来保存你站点上的活动日志。每当有一个访

问者访问到你的站点时,就在表中添加一条新记录,记下访问者的名字,活动,和进行访

问的时间。要建立一个表,其中的记录包含有当前的日期和时间,可以添加一个DATETIME

型字段,指定其缺省值为函数GETDATE()的返回值,就象这样:

CREATE TABLE site_log (

username VARCHAR(40),

useractivity VARCHAR(100),

entrydate DATETIME DEFAULT GETDATE())

转换日期和时间

你也许已经注意到,函数GETDATE()的返回值在显示时只显示到秒。实际上,SQL Sever

内部时间可以精确到毫秒级(确切地说,可以精确到3.33毫秒)。

要得到不同格式的日期和时间,你需要使用函数CONVERT()。例如,当下面的这个语句

执行时,显示的时间将包括毫秒:

SELECT CONVERT(VARCHAR(30),GETDATE(),9)

注意例子中数字9的使用。这个数字指明了在显示日期和时间时使用哪种日期和时间格式。

当这个语句执行时,将显示如下的日期和时间:

…………………………………..

Nov 30 1997 3:29:55:170AM

(1 row(s) affected)

在函数CONVERT()中你可以使用许多种不同风格的日期和时间格式。下表显示了所

有的格式。

表的日期和时间类型

类型值 标准输出

0 Default mon dd yyyy hh:miAM

1 USA mm/dd/yy

2 ANSI yy.mm.dd

3 British/French dd/mm/yy

4 German dd.mm.yy

5 Italian dd-mm-yy

6 - dd mon yy

7 - mon dd,yy

8 - hh:mi:ss

Default + milliseconds–mon dd yyyy

hh:mi:ss:mmmAM(or )

10 USA mm-dd-yy

11 JAPAN yy/mm/dd

12 ISO yymmdd

13 Europe Default + milliseconds–dd mon yyyy

hh:mi:ss:mmm(24h)

14 - hh:mi:ss:mmm(24h)

类型0,9,和13总是返回四位的年。对其它类型,要显示世纪,把style值加上100。类

型13和14返回24小时时钟的时间。类型0,7,和13返回的月份用三位字符表示(用Nov

代表November).

对上表中所列的每一种格式,你可以把类型值加上100来显示有世纪的年(例如,00年

将显示为2000年)。例如,要按日本标准显示日期,包括世纪,你应使用如下的语句:

SELECT CONVERT(VARCHAR(30),GETDATE(),111)

在这个例子中,函数CONVERT()把日期格式进行转换,显示为1997/11/30

抽取日期和时间

在许多情况下,你也许只想得到日期和时间的一部分,而不是完整的日期和时间。例如,

假设你想列出你的站点目录中每个站点被查询的月份。这时你不希望完整的日期和时间把

网页弄乱。为了抽取日期的特定部分,你可以使用函数DATEPART(),象这样:

SELECT site_name ‘Site Name’,

DATEPART(mm,site_entrydate) ‘Month Posted’ FROM site_directory

函数DATEPART()的参数是两个变量。第一个变量指定要抽取日期的哪一部分;第二个

变量是实际的数据。在这个例子中,函数DATEPART()抽取月份,因为mm代表月份。

下面是这个SELECT 语句的输出结果:

Site Name Month Posted

………………………………………………………………

Yahoo 2

Microsoft 5

Magicw3 5

(3 row(s) affected)

Month Posted列显示了每个站点被查询的月份。函数DATEPART()的返回值是一个整数。

你可以用这个函数抽取日期的各个不同部分,如下表所示。

表的日期的各部分及其简写

日期部分 简写值

year yy 1753–9999

quarter qq 1–4

month mm 1–12

day of year dy 1–366

day dd 1–31

week wk 1–53

weekday dw 1–7(Sunday–Saturday)

hour hh 0–23

minute mi 0–59

second ss 0–59

milisecond ms 0–999

当你需要进行日期和时间的比较时,使用函数DATEPART()返回整数是有用的。但是,

上例中的查询结果(2,5)不是十分易读。要以更易读的格式得到部分的日期和时间你可

以使用函数DATENAME(),如下例所示:

SELECT site_name ‘Site Name’

DATENAME(mm,site_entrydate) ‘Month Posted’

FROM site_directory

函数DATENAME()和函数DATEPART()接收同样的参数。但是,它的返回值是一个

字符串,而不是一个整数。下面是上例该用DATENAME()得到的结果:

Site Name Month Postec

………………………………………………………………….

Yahoo February

Microsoft June

Magicw3 June

(3 row(s) affected)

你也可以用函数DATENAE()来抽取一个星期中的某一天。下面的这个例子同时抽取一

周中的某一天和日期中的月份:

SELECT site_name ‘Site Name’,

DATENAME(dw,site_entrydate)+ ‘-’ + DATENAME(mm,site_entrydate)

‘Day and Month Posted’ FORM site_directory

这个例子执行时,将返回如下的结果:

Site Name Day and Month Posted

………………………………………………………………………

Yahoo Friday - February

Microsoft Tuesday - June

Magicw3 Monday - June

(3 row(s) affected)

返回日期和时间范围

当你分析表中的数据时,你也许希望取出某个特定时间的数据。你也许对特定的某一天

中――比如说2000年12月25日――访问者在你站点上的活动感兴趣。要取出这种类型的

数据,你也许会试图使用这样的SELECT语句:

SELECT * FROM weblog WHERE entrydate=“12/25/20000”

不要这样做。这个SELECT语句不会返回正确的记录――它将只返回日期和时间是

12/25/2000 12:00:00:000AM的记录。换句话说,只有刚好在午夜零点输入的记录才被返回。

注意:

在本文中,假设字段entrydate是DATETIME型,而不是SMALLDATETIME型。

本节的讨论对SMALLDATETIME型字段也是适用的,不过SMALLDATETIME型字段只

能精确到秒。

问题是SQL Sever将用完整的日期和时间代替部分日期和时间。例如,当你输入一个日期,

但不输入时间时,SQL Sever将加上缺省的时间“12:00:00:000AM”。当你输入一个时

间,但不输入日期时,SQL Sever将加上缺省的日期“Jan 1 1900”。

要返回正确的记录,你需要适用日期和时间范围。有不止一种途径可以做到这一点。例如,

下面的这个SELECT 语句将能返回正确的记录:

SELECT * FROM weblog

WHERE entrydate>=“12/25/2000” AND entrydate<“12/26/2000”

这个语句可以完成任务,因为它选取的是表中的日期和时间大于等于12/25/2000

12:00:00:000AM并小于12/26/2000 12:00:00:000AM的记录。换句话说,它将正确地返回2000

年圣诞节这一天输入的每一条记录。

另一种方法是,你可以使用LIKE来返回正确的记录。通过在日期表达式中包含通配符“%”,

你可以匹配一个特定日期的所有时间。这里有一个例子:

SELECT * FROM weblog WHERE entrydate LIKE ‘Dec 25 2000%’

这个语句可以匹配正确的记录。因为通配符“%”代表了任何时间。

使用这两种匹配日期和时间范围的函数,你可以选择某个月,某一天,某一年,某个小时,

某一分钟,某一秒,甚至某一毫秒内输入的记录。但是,如果你使用LIKE 来匹配秒或毫

秒,你首先需要使用函数CONVERT()把日期和时间转换为更精确的格式(参见前面 “转

换日期和时间”一节)。

比较日期和时间

最后,还有两个日期和时间函数对根据日期和时间取出记录是有用的。使用函数DATEADD

()和DATEDIFF(),你可以比较日期的早晚。例如,下面的SELECT语句将显示表中

的每一条记录已经输入了多少个小时:

SELECT entrydate ‘Time Entered’

DATEDIFF(hh,entrydate,GETDATE()) ‘Hours Ago’ FROM weblog

如果当前时间是2000年11月30号下午6点15分,则会返回如下的结果:

Time Entered Hours Ago

…………………………………………………..

Dec 30 2000 4:09PM 2

Dec 30 2000 4:13PM 2

Dec 1 2000 4:09PM 698

(3 row(s) affected)

函数DADEDIFF()的参数是三个变量。第个变量指定日期的某一部分。在这个例子中,

是按小时对日期进行比较,(要了解日期各部分的详细内容,请参考表11.2)在日期2000

年11月1日和2000年11月30日的指定时间之间有689个小时。另外两个参数是要进行

比较的时间。为了返回一个正数,较早的时间应该先给。

函数DATEADD()把两个日期相加。当你需要计算截止日期这一类的数据时,这个函数

是有用处的。例如,假设访问者必须先注册才能使用你的站点。注册以后,他们可以免费

使用你的站点一个月。要确定什么时候他们的免费时间会用完,你可以使用如下的SELECT

语句:

SELECT username ‘ser Name’,

DATEADD(mm,1,firstvisit_date) ‘Registration Expires’

FROM registration_table

函数DATEADD()的参数有三个变量。第一个变量代表日期的某一部分(参见表11.2),

这个例子用到了代表月份的mm。第二个变量指定了时间的间隔――在本例中是一个月。

最后一个变量是一个日期,在这个例子中,日期是取自DATETIME型字段firstvisit_dat e.

假设当前日期是June 30,2000,这个语句将返回如下的内容:

User Name Registration Expires

…………………………………………………………………………

Bill Gates Jul 30 2000 4:09PM

President Clinton Jul 30 2000 4:13PM

William Shakespeare Jul 1 2000 4:09PM

(3 row(s) affected)

注意:

与你预料的相反,使用函数DATEADD()把一个日期加上一个月,它并不加上30天。这

个函数只简单地把月份值加1。这意味着在11月注册的人将比在2月注册的人多得到2天

或3天的时间。要避免这个问题,你可以用函数DATEADD()直接增加天数,而不是月

份。

CListCtrl应用

发表于 2005-08-07 | 分类于 技术控 | | 阅读次数:
字数统计: 0 字 | 阅读时长 ≈ 1 分钟

判断客户端是否断开

发表于 2005-07-25 | 分类于 技术控 | | 阅读次数:
字数统计: 86 字 | 阅读时长 ≈ 1 分钟

BOOL CMySocket::IsDropped() { BOOL bDropped = FALSE; INT iRet = 0; BOOL bOK = TRUE;

struct timeval timeout = { 0, 0 };//立刻返回,不阻塞 fd_set readSocketSet;

FD_ZERO( &readSocketSet; ); FD_SET( m_hSocket, &readSocketSet; );

iRet = ::select( 0, &readSocketSet;, NULL, NULL, &timeout; ); bOK = ( iRet > 0 );

if( bOK ) { bOK = FD_ISSET( m_hSocket, &readSocketSet; ); }

if( bOK ) { CHAR szBuffer[1] = “”; iRet = ::recv( m_hSocket, szBuffer, 1, MSG_PEEK ); bOK = ( iRet > 0 ); if( !bOK ) { INT iError = ::WSAGetLastError(); bDropped = ( ( iError == WSAENETRESET ) || ( iError == WSAECONNABORTED ) || ( iError == WSAECONNRESET ) || ( iError == WSAEINVAL ) || ( iRet == 0 ) ); //Graceful disconnect from other side. } }

return( bDropped );

}

MyDetective V1.0

发表于 2005-06-21 | 分类于 我的拙作 | | 阅读次数:
字数统计: 420 字 | 阅读时长 ≈ 1 分钟

一日写程序,倍感无聊,想知道到底我按了多少次键,想看看键盘和鼠标被我“折磨”了多少次。嘿嘿。 为了满足自己的好奇心,所以有了一个写一下小程序的想法。 MyDetective是一个侦测你的按键次数的一个有趣的小工具,包括鼠标左右按键和键盘上的每一个键。 想想在你写文章的时候或者写程序的时候,还可以统计出你按了多少次键,是不是很有趣呢?

其实主要的原理,就是同时运用鼠标钩子和键盘钩子,而且是全局钩子,这样才能保证你的任何按键动作都能被这个程序截获。

下面我们开始体验一下:

启动程序,如下图所示:

点击开始后,程序就开始工作了,如果你觉得他碍眼,点击最小化按钮,程序会自动缩小到托盘那里,并以气泡提示的方法, 提示你按键的信息,点击托盘图标,会出现气泡提示,告诉你当前统计的按键信息,双击托盘图标,就会还原为对话框的形式。

已经测试,能在Win2000和WinXP下运行,win98没试过。大家可以帮我试试。 附上DLL钩子程序和应用程序的源代码,供网友参考。

来看看我打这篇文章的时候,共按了多少次键,看下图,呵呵,居然6000多次了,真是“不知不觉”……怎么样?有意思吧 : )

不知不觉,都晚上1点了,呵呵,我睡了 ZZZZZzzzzz……

点击下载此文件

设置网卡可以接收所有IP的数据(混杂模式)

发表于 2005-06-16 | 分类于 技术控 | | 阅读次数:
字数统计: 246 字 | 阅读时长 ≈ 1 分钟

// Function name : SetAdapterReceiveAll // Description : 设置网卡可以接收所有IP的数据(混杂模式) // Return type : void bool SetAdapterReceiveAll() { //检查Winsock版本 WORD ver=MAKEWORD(2,2); WSADATA wsaData; int err=WSAStartup(ver,&wsaData;); if(err!=0) { _RPT2( _CRT_ERROR,”启动WinSocket version %d.%d 错误!”,2,2); return false; } //创建原始套接字 s=socket(AF_INET,SOCK_RAW,IPPROTO_RAW); //设置为对IP头亲自操 BOOL bFlag=TRUE; err=setsockopt(s,IPPROTO_IP,IP_HDRINCL,(char )&bFlag;,sizeof(bFlag)); if(err!=0) return false; //获得本机地址 char hostname[256]; memset(hostname,0,256); err=gethostname(hostname,256); if(err!=0) return false; struct hostent phost=gethostbyname(hostname); if(!phost) return false; //把原始套接字绑定到本定网卡 struct sockaddr_in addr_in; memset(&addr;_in,0,sizeof(addr_in)); addr_in.sin_addr=(in_addr )phost->h_addr_list[0]; addr_in.sin_family = AF_INET; addr_in.sin_port = htons(57274);//端口号,可以是任意值,但最好不要和公共端口冲突 bind(s, (PSOCKADDR)&addr;_in, sizeof(addr_in)); // dwValue为输入输出参数,为1时执行,0时取消 DWORD dwValue = 1; // 设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包。其中SIO_RCVALL // 的定义为: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) ioctlsocket(s, SIO_RCVALL, &dwValue;); return true; }

读取串口数据

发表于 2005-05-14 | 分类于 技术控 | | 阅读次数:
字数统计: 2.4k 字 | 阅读时长 ≈ 8 分钟

方法一:使用VC++提供的串行通信控件MSComm 首先,在对话框中创建通信控件,若Control工具栏中缺少该控件,可通过菜单Project –> Add to Project –> Components and Control插入即可,再将该控件从工具箱中拉到对话框中。此时,你只需要关心控件提供的对 Windows 通讯驱动程序的 API 函数的接口。换句话说,只需要设置和监视MSComm控件的属性和事件。

  在ClassWizard中为新创建的通信控件定义成员对象(CMSComm m_Serial),通过该对象便可以对串口属性进行设置,MSComm 控件共有27个属性,这里只介绍其中几个常用属性:

  CommPort 设置并返回通讯端口号,缺省为COM1。

  Settings 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。

  PortOpen 设置并返回通讯端口的状态,也可以打开和关闭端口。

  Input 从接收缓冲区返回和删除字符。

  Output 向发送缓冲区写一个字符串。

  InputLen 设置每次Input读入的字符个数,缺省值为0,表明读取接收缓冲 区中的全部内容。

  InBufferCount 返回接收缓冲区中已接收到的字符数,将其置0可以清除接收缓 冲区。

  InputMode 定义Input属性获取数据的方式(为0:文本方式;为1:二进制方式)。

  RThreshold 和 SThreshold 属性,表示在 OnComm 事件发生之前,接收缓冲区或发送缓冲区中可以接收的字符数。

  以下是通过设置控件属性对串口进行初始化的实例:

BOOL CSampleDlg:: PortOpen() { BOOL m_Opened; …… m_Serial.SetCommPort(2); // 指定串口号 m_Serial.SetSettings(“4800,N,8,1”); // 通信参数设置 m_Serial.SetInBufferSize(1024); // 指定接收缓冲区大小 m_Serial.SetInBufferCount(0); // 清空接收缓冲区 m_Serial.InputMode(1); // 设置数据获取方式 m_Serial.SetInputLen(0); // 设置读取方式 m_Opened=m_Serail.SetPortOpen(1); // 打开指定的串口 return m_Opened; }

  打开所需串口后,需要考虑串口通信的时机。在接收或发送数据过程中,可能需要监视并响应一些事件和错误,所以事件驱动是处理串行端口交互作用的一种非常有效的方法。使用 OnComm 事件和 CommEvent 属性捕捉并检查通讯事件和错误的值。发生通讯事件或错误时,将触发 OnComm 事件,CommEvent 属性的值将被改变,应用程序检查 CommEvent 属性值并作出相应的反应。在程序中用ClassWizard为CMSComm控件添加OnComm消息处理函数:

void CSampleDlg::OnComm() { …… switch(m_Serial.GetCommEvent()) { case 2: // 串行口数据接收,处理; } }

方法二:在单线程中实现自定义的串口通信类

  控件简单易用,但由于必须拿到对话框中使用,在一些需要在线程中实现通信的应用场合,控件的使用显得捉襟见肘。此时,若能够按不同需要定制灵活的串口通信类将弥补控件的不足,以下将介绍如何在单线程中建立自定义的通信类。

  该通信类CSimpleComm需手动加入头文件与源文件,其基类为CObject,大致建立步骤如下:

  (1) 打开串口,获取串口资源句柄

  通信程序从CreateFile处指定串口设备及相关的操作属性。再返回一个句柄,该句柄将被用于后续的通信操作,并贯穿整个通信过程。CreateFile()函数中有几个值得注意的参数设置:串口共享方式应设为0,串口为不可共享设备;创建方式必须为OPEN_EXISTING,即打开已有的串口。对于dwFlagAndAttribute参数,对串口有意义的值是FILE_FLAG_OVERLAPPED,该标志表明串口采用异步通信模式,可进行重叠操作;若值为NULL,则为同步通信方式,在同步方式下,应用程序将始终控制程序流,直到程序结束,若遭遇通信故障等因素,将导致应用程序的永久等待,所以一般多采用异步通信。

  (2)串口设置

  串口打开后,其属性被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&dcb;)读取当前串口设备控制块DCB(Device Control Block)设置,修改后通过SetCommState(hComm,&dcb;)将其写入。再需注意异步读写的超时控制设置, 通过COMMTIMEOUTS结构设置超时,调用SetCommTimeouts(hComm,&timeouts;)将结果写入。以下是温度监控程序中串口初始化成员函数:

BOOL CSimpleComm::Open( ) { DCB dcb;

m_hIDComDev=CreateFile( “COM2”, GENERIC_READ | GENERIC_WRITE, 0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ NORMAL|FILE_FLAG_OVE RLAPPED, NULL ); // 打开串口,异步操作 if( m_hIDComDev == NULL ) return( FALSE );

dcb.DCBlength = sizeof( DCB ); GetCommState( m_hIDComDev, &dcb; ); // 获得端口默认设置 dcb.BaudRate=CBR_4800; dcb.ByteSize=8; dcb.Parity= NOPARITY; dcb.StopBits=(BYTE) ONESTOPBIT; …… }

  (3)串口读写操作

  主要运用ReadFile()与WriteFile()API函数,若为异步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject()来等待结束信号并设置最长等待时间,举例如下:

BOOL bReadStatus; bReadStatus = ReadFile( m_hIDComDev, buffer, dwBytesRead, &dwBytesRead;, &m;_OverlappedRead ); if(!bReadStatus) { if(GetLastError()==ERROR_IO_PENDING) { WaitForSingleObject(m_OverlappedRead.hEvent,1000); return ((int)dwBytesRead); } return(0); } return ((int)dwBytesRead);

  定义全局变量m_Serial作为新建通信类CSimpleComm的对象,通过调用类的成员函数即可实现所需串行通信功能。与方法一相比,方法二赋予串行通信程序设计较大的灵活性,端口的读写可选择较简单的查询式,或通过设置与外设数据发送时间间隔TimeCycle相同的定时器:SetTimer(1,TimeCycle,NULL),进行定时读取或发送。

CSampleView:: OnTimer(UINT nIDEvent) { char InputData[30]; m_Serial.ReadData(InputData,30); // 数据处理 }

  若对端口数据的响应时间要求较严格,可采用事件驱动I/O读写,Windows定义了9种串口通信事件,较常用的有:

  EV_RXCHAR: 接收到一个字节,并放入输入缓冲区。

  EV_TXEMPTY: 输出缓冲区中的最后一个字符发送出去。

  EV_RXFLAG: 接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区。

  在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事件的发生。 SetCommMask(hComm,0)可使WaitCommEvent()中止。

方法三 多线程下实现串行通信

  方法一,二适用于单线程通信。在很多工业控制系统中,常通过扩展串口连接多个外设,各外设发送数据的重复频率不同,要求后台实时无差错捕捉,采集,处理,记录各端口数据,这就需要在自定义的串行通信类中创建端口监视线程,以便在指定的事件发生时向相关的窗口发送通知消息。

  线程的基本概念可详见VC++参考书目,Windows内部的抢先调度程序在活动的线程之间分配CPU时间,Win 32 区分两种不同类型的线程,一种是用户界面线程UI(User Interface Thread),它包含消息循环或消息泵,用于处理接收到的消息;另一种是工作线程(Work Thread),它没有消息循环,用于执行后台任务。用于监视串口事件的线程即为工作线程。

  多线程通信类的编写在端口的配置,连接部分与单线程通信类相同,在端口配置完毕后,最重要的是根据实际情况,建立多线程之间的同步对象,如信号灯,临界区,事件等,相关细节可参考VC++ 中的同步类。

  一切就绪后即可启动工作线程:

CWinThrea *CommThread = AfxBegin Thread(CommWatchThread, // 线程函数名 (LPVOID) m_pTTYInfo, // 传递的参数 THREAD_PRIORITY_ABOVE_NORMAL, // 设置线程优先级 (UINT) 0, // 最大堆栈大小 (DWORD) CREATE_SUSPENDED , // 创建标志 (LPSECURITY_ATTRIBUTES) NULL); // 安全性标志

  同时,在串口事件监视线程中:

if(WaitCommEvent(pTTYInfo->idComDev,&dwEvtMask;,NULL)) { if((dwEvtMask & pTTYInfo->dwEvtMask )== pTTYInfo->dwEvtMask) { WaitForSingleObject(pTTYInfo->hPostEvent,0xFFFFFFFF); ResetEvent(pTTYInfo->hPostEvent); // 置同步事件对象为非信号态 ::PostMessage(CSampleView,ID_COM1_DATA,0,0); // 发送通知消息 } }

  用PostMessage()向指定窗口的消息队列发送通知消息,相应地,需要在该窗口建立消息与成员函数间的映射,用ON_MESSAGE将消息与成员函数名关联。

BEGIN_MESSAGE_MAP(CSampleView, CView) //{AFX_MSG_MAP(CSampleView) ON_MESSAGE(ID_COM1_DATA, OnProcessCom1Data) ON_MESSAGE(ID_COM2_DATA, OnProcessCom2Data) ….. //}AFX_MSG_MAP END_MESSAGE_MAP()

  然后在各成员函数中完成对各串口数据的接收处理,但必须保证在下一次监测到有数据到来之前,能够完成所有的中间处理工作。否则将造成数据的捕捉错误。

  多线程的实现可以使得各端口独立,准确地实现串行通信,使串口通信具有更广泛的灵活性与严格性,且充分利用了CPU时间。但在具体的实时监控系统中如何协调多个线程,线程之间以何种方式实现同步也是在多线程串行通信程序实现的难点。

一个读串口的函数:

HANDLE m_hIDComDev;

int ReceiveComm(char* RecCommData) { DWORD dRead,dReadNum;

COMSTAT ComStat;

LPDWORD ComError;

char *Data;

ClearCommBreak(m_hIDComDev);

ClearCommError(m_hIDComDev,ComError,&ComStat;);

dRead=ReadFile(m_hIDComDev, Data, ComStat.cbInQue, &dReadNum;, NULL); //接收200个字符 //dReadNum为实际接收字节数 PurgeComm(m_hIDComDev,PURGE_RXCLEAR);//清空接收缓冲区

for(int i = 0 ;i < ComStat.cbInQue; i++) { RecCommData = Data;

RecCommData++;

Data++; }

*RecCommData = ‘\0’;

if(dRead) return 1; else return 0;

}

把字体直接装入程序中作为资源载入程序

发表于 2005-05-03 | 分类于 技术控 | | 阅读次数:
字数统计: 163 字 | 阅读时长 ≈ 1 分钟

下面是一段把资源导出为文件的代码.先把要打包的文件添加到VC项目中,类型自定(比如”RES_DATA”),ID自定(比如ID_RES_DATA),那么如果要把这个资源再回复为文件就这么调用: Res2File( MAKEINTRESOURCE(ID_RES_DATA), “RES_DATA”, “C:\ResData.dat” )

BOOL Res2File( LPCTSTR lpName, LPCTSTR lpType, LPCTSTR filename ) { HRSRC hRes = ::FindResource( NULL, lpName, lpType ); HGLOBAL gl =::LoadResource( NULL, hRes ); LPVOID lp = ::LockResource( gl ); HANDLE fp = ::CreateFile( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); if( fp == INVALID_HANDLE_VALUE ) return FALSE;

DWORD a; if( !::WriteFile( fp, lp, SizeofResource( NULL, hRes ), &a;, NULL ) ) return FALSE;

CloseHandle( fp ); FreeResource( gl ); return TRUE; } 你可以在运行的时候恢复字体文件,并安装它(只要拷贝到系统目录下的Font文件夹)。

获取硬盘序列号代码

发表于 2005-04-29 | 分类于 技术控 | | 阅读次数:
字数统计: 264 字 | 阅读时长 ≈ 1 分钟

void CDrvSrlNmbrDlg::OnGetSrlNum() { //更新下拉框的驱动器号 UpdateData(TRUE);

//获得下拉框的指针对象
CComboBox* Driver=(CComboBox*)GetDlgItem(IDC_DRIVER);

//获得当前选择的驱动器
CString strRootPathName;
Driver->GetWindowText(strRootPathName);

//获得驱动器序列号
LPCTSTR lpRootPathName = strRootPathName;
LPTSTR lpVolumeNameBuffer=new char[12];
DWORD nVolumeNameSize=12;
DWORD VolumeSerialNumber;
DWORD MaximumComponentLength;
DWORD FileSystemFlags;
LPTSTR lpFileSystemNameBuffer=new char[10];
DWORD nFileSystemNameSize=10;
GetVolumeInformation(lpRootPathName,
            lpVolumeNameBuffer, nVolumeNameSize,
            &VolumeSerialNumber;,
            &MaximumComponentLength;,
            &FileSystemFlags;,
            lpFileSystemNameBuffer, nFileSystemNameSize);

//显示驱动器序列号
CString str;
str.Format("驱动器%s的序列号为%x",strRootPathName,VolumeSerialNumber);
AfxMessageBox(str);

}

void CDrvSrlNmbrDlg::FindAllDrivers() { CComboBox Driver=(CComboBox)GetDlgItem(IDC_DRIVER); DWORD dwNumBytesForDriveStrings;//实际存储驱动器号的字符串长度 HANDLE hHeap; LPSTR lp; CString strLogdrive;

//获得实际存储驱动器号的字符串长度
dwNumBytesForDriveStrings=GetLogicalDriveStrings(0,NULL)*sizeof(TCHAR);

//如果字符串不为空,则表示有正常的驱动器存在
if (dwNumBytesForDriveStrings!=0) {
    //分配字符串空间
    hHeap=GetProcessHeap();
    lp=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,
        dwNumBytesForDriveStrings);

    //获得标明所有驱动器的字符串
    GetLogicalDriveStrings(HeapSize(hHeap,0,lp),lp);

    //将驱动器一个个放到下拉框中
    while (*lp!=0) {
        Driver->AddString(lp);
        lp=_tcschr(lp,0)+1;
    }
}
else
    AfxMessageBox("Can't Use The Function GetLogicalDriveStrings!");

}

在Picture控件中显示轮换图象

发表于 2005-04-26 | 分类于 技术控 | | 阅读次数:
字数统计: 67 字 | 阅读时长 ≈ 1 分钟

启动Timer SetTimer(100,100,NULL); 添加WM_TIMER处理(ClassView右键CxxxDlg选择Add windows message handler然后选择WM_TIMER add and edit) void CxxxDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default CStatic next = IDB_BITMAP2; CBitmap bmp; bmp.LoadBitmap(next); ((CStatic*)GetDlgItem(IDC_PIC))->SetBitmap(bmp.operator HBITMAP()); next = next== IDB_BITMAP1 ? IDB_BITMAP2:IDB_BITMAP1; CDialog::OnTimer(nIDEvent); }

浏览对话框的调用实现

发表于 2005-04-02 | 分类于 技术控 | | 阅读次数:
字数统计: 60 字 | 阅读时长 ≈ 1 分钟

代码:

char buf[255]; ZeroMemory(buf, sizeof(buf)); BROWSEINFO bi; bi.hwndOwner = m_hWnd; bi.pidlRoot = NULL; bi.pszDisplayName = buf; bi.lpszTitle = “请选择保存路径”; bi.ulFlags = 0; bi.lpfn = NULL; bi.lParam = 0; bi.iImage = 0; LPITEMIDLIST lp = SHBrowseForFolder(&bi;); if(lp!=NULL)SHGetPathFromIDList(lp,buf); m_FileSavePath.Format(“%s”,buf); GetDlgItem(IDC_STATIC2)->SetWindowText(buf);

MFC应用程序中指针的使用

发表于 2005-03-11 | 分类于 技术控 | | 阅读次数:
字数统计: 2.3k 字 | 阅读时长 ≈ 8 分钟

VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与操作。其实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多问题都能解决。 下面文字主要是个人在编程中指针使用的一些体会,说的不当的地方请指正。 一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架,无论是多文档还是单文档,都存在指针获取和操作问题。 下面这节内容主要是一般的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应的头文件。首先一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主要可以通过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的功能。

1) 在View中获得Doc指针 CYouSDIDoc pDoc=GetDocument();一个视只能有一个文档。 2) 在App中获得MainFrame指针 CWinApp 中的 m_pMainWnd变量就是MainFrame的指针 也可以: CMainFrame pMain =(CMainFrame )AfxGetMainWnd(); 3) 在View中获得MainFrame指针 CMainFrame pMain=(CmaimFrame )AfxGetApp()->m_pMainWnd; 4) 获得View(已建立)指针 CMainFrame pMain=(CmaimFrame )AfxGetApp()->m_pMainWnd; CyouView pView=(CyouView )pMain->GetActiveView(); 5) 获得当前文档指针 CDocument pCurrentDoc =(CFrameWnd )m_pMainWnd->GetActiveDocument(); 6) 获得状态栏与工具栏指针 CStatusBar pStatusBar=(CStatusBar )AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR); CToolBar pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);

7) 如果框架中加入工具栏和状态栏变量还可以这样 (CMainFrame )GetParent()->m_wndToolBar; (CMainFrame )GetParent()->m_wndStatusBar;

8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd->GetMenu(); 9) 在任何类中获得应用程序类 用MFC全局函数AfxGetApp()获得。

10) 从文档类取得视图类的指针 我是从http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的,从文档获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会特别是文字处理CEditView当产生多个视图类时,这个功能是非常需要的。 CDocument类提供了两个函数用于视图类的定位: GetFirstViewPosition()和GetNextView() virtual POSITION GetFirstViewPosition() const; virtual CView* GetNextView(POSITION& rPosition) const;

注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。 GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作): CTestView* pTestView; POSITION pos=GetFirstViewPosition(); pTestView=GetNextView(pos);

这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如: pView->IsKindOf(RUNTIME_CLASS(CTestView)); 即可检查pView所指是否是CTestView类。

有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下: CView CTestDoc::GetView(CRuntimeClass pClass) { CView* pView; POSITION pos=GetFirstViewPosition();

while(pos!=NULL){
    pView=GetNextView(pos);
    if(!pView->IsKindOf(pClass))
    break;
}

if(!pView->IsKindOf(pClass)){
    AfxMessageBox("Connt Locate the View.\r\n http://www.VCKBASE.com");
    return NULL;
}

return pView;

}

其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:

1.pos为NULL,即已经不存在下一个视图类供操作; 2.pView已符合要求。

1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能既是pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一个视图类时就如引。因此需采用两次判断。 使用该函数应遵循如下格式(以取得CTestView指针为例):CTestView pTestView=(CTestView)GetView(RUNTIME_CLASS(CTestView)); RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为CRuntimeClass为指针。 至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。

3.从一个视图类取得另一视图类的指针 综合1和2,很容易得出视图类之间互相获得指针的方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,以文档类的视图定位函数取得另一个视图类。同样,可以实现成一个函数: (假设要从CTestAView中取得指向其它视图类的指针)CView CTestAView::GetView(CRuntimeClass pClass) { CTestDoc pDoc=(CTestDoc)GetDocument(); CView* pView; POSITION pos=pDoc->GetFirstViewPosition(); while(pos!=NULL){ pView=pDoc->GetNextView(pos); if(!pView->IsKindOf(pClass)) break; } if(!pView->IsKindOf(pClass)){ AfxMessageBox(“Connt Locate the View.”); return NULL; }

return pView;

} 这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员函数。 有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:CTestBView pTestbView=(CTestView)GetView(RUNTIME_CLASS(CTestBView)); 11)对于单文档中也可以加入多个文档模板,但是一般的开发就使用MDI方式开发多文档模板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,请查阅MSDN,(以下四个内容(11、12、13、14)来源:http://sanjianxia.myrice.com/vc/vc45.htm)

可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置; 利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。 POSITION GetFirstDocTemplate( ) const; CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;

第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。

12)一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。 用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与模板相关的文档列表。函数原形为: viaual POSITION GetFirstDocPosition( ) const = 0; visual CDocument *GetNextDoc(POSITION & rPos) const = 0;

如果列表为空,则rPos被置为NULL.

13)在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。函数原形如下: CDocTemplate * GetDocTemplate ( ) const; 如果该文档不属于文档模板管理,则返回值为NULL。

14)一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自己的需要调用该函数。 Virtual POSITION GetFirstViewPosition( ) const; Virtual CView * GetNextView( POSITION &rPosition;) cosnt;

应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL.

15)从一个视图类取得另一视图类的指针 这个应用在多视的应用程序中很多见,一般如果自己在主程序或者主框架中做好变量记号,也可以获得,还有比较通用的就是用文档类作中转,以文档类的视图遍历定位,取得另一个视图类。这个功能从本文第10项中可以得到。

这些资料大部分都是从网上和MSDN中获得的,写这个文档就是为了让大家不用再寻找,列出标题,可操作性更强。

如何把一个程序注册成系统服务

发表于 2005-02-21 | 分类于 技术控 | | 阅读次数:
字数统计: 490 字 | 阅读时长 ≈ 2 分钟

//注册并启动/终止服务 BOOL RegisterServer(LPCTSTR lpServiceName,LPCTSTR lpDisplayName,LPCTSTR lpBinaryPathName) { SC_HANDLE hSCManager = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); if(hSCManager == NULL) { return FALSE; }

if(CreateService(hSCManager,lpServiceName,lpDisplayName,
    SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
    SERVICE_ERROR_NORMAL,lpBinaryPathName,NULL,NULL,NULL,NULL,NULL))
{
    printf("RegisterServer...\r\n");

    CloseServiceHandle(hSCManager);
    return TRUE;
}
else
{
    printf("RegisterServer Error Code = %d\r\n",GetLastError());

    CloseServiceHandle(hSCManager);
    return FALSE;
}

}

BOOL ExecuteService(LPCTSTR lpServiceName) { SC_HANDLE hSCManager,hService; hSCManager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if(hSCManager == NULL) { return FALSE; }

hService = OpenService(hSCManager,lpServiceName,SERVICE_ALL_ACCESS);
if(hService == NULL)
{
    CloseServiceHandle(hSCManager);
    return FALSE;
}

if(StartService(hService,0,NULL))
{
    printf("StartService...\r\n");

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return TRUE;
}
else
{
    printf("StartService Error Code = %d\r\n",GetLastError());

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return FALSE;
}

}

BOOL TerminateService(LPCTSTR lpServiceName) { SC_HANDLE hSCManager,hService; hSCManager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if(hSCManager == NULL) { return FALSE; }

hService = OpenService(hSCManager,lpServiceName,SERVICE_ALL_ACCESS);
if(hService == NULL)
{
    CloseServiceHandle(hSCManager);
    return FALSE;
}

if(StartService(hService,0,NULL))
{
    SERVICE_STATUS ServerStatus;
    ControlService(hService,SERVICE_CONTROL_STOP,&ServerStatus;);

    printf("TerminateService...\r\n");

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return TRUE;
}
else
{
    printf("TerminateService Error Code = %d\r\n",GetLastError());

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return FALSE;
}

}

//要做成服务的EXE必须这样写

#include

#include

#include

#define SERVERNAME “CooldogServer”

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv); void WINAPI ServiceControl(DWORD nControlCode); BOOL ExecuteService(); void TerminateService(); DWORD WINAPI ServiceProc(LPVOID lpParameter); BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,DWORD dwWaitHint);

SERVICE_STATUS_HANDLE hServiceStatus; DWORD ServiceCurrentStatus; HANDLE hServiceThread; BOOL bServiceRunning; HANDLE hServiceEvent;

void main(int argc, char* argv[]) { SERVICE_TABLE_ENTRY ServiceTable[] = { {SERVERNAME,(LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL,NULL} };

if(!StartServiceCtrlDispatcher(ServiceTable))
{
    printf("RegisterServer First");
}

}

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { hServiceStatus = RegisterServiceCtrlHandler(SERVERNAME,(LPHANDLER_FUNCTION)ServiceControl); if(!hServiceStatus || !UpdateServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,1,3000)) { return; }

hServiceEvent = CreateEvent(0,TRUE,FALSE,0);
if(!hServiceEvent || !UpdateServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,2,1000))
{
    return;
}

if(!ExecuteService())
{
    return;
}

ServiceCurrentStatus = SERVICE_RUNNING;

if(!UpdateServiceStatus(SERVICE_RUNNING,NO_ERROR,0,0,0))
{
    return;
}

WaitForSingleObject(hServiceEvent,INFINITE);
CloseHandle(hServiceEvent);

}

void WINAPI ServiceControl(DWORD dwControlCode) { switch(dwControlCode) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: ServiceCurrentStatus = SERVICE_STOP_PENDING; UpdateServiceStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000); TerminateService(); return; default: break; }

UpdateServiceStatus(ServiceCurrentStatus,NO_ERROR,0,0,0);

}

BOOL ExecuteService() { DWORD dwThreadID; hServiceThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)ServiceProc,0,0,&dwThreadID;);

if(hServiceThread != NULL)
{
    bServiceRunning = TRUE;
    return TRUE;
}
else
{
    return FALSE;
}

}

void TerminateService() { bServiceRunning = FALSE; SetEvent(hServiceEvent); UpdateServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0); }

DWORD WINAPI ServiceProc(LPVOID lpParameter)//在这个函数里面添加你想要的操作 { while(bServiceRunning) { Beep(450,150); Sleep(4000); }

return 0;

}

BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,DWORD dwWaitHint) { SERVICE_STATUS ServiceStatus; ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = dwCurrentState;

if(dwCurrentState == SERVICE_START_PENDING)
{
    ServiceStatus.dwControlsAccepted = 0;
}
else
{
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
}

if(dwServiceSpecificExitCode == 0)
{
    ServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
}
else
{
    ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
}

ServiceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
ServiceStatus.dwCheckPoint = dwCheckPoint;
ServiceStatus.dwWaitHint = dwWaitHint;

if(!SetServiceStatus(hServiceStatus,&ServiceStatus;))
{
    TerminateService();
    return FALSE;
}

return TRUE;

}

1…404142…47
Timothy

Timothy

Timothy的技术博客,记录技术以及生活点滴

564 日志
8 分类
1187 标签
RSS
github twitter
Links
  • ZWWoOoOo
  • 花開未央
  • 守望轩
  • 大漠说程序
  • ChengBo
  • BlueAndHack
  • 程序员小辉
  • 子痕的博客
  • WoodenRobot
  • VPS大佬
  • 毕扬博客
  • VPSDad
  • 猫爪导航
  • ss1271的奋斗
  • Kian.Li
  • YoungForest
  • Fred's Blog
  • Jacklandrin
© 2025 Timothy
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4