flat assembler
Message board for the users of flat assembler.

Index > Tutorials and Examples > GradientFill example.

Goto page Previous  1, 2, 3  Next
Author
Thread Post new topic Reply to topic
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 30 Mar 2013, 17:39
revolution wrote:
Well there is still a problem if the cleanup code gets an error. Perhaps you can save and update an error code but still making sure you release all handles, even when something fails.
Yes, I realize that there may be (quite hypothetical) situation when such code may loop when something will go wrong inside the cleanup. So, do you suggest that in the cleanup part we shouldn't check error codes, right?

There is even a more general question: trusted sources say that we always should check the returned values of API functions, everything in order to build reliable and secure applications. Checking error codes during the error handling may cause creation of hypothetical never ending loops (but is still absolutely logical from the MSDN point of view).

From the other hand, if we allow some handles to be not released it may cause the memory leaks as you said.

So, excessive error handling is not as good as it may seem, especially if we want to build simple code examples. Wink
Post 30 Mar 2013, 17:39
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 30 Mar 2013, 18:25
You can't do anything with the error codes anyway. If you get an error, for whatever reason, in the cleanup code then there is nothing you can do about it except record it and (maybe) tell the user about it.

It is still imperative that you release the open handles. A memory leak is more important than an ephemeral GDI error code IMO. GDI errors happen occasionally and they probably just result in a glitch in the display which is corrected on the next refresh. But memory leaks last for the lifetime of the application and they are cumulative.

In my code I never bother with checking release/delete error codes during cleanup because 1) I have nothing to do with such an error code, and 2) it is more important to avoid memory leaks.

But you could, if you think it is necessary, record and accumulate any such errors. Then after releasing all handles check your accumulated error variable to see if something caused an error and pass the error to something.
Post 30 Mar 2013, 18:25
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 30 Mar 2013, 19:19
Actually now I see you are using the code space to store variables. This is going to be problematic if you ever want to open two windows of the same class because this makes the code not-thread-safe and not-reentrant. I wonder why you didn't use the stack (as would be more usual and cause less problems)?
Post 30 Mar 2013, 19:19
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 30 Mar 2013, 20:35
revolution wrote:
Actually now I see you are using the code space to store variables. This is going to be problematic if you ever want to open two windows of the same class because this makes the code not-thread-safe and not-reentrant. I wonder why you didn't use the stack (as would be more usual and cause less problems)?
This is a valuable remark, revolution, but here in this example it's not a problem at all. This may cause problems only if you would create two instances of the main window class inside the same program. As you may have noticed the 'WindowProc' is called only once in this program as a main procedure (not a procedure for a component with gradiented background used many times inside the same application) and I doubt anybody would like to make a kind of sophisticated harakiri as you have been trying to suggest here.

Moreover, if you run this program in any number of instances this also not be an issue here because each of the instances of the application has own separated space for code and data and this also won't cause any problems.

The technique used in the application (storing variables inside the code of the 'WindowProc' procedure) simplifies in this particular case access to the variables. We can load and store their values directly without additional use of 'lea' instruction. Also there is no need to reserve an extra memory for the variables that should preserve their values between following calls of the 'WindowProc' procedure. All this is done by simple trick with variables located inside the procedure body.
Post 30 Mar 2013, 20:35
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 31 Mar 2013, 01:13
MHajduk wrote:
This may cause problems only if you would create two instances of the main window class inside the same program.
But people tend to use and modify examples for their own use. If it was a full program I would not say anything, but for an example we expect people to copy and use for various purposes, not just the posted purpose.
MHajduk wrote:
Moreover, if you run this program in any number of instances this also not be an issue here because each of the instances of the application has own separated space for code and data and this also won't cause any problems.
It does cause the program to use more memory when used in multiple instances because the OS is forced to use copy-on-write to make multiple copies of the code. It can also cause problems with AVs, which get panicky when programs write to a code section. It might be okay with your particular AV but others might experience problems.

In general writing to a code section is just a bad habit. Even though you might not care for this small example it does promote the technique towards using it in other code where this type of habit can cause difficulties.
Post 31 Mar 2013, 01:13
View user's profile Send private message Visit poster's website Reply with quote
uart777



