问题描述
我有一个简单的 fasm 程序,在这个程序中,我通过 VirtualAlloc 从 Windows 获得了一些归零的内存。然后我有一个过程,我只是在其中设置参数并调用 StretchDIBits 并传递指向空内存缓冲区的指针。因此,我希望屏幕应该是黑色的。然而事实并非如此,我一生都无法弄清楚原因。
下面是代码。
format PE64 GUI
entry main
include 'C:\Users\bmowo\Desktop\Tools\fasm\INCLUDE\win64a.inc'
include '.\main_data.asm'
section '.text' code readable executable
main:
sub rsp,8 ;alignment
invoke GetModuleHandle,0
mov [WindowClass.hInstance],rax
invoke LoadIcon,IDI_APPLICATION
mov [WindowClass.hIcon],rax
mov [WindowClass.hIconSm],rax
invoke LoadCursor,IDC_ARROW
mov [WindowClass.hCursor],rax
mov rax,CS_OWNDC or CS_HREDRAW or CS_VREDRAW
mov [WindowClass.style],eax
invoke RegisterClassExA,WindowClass
test rax,rax
jz exit
invoke CreateWindowExA,WindowClassName,WindowTitle,WS_VISIBLE+WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,CW_USEDEFAULT,[WindowClass.hInstance],0
mov [WindowHandle],rax
test rax,rax
jz exit
mov rax,2*gigabyte
mov r10d,MEM_COMMIT or MEM_RESERVE
invoke VirtualAlloc,eax,r10d,PAGE_READWRITE
mov [Memory],rax
mov [GlobalRunning],1
.main_loop:
cmp [GlobalRunning],0
je exit
stdcall Win32MessagePump
jmp .main_loop
exit:
invoke ExitProcess,[Message.wParam]
proc BlitBuffer
locals
Width dd 0
Height dd 0
PixelsBase dq 0
DC dq 0
BitmapInfo BITMAPINFO 0
ScreenRect RECT
endl
;get window region
lea rax,[ScreenRect]
invoke GetClientRect,[WindowHandle],rax
;calculate width and height
mov ecx,[ScreenRect.bottom]
sub ecx,[ScreenRect.top]
mov [Height],ecx
mov ecx,[ScreenRect.left]
sub ecx,[ScreenRect.right]
mov [Width],ecx
BitmapInfoHeaderSize equ 40
BI_RGB equ 0
mov [BitmapInfo.biSize],BitmapInfoHeaderSize ;sizeof bitmapinfoheader is 40
mov eax,[Width]
mov [BitmapInfo.biWidth],eax
mov eax,[Height]
mov [BitmapInfo.biHeight],eax
mov [BitmapInfo.biPlanes],1
mov [BitmapInfo.biBitCount],32
mov [BitmapInfo.biCompression],BI_RGB
mov [BitmapInfo.biSizeImage],0
mov [BitmapInfo.biXPelsPerMeter],0
mov [BitmapInfo.biYPelsPerMeter],0
mov [BitmapInfo.biClrUsed],0
mov [BitmapInfo.biClrImportant],0
mov [BitmapInfo.RGBQUADa],0
mov [BitmapInfo.RGBQUADb],0
mov [BitmapInfo.RGBQUADc],0
mov [BitmapInfo.RGBQUADd],0
mov rax,[Memory]
mov [PixelsBase],rax
invoke GetDC,[WindowHandle]
mov [DC],rax
lea rax,[BitmapInfo]
DIB_RGB_COLORS equ 0
invoke StretchDIBits,[DC],[Width],[Height],[PixelsBase],rax,DIB_RGB_COLORS,SRCcopY
ret
endp
proc Win32ToggleWindowFullScreen
locals
WindowStyle dd 0
MonitorInfo MONITORINFO sizeof.MONITORINFO
endl
GWL_STYLE equ -16
SWP_NOOWNERZORDER equ 0x0200
SWP_FRAMECHANGED equ 0x0020
SWP_NOMOVE equ 0x0002
SWP_NOSIZE equ 0x0001
SWP_NOZORDER equ 0x0004
HWND_TOP equ 0
SWP_FRAMECHANGED equ 0x0020
WS_OVERLAPPEDWINDOW equ 0xcf0000
MONITOR_DEFAULTTOPRIMARY equ 1
invoke getwindowlongA,GWL_STYLE
mov [WindowStyle],eax
mov rbx,WS_OVERLAPPEDWINDOW
and eax,ebx
jz .else
invoke GetwindowPlacement,GlobalWindowPlacement
mov r8,rax
cmp rax,1
jb .end
invoke MonitorFromWindow,MONITOR_DEFAULTTOPRIMARY
lea rbx,[MonitorInfo]
invoke GetMonitorInfoA,rbx
cmp rax,1
jb .end
mov rbx,not WS_OVERLAPPEDWINDOW ;not rbx
mov eax,[WindowStyle]
and eax,ebx
invoke SetwindowLongA,GWL_STYLE,eax
mov eax,[MonitorInfo.rcMonitor.right]
sub eax,[MonitorInfo.rcMonitor.left]
mov r10d,[MonitorInfo.rcMonitor.bottom]
sub r10d,[MonitorInfo.rcMonitor.top]
mov r11,HWND_TOP or SWP_NOOWNERZORDER or SWP_FRAMECHANGED
invoke SetwindowPos,[MonitorInfo.rcMonitor.left],[MonitorInfo.rcMonitor.top],r11d
jmp .end
.else:
mov eax,[WindowStyle]
or rax,WS_OVERLAPPEDWINDOW
invoke SetwindowLongA,eax
invoke SetwindowPlacement,GlobalWindowPlacement
mov rax,SWP_NOOWNERZORDER or SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER
invoke SetwindowPos,eax
.end:
ret
endp
proc Win32MessagePump
.while_message:
invoke PeekMessageA,Message,PM_REMOVE
cmp eax,0
je .end
cmp [Message.message],WM_KEYDOWN
je .keydown
cmp [Message.message],WM_PAINT
je .paint
.default:
invoke TranslateMessage,Message
invoke dispatchMessage,Message
jmp .while_message
.keydown:
stdcall Win32ToggleWindowFullScreen
.paint:
invoke BeginPaint,PaintStruct
stdcall BlitBuffer
invoke EndPaint,PaintStruct
jmp .while_message
.end:
ret
endp
proc Win32CallbackProc ;hwnd,wmsg,wparam,lparam
cmp edx,WM_DESTROY
je .close
cmp edx,WM_CLOSE
je .close
cmp edx,WM_QUIT
je .close
.default:
invoke DefWindowProcA,rcx,rdx,r8,r9
jmp .end
.close:
mov [GlobalRunning],0
jmp .end
.end:
ret
endp
'''
GlobalWindowPlacement WINDOWPLACEMENT sizeof.WINDOWPLACEMENT
breakit equ int3
kilobyte equ 1024
megabyte equ 1024*1024
gigabyte equ 1024*1024*1024
struct BITMAPINFO
biSize dd ?
biWidth dd ?
biHeight dd ?
biPlanes dw ?
biBitCount dw ?
biSizeImage dd 0
biXPelsPerMeter dd 0
biYPelsPerMeter dd 0
biClrUsed dd 0
biClrImportant dd 0
RGBQUADa db 0
RGBQUADb db 0
RGBQUADc db 0
RGBQUADd db 0
ends
PaintStruct PAINTSTRUCT
struct MONITORINFO
cbSize dd ?
rcMonitor RECT ?
rcWork RECT ?
dwFlags dd ?
ends
section '.data' data readable writeable
GlobalRunning db 0
WindowClassName db "fasm app",0
WindowTitle db "Raytracer or Rasteriser or somethin",0
;WindowClass WNDCLASSEX sizeof.WNDCLASSEX,Win32CallbackProc,COLOR_WINDOW,0
WindowClass WNDCLASSEX sizeof.WNDCLASSEX,0
Message MSG
WindowHandle dq 0
WindowDC dq 0
Memory dq 0
section '.idata' import data readable writeable
library kernel,'kernel32.dll',\
user,'user32.dll',\
gdi,'gdi32.dll'
import kernel,\
GetModuleHandle,'GetModuleHandleA',\
ExitProcess,'ExitProcess',\
VirtualAlloc,'VirtualAlloc',\
VirtualFree,'VirtualFree',\
GetLastError,'GetLastError',\
SetLastError,'SetLastError'\
import user,\
RegisterClassExA,'RegisterClassExA',\
CreateWindowExA,'CreateWindowExA',\
ShowWindow,'ShowWindow',\
UpdateWindow,'UpdateWindow',\
DefWindowProcA,'DefWindowProcA',\
GetMessage,'GetMessageA',\
TranslateMessage,'TranslateMessage',\
dispatchMessage,'dispatchMessageA',\
LoadCursor,'LoadCursorA',\
LoadIcon,'LoadIconA',\
GetClientRect,'GetClientRect',\
GetDC,'GetDC',\
ReleaseDC,'ReleaseDC',\
BeginPaint,'BeginPaint',\
EndPaint,'EndPaint',\
PostQuitMessage,'PostQuitMessage',\
MessageBoxA,'MessageBoxA',\
PeekMessageA,'PeekMessageA',\
getwindowlongA,'getwindowlongA',\
GetwindowPlacement,'GetwindowPlacement',\
SetwindowPlacement,'SetwindowPlacement',\
GetMonitorInfoA,'GetMonitorInfoA',\
SetwindowLongA,'SetwindowLongA',\
SetwindowPos,'SetwindowPos',\
MonitorFromWindow,'MonitorFromWindow'
import gdi,\
StretchDIBits,'StretchDIBits',\
PatBlt,'PatBlt'
结论。有人向我指出,调用宏正在破坏我用于 [BitmapInfo] 的 rax 寄存器,所以这是愚蠢的。解决该问题会导致出现预期的黑屏。
解决方法
调用宏正在破坏通过 rax 传递的参数。
,很抱歉我对 fasm 了解不多,我尝试通过 C++ 重现该问题:
void* p = VirtualAlloc(NULL,512 * 512,MEM_COMMIT,PAGE_READWRITE);
BITMAPINFO bi{};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 512;
bi.bmiHeader.biHeight = 512;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
StretchDIBits(hdc,512,p,&bi,DIB_RGB_COLORS,SRCCOPY);
我尝试使用上面的代码并重现了问题。经过我的测试,我认为分配的内存空间不够。
我修改为:
void* p = VirtualAlloc(NULL,512 * 512 * 4,PAGE_READWRITE);
那么它对我有用。