Win32汇编系列七、窗口程序原理及实现

前言

学习c语言的人是不是有段这样的经历,天天面对黑框框敲来敲去,有木有?不知道是否想过用c写一个窗口程序?我想应该也很希望吧,目前我见过的书中很少有介绍窗口程序设计,如果想写的话,还需要去网上、b站搜索,或者看Window程序设计之类的书,使用c写窗口程序其实挺麻烦的,需要了解Windows消息机制、GDI或者GDI+、还有各种控件,如Button,另外自带的控件都太难看,需要使用GDI或GDI+自己绘制,这样工作量又大了很多,所以现在很少有软件用这种最"底层"的方式实现了,都是各种框架,或者使用C#、WPF,我不曾在c中大量写过,但是在VB中大量写过。

这里你可能有觉得矛盾,VB中需要你自己创建窗口、创建控件?当然不用,全是为了学习Windows机制,在VB中,可以不用新建窗口,通过sub main方式启动,在方法中调用大量系统API,完成创建窗口,窗口控件,以及控件的事件响应,这样下来也上百行了,工作量还是很大的。

另外也可以使用别的语言实现,如C#,通过调用系统API,创建窗口等,Java也可以调用Windows API,但是使用相关的API创建窗口,我并没试过(不是说JFrame)。有时间试试。

(其实不太喜欢做Java,我喜欢研究Windows,奈何当初选错了路,导致现在不能回头,如果在回头后会浪费大量时间重新学习,小伙伴做选择要慎重哦!!!也希望有共同爱好的小伙伴一起交流)

实现过程

今天的主角是汇编实现,代码量也不少,原理是invoke调用大量API完成,创建窗口不管用什么语言实现,原理都一样,先注册一个窗口类、窗口调用CreateWindow创建窗口,然后在窗口过程函数中不断处理消息即可(前提这个语言能调用Win Api)。

下面介绍三个必要的函数。

RegisterClassEx

如果要创建一个创建,首先必须调用RegisterClassEx注册一个窗口类,他的参数只有一个,WNDCLASSEX结构体,但是这个结构体有点复杂,成员非常多。

typedef struct WNDCLASSEXA {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
  HICON     hIconSm;
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;

这里不全部介绍了,但是有一个很重要的需要说一下,就是lpfnWndProc成员,指向一个函数过程,这就牵扯出Windows的消息机制。

窗口过程函数

这个不太好理解,但是写过一次Windows程序的人就明白了,当我们操作一个窗口时,不论是单机、双击、右击、鼠标移动、还是任何你发出的事件,它就会接收到系统发过来的消息,消息中就会标识这个事件是什么,然后我们需要根据消息标识做出不同响应,而就需要我们写一个窗口过程函数,告诉系统,发生消息时,你给我回调到这个地方,如果在这个函数中不处理消息,就可以调用DefWindowProc函数来处理,这是系统的默认消息处理函数。

这个函数过程的参数也是固定的,在官方文档中是这样定义的,这里面又会牵扯出句柄的概念,简单说一下,句柄就是标识一个窗口的一个整数,就像身份证号一样。参数uMsg就是消息标识,也是个整数,系统把他们都定义好了,常量名都是WM_开头得,如WM_MOVE,在窗口移动窗口时系统会通知我们。

LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);

CreateWindowEx

创建一个窗口,返回窗口句柄,参数都比较简单,这里最有意思的是窗口的样式参数,有非常多的样式可选择,平常我们是见不到这些样式的。

HWND CreateWindowExA(
  DWORD     dwExStyle,
  LPCSTR    lpClassName,
  LPCSTR    lpWindowName,
  DWORD     dwStyle,
  int       X,
  int       Y,
  int       nWidth,
  int       nHeight,
  HWND      hWndParent,
  HMENU     hMenu,
  HINSTANCE hInstance,
  LPVOID    lpParam
);

CreateWindow完成之后,就需要不断循环从消息队列获取消息,进行分发。

好,上代码!!!