Joined: 17 Jan 2012
Posts: 369
uart777 31 Mar 2013, 04:51
revolution: After reading your posts, I agree with most of the things you say and I have gained much respect for you (for whatever it's worth). However, I would like to ask a question.

Agner Fog's optimization manuals say it's not a good practice to read/write data to/from code section (".text" in PEs) or to write "self-modifiable code".

My question is: What is a "code section"? What distinguishes it from data? Who defines the boundaries and where? Isn't this OS/PE specific?
Post 31 Mar 2013, 04:51
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 31 Mar 2013, 05:05
Code sections are executable (and sometimes also readable).

Data sections are readable/writeable.

The names of the sections are ignored by the OS.

This is not OS or PE specific. The CPU MMU hardware can enforce these rules for any OS or file format.

The AV angle is certainly OS specific but is still worth considering for any program you intend to make widely available.
Post 31 Mar 2013, 05:05
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 31 Mar 2013, 12:56
revolution wrote:
But people tend to use and modify examples for their own use. If it was a full program I would not say anything, but for an example we expect people to copy and use for various purposes, not just the posted purpose.
Okay, at least I understand your point of view now. Frankly, I suspected that it may be just the case.

I have one question: I would like to use constants that are hidden inside the procedure, as for example TRIVERTEX structure that will never be modified, can I left them there (for initialization of the window background)? I think that in this case shouldn't be a problem with data conflict because these values would be always constant and I would like to have some data in one place, tightly tied with the window procedure body.
Post 31 Mar 2013, 12:56
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 31 Mar 2013, 13:05
MHajduk wrote:
I have one question: I would like to use constants that are hidden inside the procedure, as for example TRIVERTEX structure that will never be modified, can I left them there (for initialization of the window background)? I think that in this case shouldn't be a problem with data conflict because these values would be always constant and I would like to have some data in one place, tightly tied with the window procedure body.
AFAIK (and I am not an expert here) this is okay. Reading a code section has never been a trigger point for AVs and never causes any undue behaviour from the OS. But there is the situation of i-cache pollution. The instruction cache may end up with portions of the data (due to pre-fetching and block size overlap) which serve no purpose other than to reduce your available i-cache for actual instructions.

In case others are not aware, you can make read-only data sections also (.idata), and logically the layout it "cleaner" from a file format perspective.
Post 31 Mar 2013, 13:05
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 01 Apr 2013, 18:22
The new version of the program has been published (see the first post in this thread).

Image

The most important changes:
  1. All variables that were placed inside the procedure body have been changed into either local variables or variables stored inside the special extra memory following the window instance. It has been done by setting the value 'cbWndExtra' field of the WNDCLASSEX structure to 8: one double word for the button handle and another double word for the 32-bit pointer to the array of the TRIVERTEX structure instances. These two variables have to be set in order to preserve their values between following window procedure calls.

  2. The error checks aren't so much restrictive as in the previous version (we don't check returned values of the cleaning functions: DeleteDC, DeleteObject, ReleaseDC etc.). Errors occurring during the 'WM_PAINT' and 'WM_SIZE' messages processing are no more "critical" accordingly to the revolution's suggestions.

  3. Completely new error handling routines have been added. A value returned by any API function is compared to zero and in case it is we call the special error handling procedure. Immediately before the procedure call we push on the stack an address where procedure returns if an error occurred. If there was no error (i.e. error code is equal to 'ERROR_SUCCESS') we return to the place following the procedure call. Hence we can name this technique as a "conditional return". See the code pieces below, they should explain everything:
    Code:
    ; Macros used to organize process of error handling. The only macro parameter
    ; is an address where the program flow is redirected after return from the error
    ; handling routine.
    ;
    ; The macros differ in one place: in the 'test reg, reg' instruction. Most of
    ; API functions return 32-bit values but there are some (as 'RegisterClassEx')
    ; that are told to return 16-bit values, so the significant result occupies either
    ; the entire eax register or only its ax part. We test the eax (ax) register
    ; value to check if it is equal to 0 what signalizes a function fail. If the value
    ; is nonzero we jump over next instructions. Otherwise we push 'ReturnTo' on the
    ; stack and call the 'ErrorRoutine'.
    ;
    macro   IfErr   ReturnTo
    {
            test    eax, eax
            jnz     @f
    
            push    ReturnTo
            call    ErrorRoutine
    
            @@:
    }
    
    macro   IfErr16 ReturnTo
    {
            test    ax, ax
            jnz     @f
    
            push    ReturnTo
            call    ErrorRoutine
    
            @@:
    }
    
    (...)
    
            ; The error handling routine that organizes the program flow when any of
            ; API functions returns zero value.
            ;
            ErrorRoutine:
                    invoke  GetLastError
                    test    eax, eax
                    jnz     @f
    
                    ; If there was no error, i.e. the 'GetLastError' function returned
                    ; 'ERROR_SUCCESS', we return to the place when the 'ErrorRoutine'
                    ; procedure was called and remove the 'ReturnTo' address stored on
                    ; the stack before the procedure call.
                    ;
                    retn    4
    
                    @@:
                    ; If there was an error, we display a message box with a human readable
                    ; description of the error, remove the address of the procedure caller
                    ; and return to the 'ReturnTo' address.
                    ;
                    stdcall ShowLastError, NULL, eax
    
                    add     esp, 4
                    retn    


  4. The default gradient can be changed at any time by sending to the window procedure a custom message WM_SETGRADIENT with the 'wparam' set to a pointer to an user defined array of TRIVERTEX structure instances:
    Code:
    invoke  SendMessage, [HwndMain], WM_SETGRADIENT, CustomVertices, 0    
