Window设计程序笔记

Window程序的入口函数。

入口函数和启动函数:
WinMain --WinMainCRTStartup
wWinMain -- wWinMainCRTStartup
Main -- mainCRTStartup
Wmain --  wmainCRTStartup


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)


UNICODE字符

在严格的ASCII中,每个字符用7位表示,或者计算机上普遍使用的每字符有8位宽;而Unicode使用全16位字符集。这使得Unicode能够表示世界上所有的书写语言中可能用于计算机通讯的字符、象形文字和其它符号。这使得一个char长度变成双字节(宽字节)

如果使用的是宽字符那么相关的函数也使用宽函数如wcslen。


Windows的字符串函数
正如前面谈到的,Microsoft C包括宽字符和需要字符串参数的C语言执行时期链接库函数的所有普通版本。不过,Windows复制了其中一部分。例如,下面是Windows定义的一组字符串函数,这些函数用来计算字符串长度、复制字符串、连接字符串和比较字符串:
ILength = lstrlen (pString) ;
pString = lstrcpy (pString1, pString2) ;
pString = lstrcpyn (pString1, pString2, iCount) ;
pString = lstrcat (pString1, pString2) ;
iComp = lstrcmp (pString1, pString2) ;
iComp = lstrcmpi (pString1, pString2) ;
这些函数与C链接库中对应的函数功能相同。如果定义了UNICODE标识符,那么这些函数将接受宽字符串,否则只接受常规字符串。宽字符串版的lstrlenW函数可在Windows 98中执行



窗口与消息



Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给窗口消息处理程序,不用放入消息队列中


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLASwndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;//窗口风格样式
wndclass.lpfnWndProc = WndProc ;//这条叙述将这个窗口类别的窗口消息处理程序设定为WndProc,即HELLOWIN.C中的第二个函数。这个过程将处理依据这个窗口类别建立的所有窗口的全部消息,即指向回调函数
wndclass.cbClsExtra = 0 ;用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间0表示不需要使用
wndclass.cbWndExtra = 0 ;用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间0表示不需要使用
wndclass.hInstance = hInstance ;//字段就是程序的执行实体句柄(它也是WinMain的参数之一)
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;//为所有依据这个窗口类别建立的窗口设置一个图标。图标是一个小的位图图像,它对使用者代表程序,将出现在Windows工作列中和窗口的标题列的左端.现在,为了方便起见,我们将使用预先定义的图示。
要取得预先定义图示的句柄,可以将第一个参数设定为NULL来呼叫LoadIcon。在加载程序写作者自订的图标时(图标应该存放在磁盘上的.EXE程序文件中),这个参数应该被设定为程序的执行实体句柄hInstance.第二个参数代表图示
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;//函数加载一个预先定义的鼠标光标
wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ;//这意味着窗口显示区域的背景完全为白色,这是一种极其普遍的做法
wndclass.lpszMenuNam = NULL ;//下一个字段指定窗口类别菜单。HElLOWIN没有应用程序菜单,所以该字段被设定为NULL
wndclass.lpszClassName= szAppName ;//最后,必须给出一个类别名称。对于小程序,类别名称可以与程序名相同,即存放在szAppName变量中的「HelloWin」字符串。
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
整理编撰:Defoe.Tu tyysoft@yahoo.com.cn
szAppName, MB_ICONERROR) ;
return 0 ;
}

建立窗口
hwnd = CreateWindow( szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT,// initial x position相当于屏幕左上角
CW_USEDEFAULT,// initial y position
CW_USEDEFAULT,// initial x size
CW_USEDEFAULT,// initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters

返回所创建窗口的句柄到hwnd

在CreateWindow呼叫传回之后,Windows内部已经建立了这个窗口。这就是说,Windows已经配置了一块内存


ShowWindow (hwnd, iCmdShow) ;第一个参数是刚刚用CreateWindow建立的窗口句柄。第二个参数是作为参数传给WinMain的iCmdShow。
UpdateWindow (hwnd) ;//会重画显示区域


程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}


typedef struct tagMSG
{
HWND hwnd ;
UINT message ;
WPARAM wParam ;
LPARAM lParam ;
DWORD time ;
POINT pt ;
}
MSG, * PMSG ;

wParam 一个32位的「message parameter(消息参数)」,其含义和数值根据消息的不同而不同。
lParam 一个32位的消息参数,其值与消息有关

time 消息放入消息队列中的时间。
pt 消息放入消息队列时的鼠标坐标。

TranslateMessage (&msg) ;
将msg结构传给Windows,进行一些键盘转换。

DispatchMessage (&msg) 

又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。在HELLOWIN中,这个窗口消息处理程序就是WndProe函数。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。


窗口消息处理程序总是定义为如下形式:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)


一般来说,Windows程序写作者使用switch和case结构来确定窗口消息处理程序接收的是什么消息,以及如何适当地处理它。窗口消息处理程序在处理消息时,必须传回0.


WM_PAINT消息

当窗口显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个消息通知程序

