I'm new to windows programming and started working on a text game a few months ago. I decided to rewrite the WM_PAINT section of my code, I normally had the entire client area continually redrawn but thought I'd try redrawing specific regions instead to reduce flicker. I noticed in one specific section of my code, InvalidateRect/UpdateWindow doesn't seem to want to work, but if I replace the 2 functions with RedrawWindow, it displays perfectly. Should there be an instance where InvalidateRect/UpdateWindow fails to work but RedrawWindow works fine in it's place?
Would there be any drawbacks to just replace all InvalidateRect/UpdateWindow calls in my code with RedrawWindow?
Thanks for an info!
.if uMsg==WM_PAINT
invoke BeginPaint,hWnd,ADDR ps
mov hdc,eax
.if (PAINT_DMGMSG>0) ;contains ->to a char's DMGMSG
invoke CreateFont,16,12,0,0,400,0,0,0,OEM_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,NULL
invoke SelectObject,hdc,eax
mov hfont,eax
invoke SetTextColor,hdc,COLOR_SNOWWHITE
mov esi,TS_MSG2
mov edi,TS_MSG1
mov ecx,len(TS_MSG2)
rep movsb
mov BYTE ptr [edi],0
mov esi,TS_MSG3
mov edi,TS_MSG2
mov ecx,len(TS_MSG3)
rep movsb
mov BYTE ptr [edi],0
mov esi,PAINT_DMGMSG
mov edi,TS_MSG3
mov ecx,len(esi)
rep movsb
mov BYTE ptr [edi],0
invoke TextOut,hdc,0,500,TS_MSG1,len(TS_MSG1)
invoke TextOut,hdc,0,518,TS_MSG2,len(TS_MSG2)
invoke TextOut,hdc,0,536,TS_MSG3,len(TS_MSG3)
mov PAINT_DMGMSG,0
.endif
invoke EndPaint,hWnd,ADDR ps
.elseif uMsg==WM_CHAR ;used to handle keyboard input
push wParam
pop char
.if(SDWORD ptr [ebx+OFFSET_ALLEGIANCE]<0)&&(DWORD ptr [ebx+OFFSET_STATUS]!=STAT_DEAD)
invoke Combat,[esi+12],ebx
mov ebx,[edi+4]
mov eax,[human.color]
mov [ebx+OFFSET_CHARCOLOR],eax
mov ebx,pChar
mov ebx,[ebx+OFFSET_MOBMEM]
.if (DWORD ptr [ebx+OFFSET_MOBMEM_DMGMSG]>0)
mov ebx,[ebx+OFFSET_MOBMEM_DMGMSG]
mov XYrc.left,0
mov XYrc.top,500
push wWin
pop XYrc.right
mov XYrc.bottom,560 ;+font height
mov PAINT_DMGMSG,ebx
; invoke InvalidateRect,hWnd,ADDR XYrc,TRUE
; invoke UpdateWindow,hWnd
invoke RedrawWindow,hWnd,ADDR XYrc,NULL,NULL ;<- WORKS FINE!
.endif
.endif
Assuming you use 0 as the last argument for the RedrawWindow() (as you do), there indeed is one important difference:
UpdateWindow() sends WM_PAINT immediately, before the function returns.
RedrawWindow() (unless flag RDW_UPDATENOW is used) just places WM_PAINT into the message queue so the painting actually happens later.
This can make difference, if you invalidate the rectangle, but change the window state then after. In case (1), the control is painted using the "old" data, but (2) "works".
If your issue is caused by this difference, you may try to use InvalidateRect() only (do not call UpdateWindow(), nor RedrawWindow(). The rect invalidation will cause then WM_PAINT will come when the queue has no other event in it. (Originally UpdateWindow() prevented that as the immediate WM_PAINT validates the window as a side effect of the painting).
Related
This is the code that I use with FASM:
format PE console
entry main
include '..\MACRO\import32.inc'
section '.data' data readable writeable
msg db "привіт!",0dh,0ah,0 ;hi
lcl_set db ?
section '.code' code readable executable
main:
;fail without set locale
push msg
call [printf]
pop ecx
;succeed with set locale
push msg
call _liapnuty
pop ecx
push 0
call [ExitProcess]
_liapnuty:
push ebp
mov ebp, esp
;sub esp, 0
mov ebx,[ebp+8] ; 1st arg addr
mov al, [lcl_set]
or al, al
jnz _liapnuty_rest
call __set_locale
_liapnuty_rest:
push ebx
call [printf]
pop ebx
mov esp, ebp
pop ebp
ret 0
__set_locale:
mov al, [lcl_set]
or al, al
jnz __set_locale_rest
push 1251
call SetConsoleCP
call SetConsoleOutputCP
pop ecx
mov [lcl_set], 1
;push lcl
;call [system]
; pop ecx
; mov [lcl_set], 1
;push cls
;call [printf]
; pop ecx
__set_locale_rest:
ret 0
section '.idata' import data readable
library kernel,'kernel32.dll',\
msvcrt,'msvcrt.dll'
import kernel,\
SetConsoleCP,'SetConsoleCP',\
SetConsoleOutputCP,'SetConsoleOutputCP',\
ExitProcess,'ExitProcess'
import msvcrt,\
printf,'printf'
It works almost perfectly, except that before exiting it waits for like a second for some reason. It outputs data almost instantly, yet it fails to shut down quickly. If the reason is using these libraries or not clearing the stack after calling ExitProcess (which obviously can't be done), then let me know and I will mostly gladly accept this answer, but I want to be 100% sure I'm doing everything correctly.
The reason for all of it was because kernel32 functions pop their parameters themselves on return. If I remove unnecessary pops it starts working fast again. Of course, the program still runs with damaged stack but it does a lot of damage control at the end. That's why it was slow, but still worked. For everyone facing this issue, make sure to be careful with the calling convention.
To debug the application and find the error I used OLLYDBG. It's free and it works. It helps you debug EXEs and DLLs, allowing to step one command at a time. Also it shows the memory, the stack and all of the registers and flags.
Using the stack I was able to find out that it gets corrupted.
I'm trying to open a file for writing using the CreatFileA system call in assembly in x64, however in order to do so I need to specify the desired access. I don't know what the constant is for GENERIC_WRITE, the GENERIC_READ constant is 80000000h.
; create the file
lea rcx, fileName
mov rdx, 40000000h
xor r8, r8
xor r9, r9
mov QWORD PTR [rsp+48h-28h], 2
mov QWORD PTR [rsp+48h-20h], 80h
mov QWORD PTR [rsp+48h-18h], 0
call CreateFileA
mov FD2, rax
; write to the new file
lea rcx, FD2
lea rdx, buffer
mov r8, len
lea r9, written
mov QWORD PTR [rsp+48h-28h], 0
call WriteFile
mov writeResult, rax
Turning the comments into an answer so this can get closed out.
As Michael points out, the bits that make up the Access Mask are defined here.
Using that we see that GENERIC_READ is 0x80000000 and GENERIC_WRITE is 0x40000000.
Generally speaking, you should probably look at Windows' headers to get the definitive and most up-to-date values for all Windows constants. This one is in Winnt.h.
Addressing the follow-on question, your assembler code to load the handle to be passed to WriteFile is incorrect. You are saving the value returned from CreateFile using
mov FD2, rax
But then you load it back using
lea rcx, FD2
lea is going to return a pointer to the handle, not the handle itself.
So, for once, Windows was really being helpful when it returned the The handle is invalid error message. (By implication) it told which parameter was the source of the problem, and (roughly) what the problem was.
I've spend lot of time trying to solve this problem and I don't understand, why it doesn't work. Problem's description is in comments below:
.386
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE
.NOLIST
.NOCREF
INCLUDE \masm32\include\windows.inc
.LIST
.CODE
DllEntry PROC hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax, TRUE
ret
DllEntry ENDP
caesarAsm proc string: DWORD, key: DWORD, stringLength : DWORD
mov esi, 1 ; I cannot use this register, mov esi, (anything) causes Crash:
; Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention
mov eax, string
ret
caesarAsm endp
END DllEntry
I searched "whole" Internet, I found that the problem is connected with stack, but no operation on stacks helped me to solve it.
I'm using Microsoft Visual Studio 2012
I assume the error does not occur in this function, rather, it is triggered elsewhere. The esi register is a callee-saved register. You must make sure its value is the same at function exit as it was at entry. You can use it in your function, but you must save and restore its value. Such as:
push esi
mov esi, 1
mov eax, string
pop esi
ret
This is all well documented. You may only use eax, ecx and edx without saving.
Side note: you are using high-level features of your assembler, you might want to check the actual generated code or refrain from using them until you are confident in what the result is going to be. Incidentally masm has a USES keyword which would do the save/restore for you.
I'm stepping through Structured Error Handling recovery code in Windows 7
(e.g, what happens after SEH handler is done and passes back "CONTINUE" code).
Here's a function which is called:
7783BD9F mov edi,edi
7783BDA1 push ebp
7783BDA2 mov ebp,esp
7783BDA4 push 1
7783BDA6 push dword ptr [ebp+0Ch]
7783BDA9 push dword ptr [ebp+8]
7783BDAC call 778692DF
7783BDB1 pop ebp
7783BDB2 ret 8
I'm used to the function prolog of "push ebp/mov ebp,esp". What's the purpose
of the "mov edi,edi"?
Raymond Chen (one of the Microsoft developers) has answered this exact question:
Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?
And he links an even earlier reference:
Why does the compiler generate a MOV EDI, EDI instruction at the beginning of functions?
Basically, it leaves space for a jump instruction to be inserted during hot patching.
So I've got a very strange and specific problem here. We're putting seemingly valid code into a linker, and then the linker is making some mistakes, like removing a valid ptr label and instead of replacing it with a value, it's putting in 0 instead. But not everywhere either. It starts at a pretty arbitrary point.
So, some background: we have an interpreted language that is converted into assembly by putting together hand-generated chunks of assembly (by an in-house compiler-like application) and adding in variables where required. This is a system that has been working for around 10 years if not longer, in pretty much it's current form, so the validity of this method is not currently in question. This assembly is assembled to an obj file using the Microsoft Assembler (ml.exe or MASM).
Then in a separate step, this obj file is linked using the Microsoft Incremental Linker together with some other libraries (static, and import libraries for dlls) to create an executable.
Following is a portion of the assembly (.asm) file that the assembler outputs when it creates the obj file in the first step:
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2148
dec di
sym2148: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb49h
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 012656h
push ebx
mov eax, OFFSET sym2151
push eax
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2152
dec di
sym2152: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb32h
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 012656h
push ebx
mov eax, OFFSET sym2155
push eax
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2156
dec di
sym2156: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb4ah
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0126bbh
push ebx
mov eax, OFFSET sym2159
push eax
call _c_rt_strcmp
As far as I can see, that is correct, and makes sense. I believe that code that generates this does a string compare and then stores a value based on the result of the string compare, and it's a section of 20-30 of them, so there are 10 or so more groups before the first bit I posted, and about 15-20 after the last bit I posted.
Following is the disassembled view of the crash location that Visual C++ 5.0 (old I know, but this is on a legacy system unfortunately) shows when the executable crashes:
004093D2 call 00629570
004093D7 mov di,1
004093DB mov ebp,esp
004093DD cmp ax,0
004093E1 je 004093E5
004093E3 dec di
004093E5 mov word ptr [ebp+6],di
004093E9 add esp,6
004093EC mov ebx,dword ptr ds:[64174Ch]
004093F2 add ebx,0BB49h
004093F8 pop ax
004093FA mov byte ptr [ebx],al
004093FC mov ebx,dword ptr ds:[64174Ch]
00409402 add ebx,12656h
00409408 push ebx
00409409 mov eax,0
0040940E push eax
0040940F call 00409414
00409414 mov di,1
00409418 mov ebp,esp
0040941A cmp ax,0
0040941E je 00409422
00409420 dec di
00409422 mov word ptr [ebp+6],di
00409426 add esp,6
00409429 mov ebx,dword ptr ds:[0]
0040942F add ebx,0BB32h
00409435 pop ax
00409437 mov byte ptr [ebx],al
00409439 mov ebx,dword ptr ds:[0]
0040943F add ebx,12656h
00409445 push ebx
00409446 mov eax,0
0040944B push eax
0040944C call 00409451
00409451 mov di,1
00409455 mov ebp,esp
00409457 cmp ax,0
0040945B je 0040945F
0040945D dec di
0040945F mov word ptr [ebp+6],di
00409463 add esp,6
00409466 mov ebx,dword ptr ds:[0]
0040946C add ebx,0BB4Ah
00409472 pop ax
00409474 mov byte ptr [ebx],al
00409476 mov ebx,dword ptr ds:[0]
0040947C add ebx,126BBh
00409482 push ebx
00409483 mov eax,0
00409488 push eax
00409489 call 0040948E
The actual crash location is 0x00409429.
The two bits of code match, as in they are the same section of code, except the first from the .asm file is what is going into the linker, and the second one is that has come out of the linker.
From what I can see, it's crashing at that location because it's attempting to de-reference an address of 0, right? So of course that's going to fail. Also, if you take a look at the different function calls at 0x004093D2, 0x0040940F, 0x0040944C and 0x00409489, only the first one is valid, the others are simply doing a function call to the line directly after them! And this broken code continues on for the next 15-20 similar segments of code that is defined in that file.
If you look at the corresponding lines for the function calls and the bad pointers in both sections, you will see that in the .asm file everything appears to be correct, but somehow the linker just breaks it all when it compiles the exe, and in a very specific spot, as there are similar chunks of code previous in the file which are constructed correctly.
We do get some linker warnings of the form: "LINK : warning LNK4049: locally defined symbol "_smfv1_ptr" imported". But we were getting those same warnings even when it was working.
The assembler used was ml.exe version 6.11, and the linker was link.exe version 5.10.7303 which were both the version from Visual C++ 5.0. Since the assembled code seems to be right, I'm going to be trying the linker from Visual Studio 2005, 2008 and 2010 and see if anything changes.
I can't really imagine what could create this kind of error, and I thought maybe it was symbols getting messed up, but there are jumps to locations (for small 'if' statements) which are stored as symbols which still work fine after they get through the linker.
Is it possible that a symbol table or something similar is getting overloaded inside the linker, and it's just reverting to bad or default values?
Call to the following address is a clear sign of unresolved symbol reference (it makes sense if you notice that all calls in .obj files are emitted as E8 00 00 00 00). You have zeroes for some data references as well (sym2151, some references to _smfv1_ptr, sym2159). What's strange is that the first call to _c_rt_strcmp did get resolved. I would suggest turning on all warning/debugging/verbose switches that you can find, and generating all kinds of listing and map files. Maybe something will jump out.
Okay, so the end result seems to be that it is a bug with the Visual C++ version of the masm assembler "ml.exe" (big surprise, huh?)
So, moving to the versions of masm and link that were provided in Visual Studio 2005 seems to be the best solution for us.
Thanks for your help guys :)