.386 
.model flat,stdcall 
option casemap:none 
include c:\masm32\include\windows.inc 
include c:\masm32\include\user32.inc 
includelib c:\masm32\lib\user32.lib           
include c:\masm32\include\kernel32.inc 
includelib c:\masm32\lib\kernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0       
.DATA?              
hInstance HINSTANCE ?
.CODE             
start: 
invoke GetModuleHandle, NULL   
                        
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, addr wc   
    invoke CreateWindowEx,NULL,\ 
                ADDR ClassName,\ 
                ADDR AppName,\ 
                WS_OVERLAPPEDWINDOW,\ 
                0,\ 
                0,\ 
                300,\ 
                300,\ 
                NULL,\ 
                NULL,\ 
                hInst,\ 
                NULL 
    mov   hwnd,eax 
    invoke ShowWindow, hwnd,SW_SHOWDEFAULT   
    invoke UpdateWindow, hwnd               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
    ret 
WndProc endp 

end start 

运行后,得出个窗口,使用了80多行。
在这里插入图片描述

判断鼠标单击

接下来,我们做个简单的修改,当鼠标单击窗口的时候,在窗口打印"Hello GUI",但是这里面又涉及到HDC的知识,小伙伴自行了解哦。

.386 
.model flat,stdcall 
option casemap:none 
include c:\masm32\include\windows.inc 
include c:\masm32\include\user32.inc 
includelib c:\masm32\lib\user32.lib           
include c:\masm32\include\kernel32.inc 
includelib c:\masm32\lib\kernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0       
HelloGUI db "HelloGUI",0     
.DATA?              
hInstance HINSTANCE ?
hWindowHdc HDC ?
.CODE             
start: 

invoke GetModuleHandle, NULL   
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, ADDR wc   
    invoke CreateWindowEx,NULL,\ 
                ADDR ClassName,\ 
                ADDR AppName,\ 
                WS_OVERLAPPEDWINDOW,\ 
                0,\ 
                0,\ 
                300,\ 
                300,\ 
                NULL,\ 
                NULL,\ 
                hInst,\ 
                NULL 
    mov   hwnd,eax 
    invoke GetDC,eax
    mov  hWindowHdc,eax
    invoke ShowWindow, hwnd,SW_SHOWDEFAULT   
    invoke UpdateWindow, hwnd               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
		LOCAL strRect:RECT
		.IF uMsg == WM_LBUTTONDOWN
			mov strRect.right,100
			mov strRect.bottom,100
			mov strRect.left,0
			mov strRect.top,0
			invoke DrawText,hWindowHdc, ADDR HelloGUI,8,ADDR  strRect,DT_VCENTER
		.ELSE
			invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
			ret
		.ENDIF
    ret 
WndProc endp 

end start 

效果:

在这里插入图片描述

创建按钮

创建控件其实也是通过CreateWindowEx来完成,并不需要我们注册类名,使用系统定义好的类名即可,这部分可以查看文档:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-window-classes。

最终还要给按钮设置WS_TABSTOP OR WS_VISIBLE OR WS_CHILD OR BS_DEFPUSHBUTTON这几个样式,其中WS_CHILD样式代表作为一个子窗口,否则会出现意料之外的。

当按钮单击后,系统会回调WM_COMMAND消息, wParam 低两字节是命令ID,这就需要在CreateWindowEx时候指明。

.386 
.model flat,stdcall 
option casemap:none 
include c:\masm32\include\windows.inc 
include c:\masm32\include\user32.inc 
includelib c:\masm32\lib\user32.lib           
include c:\masm32\include\kernel32.inc 
includelib c:\masm32\lib\kernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0    
ButtonClassName db "Button",0   
ButtonTitle db "按钮",0
HelloGUI db "HelloGUI",0

Message db "单机了按钮",0
MessageTitle db "提示",0
.DATA?              
hInstance HINSTANCE ?
hWindowHdc HDC ?
hButton HWND ?
hWindow HWND ?
.CONST 
ButtonID equ 1 
.CODE             


start: 