Smile
Post 01 Apr 2013, 18:22
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 01 Apr 2013, 20:00
Doesn't assemble in the FASM beta 1.71.08. Couldn't discern why - working well otherwise. Probably still bugs being worked out of the new label code.

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 01 Apr 2013, 20:00
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 01 Apr 2013, 20:51
bitRAKE wrote:
Doesn't assemble in the FASM beta 1.71.08. Couldn't discern why - working well otherwise. Probably still bugs being worked out of the new label code.
Could you explain me where is the problem? The latest stable release 1.70.03 compiles without any problems.

BTW, why people use unstable beta releases to show that this program doesn't compile? It is rather problem of the FASM beta version compiler (not fully tested as it seems) not my code. Wink

Seems that Tomasz introduced some updates in the planned new releases that may break the compatibility with code written for older (yet still latest stable!) releases. It sounds bad, very bad... Rolling Eyes
Post 01 Apr 2013, 20:51
View user's profile Send private message Visit poster's website Reply with quote
hopcode



Joined: 04 Mar 2008
Posts: 563
Location: Germany
hopcode 01 Apr 2013, 22:38
compiles and works ok using 1.71.09 and macros from 1.69.34 i didnt update them. and needed TRIVERTEX struct
Code:
struct TRIVERTEX
  x        dd ?
  y        dd ?
  Red    dw  ?
  Green dw ?
  Blue   dw ?
  Alpha dw ?
ends
    

Cheers,
Very Happy


Description:
Filesize: 17.6 KB
Viewed: 20995 Time(s)

gradientfill.png



_________________
⠓⠕⠏⠉⠕⠙⠑
Post 01 Apr 2013, 22:38
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 01 Apr 2013, 23:09
As Tomasz explained in this thread: http://board.flatassembler.net/topic.php?t=15293 the bug is hidden in the new version of include file UTF8.INC and will be fixed soon. Smile
Post 01 Apr 2013, 23:09
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 02 Apr 2013, 14:39
MHajduk: Are you aware that you are still writing to your code section?
Code:
;...
section '.text' code readable writeable executable

        HwndMain        dd 0    ; A handle of the main application window.
;...    
Post 02 Apr 2013, 14:39
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 02 Apr 2013, 17:01
revolution wrote:
MHajduk: Are you aware that you are still writing to your code section?
Code:
;...
section '.text' code readable writeable executable

        HwndMain        dd 0    ; A handle of the main application window.
;...    
Yeah, it was an unintended and unwanted "heritage" of the previous version when data & code were mixed in one section. Now, in the new version, they should be definitely separated:
Code:
(...)