。第一条WM_PAINT消息(通常发生在WinMain中呼叫UpdateWindow时)

对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始:


hdc = BeginPaint (hwnd, &ps) ;
而以一个EndPaint呼叫结束:
EndPaint (hwnd, &ps) ;


GetClientRect (hwnd, &rect) ;
第一个参数是程序窗口的句柄。第二个参数是一个指标,指向一个RECT型态的rectangle结构。该结构有四个LONG字段,分别为left、top、right和bottom。GetClientRect将这四个字段设定为窗口显示区域的尺寸。left和top字段通常设定为0,right和bottom字段设定为显示区域的宽度和高度(像素点数)。

程序使用InvalidateRect或InvalidateRgn函数刻意产生WM_PAINT消息。


WM_DESTROY消息
WM_DESTROY消息是另一个重要消息。这一个消息指示,Windows正在根据使用者的指示关闭窗口。该消息是使用者单击Close按钮或者在程序的系统菜单上选择 Close时发生的(在本章的后面,我们将详细讨论WM_DESTROY消息是如何生效的)。
HELLOWIN通过呼叫PostQuitMessage以标准方式响应WM_DESTROY消息:
PostQuitMessage (0)


串口的实现(从一个现场的类)

声明一个串口类,然后创建BOOL CCommInterface::CreateInterface_CommPort(int nCommPort, int nCommBaudrate,
 const void *pfnInterfaceOpenCallback,
 const void *pfnInterfaceCloseCallback,
 const void *pfnDataProcCallback,
 const void *pParamCallback)


BOOL CCommInterface::AppendCmdToSend(const BYTE byBuf[], int nLen, int nDstPort)发送队列


void OnCommDataRcvd(BYTE * pBuff, int nLength, void * pParam)//串口数据接受



多线程

1.hThread = CreateThread (&security_attributes, dwStackSize, ThreadProc,
pParam, dwFlags, &idThread) ;
第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。
CreateThread的第三个参数是指向线程函数的指标。函数名称没有限制,但是必须以下列形式声明:
DWORD WINAPI ThreadProc (PVOID pParam) ;
CreateThread的第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据。

CreateThread的第五个参数通常为0,但当建立的线程不马上执行时为旗标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。第六个参数是一个指标,指向接受执行绪ID值的变量。
大多数Windows程序写作者喜欢用在PROCESS.H表头文件中声明的C执行时期链接库函数_beginthread。它的语法如下:
2.hThread = _beginthread (ThreadProc, uiStackSize, pParam) ;
它更简单,对于大多数应用程序很完美,这个线程函数的语法为:
void __cdecl ThreadProc (void * pParam) ;


3.3、AfxBeginThread——MFC中线程创建的MFC函数

用户界面线程的AfxBeginThread 
用户界面线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority, 
UINT nStackSize, 
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1是从CWinThread派生的RUNTIME_CLASS类;
参数2指定线程优先级,如果为0,则与创建该线程的线程相同;
参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
参数5表示线程的安全属性,NT下有用。

 

工作者线程的AfxBeginThread 
工作者线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
AFX_THREADPROC pfnThreadProc, 
LPVOID pParam,
int nPriority, 
UINT nStackSize, 
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1  线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
参数2 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。

实际中我们经常这样用 AfxBeginThread(ThreadProc,this);//把this传过去,就可以调用类的成员了. 这样线程函数就可以使用和操作类的成员了。千万要注意线程函数是静态类函数成员


创建了新的线程后,该线程就开始启动执行了。但如果在dwCreationFlags中使用了CREATE_SUSPENDED标志,那么线程并不马上执行,而是先挂起,等到调用ResumeThread后才开始启动线程

当创建线程时,除了使用CREATE_SUSPENDED外,也可以调用SuspendThread函数来暂停线程的运行:

DWORD SuspendThread(HANDLEhThread);



当多个线程共同使用同一个资源的时候可以使用临界区域

CRITICAL_SECTION cs ;
这个CRITICAL_SECTION数据型态是一个结构,但是其中的字段只能由Windows内部使用。这个临界区域对象必须先被程序中的某个线程初始化,通过呼叫:
InitializeCriticalSection (&cs) ;

当临界区域对象被初始化之后,线程可以通过下面的呼叫进入临界区域:
EnterCriticalSection (&cs) ;
在这时,线程被认为「拥有」临界区域对象。两个线程不可以同时拥有同一个临界区域对象,因此,如果一个线程进入了临界区域,那么下一个使用同一临界区域对象呼叫EnterCriticalSection的线程将在函数呼叫中被暂停。只有当第一个线程通过下面的呼叫离开临界区域时,函数才会传回控制权:
LeaveCriticalSection (&cs) ;

这时,在EnterCriticalSection呼叫中被停住的那个线程将拥有临界区域,其函数呼叫也将传回,允许线程继续执行。
当临界区域不再被程序所需要时,可以通过呼叫
DeleteCriticalSection (&cs) ;







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值