invoke GetModuleHandle, NULL   
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, ADDR wc   
    invoke CreateWindowEx,NULL,\ 
                ADDR ClassName,\ 
                ADDR AppName,\ 
                WS_OVERLAPPEDWINDOW,\ 
                0,\ 
                0,\ 
                300,\ 
                300,\ 
                NULL,\ 
                NULL,\ 
                hInst,\ 
                NULL 
    mov   hWindow,eax

    invoke CreateWindowEx,NULL,ADDR ButtonClassName,ADDR ButtonTitle, WS_TABSTOP OR  WS_VISIBLE OR WS_CHILD OR BS_DEFPUSHBUTTON  ,\
    0,0,100,100,hWindow,ButtonID,NULL,NULL
    mov hButton,eax

    invoke GetDC,eax
    mov  hWindowHdc,eax
    invoke ShowWindow, hWindow,SW_SHOWDEFAULT   
    invoke UpdateWindow, hWindow               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
		LOCAL strRect:RECT
		.IF uMsg == WM_LBUTTONDOWN
			mov strRect.right,100
			mov strRect.bottom,100
			mov strRect.left,0
			mov strRect.top,0
			invoke DrawText,hWindowHdc, ADDR HelloGUI,8,ADDR  strRect,DT_VCENTER
		.ELSEIF uMsg ==WM_COMMAND
				mov eax,wParam 
				.IF ax == ButtonID 
					invoke MessageBox,hWindow,ADDR Message,ADDR MessageTitle,MB_OK
				.ENDIF
				
		.ELSE
			invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
			ret
		.ENDIF
    ret 
WndProc endp 

end start 


在这里插入图片描述
在这里插入图片描述

