Win32画图应用

Posted

技术标签:

【中文标题】Win32画图应用【英文标题】:Win32 paint application 【发布时间】:2017-06-09 11:44:33 【问题描述】:

我必须在 Windows 中编写一个类似于绘图的程序。我正在使用 MASM32。一切正常,但是当我移动鼠标进行绘图时,它会画一条虚线,而不是实线。

请帮我解决这个问题:(我们对此有其他想法吗? 谢谢你,对不起我的英语不好

P/S:看图看看我的问题!

.386    ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model
option casemap:none

include C:\masm32\include\windows.inc   ; windows.inc have structures and constants
include C:\masm32\include\user32.inc 
includelib C:\masm32\lib\user32.lib ; CreateWindowEx, RegisterClassEx,...
include C:\masm32\include\kernel32.inc 
includelib C:\masm32\lib\kernel32.lib   ; ExitProcess
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
include C:\masm32\include\gdi32.inc 
includelib C:\masm32\lib\gdi32.lib 

.CONST
DRAWING equ 1
WAITING equ 0

.DATA
ClassName db 'SimpleWinClass',0
AppName db 'Paint',0

labelNoti db 'Notification',0
labelClick db 'Start draw!',0
labelRelease db 'Stop draw!',0
labelDrawing db 'Drawing...',0
labelWaiting db 'Waiting.',0

object db '.',0
fontName db 'myfont',0

StaticClassName db 'static',0
X dw 'x',0
Y dw 'y',0

state db WAITING

.DATA?
; HINSTANCE & LPSTR typedef DWORD in windows.inc
; reserve the space for future use
hInstance HINSTANCE ?
CommandLine LPSTR ?
hitpoint POINT <>

; use for create window
wc WNDCLASSEX <?>
msg MSG <?> ; handle message
hwnd HWND ? ; handle window procedure

hwndX HWND ?
hwndY HWND ?
hwndState HWND ?

hdc HDC ?
ps PAINTSTRUCT <?>

font HGDIOBJ ?
hFont HFONT ?

.CODE
start:
    ; call GetModuleHandle(null)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
    push NULL
    call GetModuleHandle    ; module handle same as instance handle in Win32
    mov hInstance, eax  ; return an instance to handle in eax

    ; call GetCommandLine()
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx
    call GetCommandLine ; no parameters
    mov CommandLine, eax    ; return a pointer to the command-line for current process

    ; call WinMain(hInstance, hPrevInstance, CmdLine, CmdShow)
    ; our main function
    push SW_SHOW
    push CommandLine
    push NULL
    push hInstance
    call WinMain

    ; call ExitProcess
    push eax
    call ExitProcess

    ; Define WinMain 
    WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
        ; Structure in msdn, define in windows.inc
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx

        ; Load default icon
        push IDI_APPLICATION
        push NULL
        call LoadIcon
        mov wc.hIcon, eax
        mov wc.hIconSm, eax

        ; Load default cursor
        push IDC_ARROW
        push NULL
        call LoadCursor
        mov wc.hCursor, eax

        mov wc.cbSize, SIZEOF WNDCLASSEX    ; size of this structure
        mov wc.style, CS_HREDRAW or CS_VREDRAW  ; style of windows https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
        mov wc.lpfnWndProc, OFFSET WndProc  ; andress of window procedure
        mov wc.cbClsExtra, NULL 
        mov wc.cbWndExtra, NULL
        push hInstance
        pop wc.hInstance
        mov wc.hbrBackground,COLOR_WINDOW+1 ; background color, require to add 1
        mov wc.lpszMenuName, NULL
        mov wc.lpszClassName, OFFSET ClassName

        ; we register our own class, named in ClassName
        push offset wc
        call RegisterClassEx

        ; after register ClassName, we use it to create windows compond
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
        push NULL
        push hInstance
        push NULL
        push NULL
        push 600
        push 600
        push CW_USEDEFAULT
        push CW_USEDEFAULT
        push WS_OVERLAPPEDWINDOW
        push offset AppName
        push offset ClassName
        push WS_EX_CLIENTEDGE
        call CreateWindowEx

        mov hwnd, eax   ; return windows handle

        ; display window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
        push CmdShow
        push hwnd
        call ShowWindow

        ; update window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
        push hwnd
        call UpdateWindow

        ; Message Loop
        MESSAGE_LOOP:
            ; get message
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
            push 0
            push 0
            push NULL
            push offset msg
            call GetMessage

            ; return in eax
            ; if the function retrieves a message other than WM_QUIT, the return value is nonzero.
            ; if the function retrieves the WM_QUIT message, the return value is zero.
            cmp eax, 0
            jle END_LOOP

            ; translate virtual-key messages into character messages - ASCII in WM_CHAR
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
            push offset msg
            call TranslateMessage 

            ; sends the message data to the window procedure responsible for the specific window the message is for.
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
            push offset msg
            call DispatchMessage
        jmp MESSAGE_LOOP

        END_LOOP:
            mov eax, msg.wParam   
        ret 
    WinMain endp

    ; Handle message with switch(notification)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        ; close app
        cmp uMsg, WM_DESTROY
        je ON_WM_DESTROY

        cmp uMsg, WM_CREATE
        je ON_WM_CREATE

        cmp uMsg, WM_PAINT
        je ON_WM_PAINT

        cmp uMsg, WM_LBUTTONDOWN
        je ON_WM_LBUTTONDOWN

        cmp uMsg, WM_LBUTTONUP
        je ON_WM_LBUTTONUP

        cmp uMsg, WM_MOUSEMOVE
        je ON_WM_MOUSEMOVE

        jmp ON_DEFAULT

        ; user close program
        ON_WM_DESTROY:
            push NULL
            call PostQuitMessage
            jmp EXIT

        ON_WM_CREATE:
            ; create static text
            push NULL
            push hInstance
            push 1
            push hWnd
            push 25
            push 50
            push 20
            push 20
            push WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT
            push offset X
            push offset StaticClassName
            push WS_EX_CLIENTEDGE
            call CreateWindowEx
            mov hwndX, eax

            ; create static text
            push NULL
            push hInstance
            push 1
            push hWnd
            push 25
            push 50
            push 20
            push 90
            push WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT
            push offset Y
            push offset StaticClassName
            push WS_EX_CLIENTEDGE
            call CreateWindowEx
            mov hwndY, eax

            ; create static text
            push NULL
            push hInstance
            push 1
            push hWnd
            push 25
            push 80
            push 60
            push 20
            push WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT
            push offset labelWaiting
            push offset StaticClassName
            push WS_EX_CLIENTEDGE
            call CreateWindowEx
            mov hwndState, eax

            push offset fontName
            push DEFAULT_PITCH or FF_DONTCARE
            push DEFAULT_QUALITY
            push CLIP_DEFAULT_PRECIS
            push CLIP_DEFAULT_PRECIS
            push DEFAULT_CHARSET
            push FALSE
            push FALSE
            push FALSE
            push 700
            push 0
            push 0
            push 16
            push 24
            call CreateFont
            mov font, eax

            jmp EXIT

        ON_WM_LBUTTONDOWN:
            mov [state], DRAWING

            push offset labelDrawing
            push hwndState
            call SetWindowText
            jmp EXIT

        ON_WM_LBUTTONUP:
            mov [state], WAITING

            push offset labelWaiting
            push hwndState
            call SetWindowText
            jmp EXIT

        ON_WM_MOUSEMOVE:
            push lParam
            call updateXY

            cmp [state], DRAWING
            je DRAW
            jne EXIT

            DRAW:
                push FALSE
                push NULL
                push hWnd 
                call InvalidateRect

            jmp EXIT

        ON_WM_PAINT:
            push offset ps
            push hWnd
            call BeginPaint
            mov hdc, eax

            push font
            push hdc
            call SelectObject

            mov hFont, eax

            push 1
            push offset object
            push hitpoint.y
            push hitpoint.x
            push hdc
            call TextOut

            push hFont
            push hWnd
            call SelectObject

            push offset ps
            push hWnd 
            call EndPaint

            jmp EXIT

        ON_DEFAULT:
            ; handle any message that program don't handle
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
            push lParam 
            push wParam 
            push uMsg   ; message
            push hWnd   ; windows
            call DefWindowProc
            jmp EXIT

        EXIT:
            ret
    WndProc endp

    updateXY proc lParam:LPARAM
            mov eax, lParam

            xor ebx, ebx
            mov bx, ax

            mov hitpoint.x, ebx

            push offset X
            push ebx
            call dwtoa

            push offset X
            push hwndX
            call SetWindowText

            mov eax, lParam
            shr eax, 16

            mov hitpoint.y, eax

            push offset Y
            push eax
            call dwtoa

            push offset Y
            push hwndY
            call SetWindowText
        ret
    updateXY endp

end start

【问题讨论】:

有什么理由必须使用汇编语言? 这是一个常见问题,您的代码没有任何问题(实际上,恭喜您的结果)。常用的解决方案是从最后一个位置到当前位置绘制的不是点而是线。第一个动作仍然画一个点。 鼠标的移动速度可以超过 1px。所以坐标没有创建连续的线。您可以在这些点之间绘制简单的线,甚至可以在短时间内缓存坐标,猜测什么是单笔划,并使用一些双三次/样条插值来获得通过点的软曲线......(对于家庭作业来说可能太复杂了,尤其是在汇编中,简单的线条就足够了)。 如果需要画线,请致电MoveToEx和LineTo。无需自己实现线条绘制。如果您需要更高精度的鼠标输入,一个简单的方法是调用GetMouseMovePointsEx。 @IInspectable 谢谢它的工作!!!但是如何增加线条的线宽? 【参考方案1】:

您正在使用字符串输出函数TextOut 输出一个点('.')作为一个点。该字符的(白色)背景比(黑色)数据点多。因此,移动鼠标会用背景覆盖当前位置的点。划线就可以看到效果了。

你需要一个拥有更多黑色数据的角色。让我们选择空格(' ')并反转背景:

object db ' ',0
...
ON_WM_PAINT:
...
    mov hFont, eax
    invoke SetBkColor, hdc, 00FF0000h

    push 1
    push offset object
    push hitpoint.y
    push hitpoint.x
    push hdc
    call TextOut
...

INVOKE 是一个内置的 MASM 宏,它根据前面 PROTO 或 PROC 指令中声明的调用约定或默认调用约定为您执行适当的推送、弹出和调用。例如,您可以更改

push 1
push offset object
push hitpoint.y
push hitpoint.x
push hdc
call TextOut

invoke TextOut, hdc, hitpoint.x, hitpoint.y, offset object, 1

如果您快速移动鼠标,则鼠标加速也会导致虚线。我发现没有简单的方法可以关闭该加速。如果您缓慢移动鼠标,适当大小的倒置空间会减少出现虚线的机会。更改字体的宽度和高度:

push offset fontName
push DEFAULT_PITCH or FF_DONTCARE
push DEFAULT_QUALITY
push CLIP_DEFAULT_PRECIS
push CLIP_DEFAULT_PRECIS
push DEFAULT_CHARSET
push FALSE
push FALSE
push FALSE
push 700
push 0
push 0
push 18                         ; Width
push 12                         ; Height
call CreateFont
mov font, eax

更好的是使用MoveToExLineTo 绘制一条从最后一个鼠标位置到当前鼠标位置的线。要设置线条的粗细,您可以使用CreatePenSelectObject

LineTo 的示例:

.386                ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model % STDCALL as default calling convention
option casemap:none

include C:\masm32\include\windows.inc
include C:\masm32\include\kernel32.inc
include C:\masm32\include\user32.inc
include C:\masm32\include\gdi32.inc
include C:\masm32\include\masm32.inc

includelib C:\masm32\lib\kernel32.lib   ; ExitProcess, GetCommandLineA, GetModuleHandleA
includelib C:\masm32\lib\user32.lib     ; BeginPaint, CreateWindowExA@, DefWindowProcA, DispatchMessageA, EndPaint, GetMessageA, InvalidateRect, LoadCursorA, LoadIconA, PostQuitMessage, RegisterClassExA, SetWindowTextA, ShowWindow, TranslateMessage, UpdateWindow
includelib C:\masm32\lib\gdi32.lib      ; CreatePen, LineTo, MoveToEx, SelectObject
includelib C:\masm32\lib\masm32.lib     ; dwtoa

.CONST
DRAWING equ 1
WAITING equ 0

.DATA
    ClassName db 'SimpleWinClass',0
    AppName db 'Paint',0

    labelDrawing db 'Drawing...',0
    labelWaiting db 'Waiting.',0

    StaticClassName db 'static',0
    X dw 'x',0
    Y dw 'y',0

    state db WAITING

.DATA?
    hInstance HINSTANCE ?
    CommandLine LPSTR ?
    hitpoint POINT <>
    lastpoint POINT <>

    wc WNDCLASSEX <?>
    msg MSG <?> ; handle message
    hwnd HWND ? ; handle window procedure

    hwndX HWND ?
    hwndY HWND ?
    hwndState HWND ?

    hdc HDC ?
    ps PAINTSTRUCT <?>

    hPen HPEN ?

.CODE

updateXY PROC lParam:LPARAM
    movzx eax, WORD PTR lParam
    mov hitpoint.x, eax

    invoke dwtoa, eax, offset X
    invoke SetWindowText, hwndX, offset X

    mov eax, lParam
    shr eax, 16
    mov hitpoint.y, eax

    invoke dwtoa, eax, offset Y
    invoke SetWindowText, hwndY, offset Y
    ret
updateXY ENDP

; https://msdn.microsoft.com/library/windows/desktop/ms633573.aspx
WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    cmp uMsg, WM_MOUSEMOVE
    je ON_WM_MOUSEMOVE

    cmp uMsg, WM_PAINT
    je ON_WM_PAINT

    cmp uMsg, WM_CREATE
    je ON_WM_CREATE

    cmp uMsg, WM_LBUTTONDOWN
    je ON_WM_LBUTTONDOWN

    cmp uMsg, WM_LBUTTONUP
    je ON_WM_LBUTTONUP

    cmp uMsg, WM_DESTROY
    je ON_WM_DESTROY

    jmp ON_DEFAULT

    ON_WM_DESTROY:              ; User closes program
        invoke PostQuitMessage, NULL
        jmp EXIT

    ON_WM_CREATE:
        ; Create windows for text
        invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset StaticClassName, offset X, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT, 20, 20, 50, 25, hWnd, 1, hInstance, NULL
        mov hwndX, eax
        invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset StaticClassName, offset Y, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT, 90, 20, 50, 25, hWnd, 1, hInstance, NULL
        mov hwndY, eax
        invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset StaticClassName, offset labelWaiting, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT, 20, 60, 80, 23, hWnd, 1, hInstance, NULL
        mov hwndState, eax

        ; Create pen for LineTo
        invoke CreatePen, PS_SOLID, 10, 00FF0000h
        mov hPen, eax

        jmp EXIT

    ON_WM_LBUTTONDOWN:

        ; last mouse position = current mouse position
        mov eax, hitpoint.x
        mov lastpoint.x, eax
        mov eax, hitpoint.y
        mov lastpoint.y, eax

        mov [state], DRAWING
        invoke SetWindowText, hwndState, offset labelDrawing
        jmp EXIT

    ON_WM_LBUTTONUP:
        mov [state], WAITING
        invoke SetWindowText, hwndState, offset labelWaiting
        jmp EXIT

    ON_WM_MOUSEMOVE:
        invoke updateXY, lParam                     ; PROC above

        cmp [state], DRAWING
        jne EXIT

        invoke InvalidateRect, hWnd, NULL, FALSE    ; https://msdn.microsoft.com/library/dd145002.aspx
        jmp EXIT

    ON_WM_PAINT:
        invoke BeginPaint, hWnd, offset ps
        invoke MoveToEx, ps.hdc, lastpoint.x, lastpoint.y, NULL
        invoke SelectObject, ps.hdc, hPen;
        invoke LineTo, ps.hdc, hitpoint.x, hitpoint.y
        mov eax, hitpoint.x             ; last mouse position = current mouse position
        mov lastpoint.x, eax
        mov eax, hitpoint.y
        mov lastpoint.y, eax
        invoke EndPaint, hWnd, offset ps
        jmp EXIT

    ON_DEFAULT:     ; handle any message that program don't handle
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam    ; https://msdn.microsoft.com/library/windows/desktop/ms633572.aspx
        jmp EXIT

    EXIT:
        ret
WndProc ENDP

WinMain PROC hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD

    ; WNDCLASSEX structure in MSDN, declaration in windows.inc
    ; https://msdn.microsoft.com/library/windows/desktop/ms633577.aspx
    invoke LoadIcon, NULL, IDI_APPLICATION  ; Load default icon
    mov wc.hIcon, eax
    mov wc.hIconSm, eax
    invoke LoadCursor, NULL, IDC_ARROW      ; Load default cursor
    mov wc.hCursor, eax

    mov wc.cbSize, SIZEOF WNDCLASSEX        ; size of this structure
    mov wc.style, CS_HREDRAW or CS_VREDRAW  ; style of windows https://msdn.microsoft.com/library/windows/desktop/ff729176.aspx
    mov wc.lpfnWndProc, OFFSET WndProc      ; andress of window procedure
    mov wc.cbClsExtra, NULL
    mov wc.cbWndExtra, NULL
    push hInstance
    pop wc.hInstance
    mov wc.hbrBackground,COLOR_WINDOW+1     ; background color, require to add 1
    mov wc.lpszMenuName, NULL
    mov wc.lpszClassName, OFFSET ClassName

    invoke RegisterClassEx, offset wc       ; https://msdn.microsoft.com/library/windows/desktop/ms633587.aspx

    ; https://msdn.microsoft.com/library/windows/desktop/ms632680.aspx
    invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset ClassName, offset AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 600, NULL, NULL, hInstance, NULL
    mov hwnd, eax                       ; Store windows handle
    invoke ShowWindow, hwnd, CmdShow    ; https://msdn.microsoft.com/library/windows/desktop/ms633548.aspx
    invoke UpdateWindow, hwnd           ; https://msdn.microsoft.com/library/windows/desktop/dd145167.aspx

    ; Message Loop
    MESSAGE_LOOP:                       ; https://msdn.microsoft.com/library/windows/desktop/ms644936.aspx

        invoke GetMessage, offset msg, NULL, 0, 0
        test eax, eax
        jle END_LOOP

        invoke TranslateMessage, offset msg
        invoke DispatchMessage, offset msg

        jmp MESSAGE_LOOP

    END_LOOP:
    mov eax, msg.wParam
    ret
WinMain ENDP

main PROC

    invoke GetModuleHandle, NULL    ; https://msdn.microsoft.com/library/windows/desktop/ms683199.aspx
    mov hInstance, eax              ; return an instance to handle in eax

    invoke GetCommandLine           ; https://msdn.microsoft.com/library/windows/desktop/ms683156.aspx
    mov CommandLine, eax            ; return a pointer to the command-line for current process

    invoke WinMain, hInstance, NULL, CommandLine, SW_SHOW

    invoke ExitProcess, eax
main ENDP

END main

【讨论】:

@ĐinhVănKiệt:你能把调用翻译回推送和调用吗?

以上是关于Win32画图应用的主要内容,如果未能解决你的问题,请参考以下文章

快速打开win10画图

快速打开win10画图

快速打开win10画图

JavaFX:仿win画图

win10系统下如何用命令行的方式打开画图软件

windows画图工具打字只显示一半 应该如何解决 比如 打个 张 字 光出现下面半个 上半个就没了