; The data section.
;
section '.data' data readable writeable

        HwndMain        dd 0    ; A handle of the main application window.

        HwndBis         dd 0    ; A handle of the additional resizable application
                                ; window.

(...)
                                
; The code section.
;
section '.code' code readable executable

        start:

(...)    
But there is something more interesting that seems I overlooked yesterday. I mean the 'IfErr' and 'IfErr16' macros. Because the part where we check if the eax register is zero could be moved to the error routine, so the code becomes even shorter than the previous one. The new error routine looks like it is presented below:
Code:
        ; The error handling routine that organizes the program flow when any of
        ; API functions returns zero value.
        ;
        ErrorRoutine16:
                ; Zeroize bits of the eax register from the 16th to the 31st one.
                ; Done to assure that the eax register won't contain any "garbage"
                ; after calling API functions returning 16-bit result.
                ;
                and     eax, 0xFFFF

        ErrorRoutine:
                ; Check value stored in the eax register. If it's nonzero then
                ; return to the address following the place the error procedure
                ; has been called.
                ;
                test    eax, eax
                jz      @f

                retn    4

                ; Check value of the last error that has occured.
                ;
                @@:
                invoke  GetLastError
                test    eax, eax
                jnz     @f

                ; If there was no error, i.e. the 'GetLastError' function returned
                ; 'ERROR_SUCCESS', we return to the place when the 'ErrorRoutine'
                ; procedure was called and remove the 'ReturnTo' address stored on
                ; the stack before the procedure call.
                ;
                retn    4

                @@:
                ; If there was an error, we display a message box with a human readable
                ; description of the error, remove the address of the procedure caller
                ; and return to the 'ReturnTo' address.
                ;
                stdcall ShowLastError, NULL, eax

                add     esp, 4
                retn
    
After this modification 'IfErr' and 'IfErr16' could have the form presented here:
Code:
macro   IfErr   ReturnTo
{
        push    ReturnTo
        call    ErrorRoutine
}

macro   IfErr16 ReturnTo
{
        push    ReturnTo
        call    ErrorRoutine16
}    
but since these notations are practically equivalent to
Quote:
stdcall ErrorRoutine, <ReturnTo>

and

stdcall ErrorRoutine16, <ReturnTo>
we may completely replace these macros by corresponding stdcalls.

The new version of the program has been published in the first post of this thread.
Post 02 Apr 2013, 17:01
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 02 Apr 2013, 17:09
BTW, I'd like to announce that after a "silent fix" of the bug in the include file UTF8.INC made by Tomasz Grysztar the FASM 1.71.09 beta version compiles the 'GradientFill' example without any problems.
Post 02 Apr 2013, 17:09
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20627
Location: In your JS exploiting you and your system
revolution 02 Apr 2013, 17:11
MHajduk wrote:
Yeah, it was an unintended and unwanted "heritage" of the previous version when data & code were mixed in one section.
It is so common that the temporary code one writes often ends becoming permanent. Starting with a good layout can pay off multiple times over, and cause many less problems in the long term. I sometimes find it quite depressing to think about how many times I have rewritten the same piece of code before I can finally get it right. For me, a gram of planning can be worth a tonne of future rewards.
Post 02 Apr 2013, 17:11
View user's profile Send private message Visit poster's website Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 02 Apr 2013, 17:38
revolution wrote:
I sometimes find it quite depressing to think about how many times I have rewritten the same piece of code before I can finally get it right.
Honestly, it's quite difficult to me to imagine that you can make any mistakes in your code. I've always received you as a person who can answer immediately presenting a code in the final, perfect form. Wink
Post 02 Apr 2013, 17:38
View user's profile Send private message Visit poster's website Reply with quote
hopcode



Joined: 04 Mar 2008
Posts: 563
Location: Germany
hopcode 02 Apr 2013, 20:05
revolution wrote:
For me, a gram of planning can be worth a tonne of future rewards.
agree 100%. it is a MUST when being a professional, for several important reasons. i can confirm it in my little experience of 6 absolute years of assembly programming.
Cheers,
Very Happy

_________________
⠓⠕⠏⠉⠕⠙⠑
Post 02 Apr 2013, 20:05
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page Previous  1, 2, 3  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.