怎么样,学废了吗?

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
用C++写的一个简单的界面演示系统 void CMiniDrawDoc::AddFigure (CFigure *PFigure) { m_FigArray.Add (PFigure); SetModifiedFlag (); } CFigure *CMiniDrawDoc::GetFigure (int Index) { if (Index m_FigArray.GetUpperBound ()) return 0; return (CFigure *)m_FigArray.GetAt (Index); } int CMiniDrawDoc::GetNumFigs () { return m_FigArray.GetSize (); } void CMiniDrawDoc::DeleteContents() { // TODO: Add your specialized code here and/or call the base class int Index = m_FigArray.GetSize (); while (Index--) delete m_FigArray.GetAt (Index); m_FigArray.RemoveAll (); CDocument::DeleteContents(); } void CMiniDrawDoc::OnEditClearAll() { // TODO: Add your command handler code here DeleteContents (); UpdateAllViews (0); SetModifiedFlag (); } void CMiniDrawDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable (m_FigArray.GetSize ()); } void CMiniDrawDoc::OnEditUndo() { // TODO: Add your command handler code here int Index = m_FigArray.GetUpperBound (); if (Index > -1) { delete m_FigArray.GetAt (Index); m_FigArray.RemoveAt (Index); } UpdateAllViews (0); SetModifiedFlag (); } void CMiniDrawDoc::OnUpdateEditUndo(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable (m_FigArray.GetSize ()); } // implementation of figure classes: IMPLEMENT_SERIAL (CFigure, CObject, 3) CRect CFigure::GetDimRect () { return CRect (min (m_X1, m_X2), min (m_Y1, m_Y2), max (m_X1, m_X2) + 1, max (m_Y1, m_Y2) + 1); } void CFigure::Serialize (CArchive& ar) { if (ar.IsStoring ()) ar << m_X1 << m_Y1 << m_X2 << m_Y2 <> m_X1 >> m_Y1 >> m_X2 >> m_Y2 >> m_Color; } IMPLEMENT_SERIAL (CLine, CFigure, 3) CLine::CLine (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = X1; m_Y1 = Y1; m_X2 = X2; m_Y2 = Y2; m_Color = Color; m_Thickness = Thickness; } void CLine::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CLine::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_SOLID, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); // draw figure: PDC->MoveTo (m_X1, m_Y1); PDC->LineTo (m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectangle, CFigure, 3) CRectangle::CRectangle (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = X1; m_Y1 = Y1; m_X2 = X2; m_Y2 = Y2; m_Color = Color; m_Thickness = Thickness; } void CRectangle::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CRectangle::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: PDC->Rectangle (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectFill, CFigure, 3) CRectFill::CRectFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CRectFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: PDC->Rectangle (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); } IMPLEMENT_SERIAL (CRectRound, CFigure, 3) CRectRound::CRectRound (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; m_Thickness = Thickness; } void CRectRound::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CRectRound::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: int SizeRound = (m_X2 - m_X1 + m_Y2 - m_Y1) / 6; PDC->RoundRect (m_X1, m_Y1, m_X2, m_Y2, SizeRound, SizeRound); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectRoundFill, CFigure, 3) CRectRoundFill::CRectRoundFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CRectRoundFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: int SizeRound = (m_X2 - m_X1 + m_Y2 - m_Y1) / 6; PDC->RoundRect (m_X1, m_Y1, m_X2, m_Y2, SizeRound, SizeRound); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); } IMPLEMENT_SERIAL (CCircle, CFigure, 3) CCircle::CCircle (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; m_Thickness = Thickness; } void CCircle::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CCircle::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: PDC->Ellipse (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CCircleFill, CFigure, 3) CCircleFill::CCircleFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CCircleFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: PDC->Ellipse (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); }
下面是一个Win32汇编秒表程序,使用了Windows API中的计时器函数来实现毫秒级的计时和显示: ``` .386 .model flat, stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc includelib \masm32\lib\user32.lib include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib .data msg db 'Time:',13,10,0 count dd 0 tick dd 0 hWnd dd 0 .code start: ; 创建窗口 invoke GetModuleHandle, NULL mov hInstance, eax invoke WinMain, hInstance, NULL, NULL, SW_SHOWDEFAULT invoke ExitProcess, eax WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:DWORD mov eax, hInst mov hInstance, eax ; 注册窗口类 mov wc.cbSize, SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, offset WndProc mov wc.cbClsExtra, 0 mov wc.cbWndExtra, 0 push hInstance pop wc.hInstance mov wc.hbrBackground, COLOR_WINDOW+1 mov wc.lpszMenuName, NULL mov wc.lpszClassName, offset szClassName push IDI_APPLICATION push NULL call LoadIcon mov wc.hIcon, eax mov wc.hIconSm, eax push IDI_APPLICATION push NULL call LoadCursor mov wc.hCursor, eax invoke RegisterClassEx, addr wc ; 创建窗口 invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset szClassName, offset szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, hInstance, NULL mov hWnd, eax invoke ShowWindow, hWnd, SW_SHOW invoke UpdateWindow, hWnd ; 设置计时器 invoke SetTimer, hWnd, 1, 1, NULL ; 消息循环 msgloop: invoke GetMessage, ADDR msg, NULL, 0, 0 cmp eax, 0 je quit invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg jmp msgloop quit: invoke KillTimer, hWnd, 1 invoke DestroyWindow, hWnd invoke UnregisterClass, offset szClassName, hInstance mov eax, msg.wParam ret WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM cmp uMsg, WM_CREATE je wmcreate cmp uMsg, WM_TIMER je wmtimer cmp uMsg, WM_PAINT je wmpaint cmp uMsg, WM_DESTROY je wmdestroy jmp defwndproc wmcreate: mov eax, 0 ret wmtimer: ; 获取当前时间 invoke GetTickCount mov tick, eax ; 计算时间差 sub eax, count mov count, tick ; 更新窗口 invoke InvalidateRect, hWnd, NULL, TRUE invoke UpdateWindow, hWnd ret wmpaint: ; 绘制文本 invoke BeginPaint, hWnd, ADDR ps invoke GetClientRect, hWnd, ADDR rect invoke SetTextColor, ps.hdc, 0000FFh invoke SetBkMode, ps.hdc, TRANSPARENT invoke TextOut, ps.hdc, 10, 10, ADDR msg, sizeof msg - 1 invoke wsprintf, ADDR buf, '%d.%03d', count / 1000, count % 1000 invoke TextOut, ps.hdc, 60, 10, ADDR buf, eax invoke EndPaint, hWnd, ADDR ps ret wmdestroy: mov eax, 0 jmp quit defwndproc: invoke DefWindowProc, hWnd, uMsg, wParam, lParam ret szClassName db 'MyWindowClass',0 szTitle db 'My Window',0 buf db 16 dup (?) rect RECT <0, 0, 320, 240> wc WNDCLASSEX ? ps PAINTSTRUCT <> hInstance HINSTANCE ? hWnd HWND ? msg MSG <> count dd 0 tick dd 0 end start ``` 该程序创建了一个窗口,通过设置计时器每1毫秒触发一次回调函数来实现计时和显示。在回调函数中,通过调用`GetTickCount`函数获取当前时间,计算时间差,并更新窗口显示。窗口绘制使用了Windows API中的GDI函数。 注意:该程序只是一个简单的示例,实际使用中可能需要更复杂的逻辑来处理计时和显示,同时需要注意计时器的精度和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值