flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
revolution 14 Feb 2011, 09:44
I think anything generic would not have enough flexibility.
Besides you are just moving the problem of error handling to some other place, you are not eliminating your "problem". |
|||
![]() |
|
vid 14 Feb 2011, 10:30
Yep, this method is used for error handling. You write a wrapper for every function call, which checks for returned error code and raises exception based on it. Then in rest of code, you only call your wrappers, never the API directly.
But, if you really want proper cleanup, this might not be worth of effort. For example if you do something like this pseudo-code: Code: call CreateFile cmp eax, -1 je error ... call WriteFile cmp eax, 0 je error2 ... error2: call CloseFile, ... ;cleanup opened file handle error: ret With error code returned directly, this is easy. But with exceptions, you would have to set up different handler after successful CreateFile, one that releases the handle. In real OOP this is solved by compiler automatically calling destructor. In Asm, you have nothing of sorts. You'd have to set up different handler after each call that allocates something what mu be freed, and that might turn out being more troubling than error checking after each API call. For WinAPIs, I personally prefer to error create macro that takes formatted (printf-like) error message, appends string representation of last error (GetLastError + FormatMessage), and displays it. It looks like: Code: ... push [filename] call CreateFile cmp eax, -1 jne @f winerr "Failed to open file '%s'", [filename] mov eax, <MY_ERROR_CODE> jmp <return_from_proc> @@: Upon error, displayed message might look like: Failed to open file 'foobar.txt': The system cannot find the file specified. This way you have all of API-call-related code in one place (no CreateFile_error label somewhere else in code), even though it takes few extra lines. To save few more lines, you can wrap common error code tests to macro, like: Code: ; check for error returned as 0 in eax macro winerr0 errcode, errlabel, [args] { common local ..end cmp eax, 0 jne ..end winerr args mov eax, errcode jmp errlabel ..end: } ... call CreateFile winerr0 MY_ERROR_CODE, return_label, "Failed to open file '%s'", [filename] |
|||
![]() |
|
vid 14 Feb 2011, 10:35
Also note that most of the time when API returns error, you simply want to clean up and return error. Cases where you want to handle one specific error code differently are quite rare.
|
|||
![]() |
|
b1528932 14 Feb 2011, 11:45
Quote: Cases where you want to handle one specific error code differently are quite rare. When doing overlapped io, i always get error. When the other side is closed (pipe), i get diffrent error. Idea of constructor/destructor is ok, but then how to make it. I could do it 'per api', 'per operation', or 'per program'. How do i chose best scale. For example, i want to write data to file. Should i setup constructor/destructor for entire operation, or separatly for CreateFile/CreateIoCompletionPort/PostQueuedCompletionStatus/GetQueuedCompletionStatus/LockFileEx/WriteFile/UnlockFileEx/CloseHandle. How does HLL do it? I recall a term 'class', wich i belive it is what hold those things. Does HLL class (should) handle entire write, or should be wrapped around each api call? I have 0 (literaly, ZERO) experience in HLL, i got stuck multiple times in past inside c problems, and decided that its not for me. Ok i have a question. What if inside my error handler error occur? Note that FormatMessage do not return void. What do i do then? Its just an example, CloseHandle is other one. I always wonder what shgould i do in my error handler when it produce error. I could kill entire program, but exception would be better because if someone is debugging, he could recover. I could just ignore it, and proceed with next part of handler. But then why not ignore error in 'real' program, i cant (or can i?) decide that this code is unimportant and i dont care if error occured. What if CloseHandle fail? I have no solution to this day, i just do conditional jump and return without doing anything. Why functions like CloseHandle (destructors of objects) have even a possibility of failure? I can ensure that object is valid, its illegal to any program interfere with my and tamper with my handles, so i have 100% control of that handle if other programs folows documentation. The only reason closeHandle can fail is HANDLE_FLAG_PROTECT_FROM_CLOSE. Why would they create such thing? And even if they did, calling CloseHandle should be void, if handle cant be closed because of HANDLE_FLAG_PROTECT_FROM_CLOSE, ok it wont but its user dont have to know that handle is still alive. He closed, and can wash his hands from memleak possibility. This option can cause BSoD in XP btw, local DoS. |
|||
![]() |
|
vid 14 Feb 2011, 12:41
Quote:
Of course I meant rare coding-wise, not runtime-wise. Writing extra error code checking here and there doesn't hurt. Quote: Idea of constructor/destructor is ok, but then how to make it. There is no normal way in Asm, without really heavy macroing. That's what we have object-oriented HLL for. Quote: What if inside my error handler error occur? Note that FormatMessage do not return void. What do i do then? If FormatMessage returns error (it does happen!), I only print numeric representation of error code, eg Failed to open 'a.txt': <Windows error 256 (0x100)>. Quote: Its just an example, CloseHandle is other one. If CloseHandle inside error handler fails, there is not much to do, aside from letting programmer know it failed (CloseHandle failing means programmer error). So, theoretical ideal would be to print error in Debug build, and simply continue in Release build. In reality however, everyone ignores return code from CloseHandle. Programming mistakes which could be discovered by checking return value of CloseHandle are quite rare, so no one bothers. Quote: I always wonder what shgould i do in my error handler when it produce error. There is no universal recipe. I'd say you should try to do as much as possible (display error info, clean up), and then return to caller. Why? Some errors are recoverable, some are not. Sometimes you want to recover from error, sometimes you don't. Most often, when writing some inner routine, you don't know if one who calls this routine would want to recover or not. So again, my best "general" advice is to cleanup as much as possible upon error, return (original) error status to caller, and let him decide what to do. Caller can handle error somehow if he wishes, or easily "pass" the error to it its own caller. If unhandled error reaches "topmost" level, simply exit your app. Question at what level to display error really can't be answered generally, you must decide in each case. Note that this requires every procedure to be able to signal error, and error to be checked at every call. In Asm-only projects without too many callbacks, CF is great for this. More "standard" way that can be mixed with HLLs is for every procedure to return error code in EAX (0 means okay, nonzero means error code). This way, passing error from called procedure to caller takes only two extra instructions per call. Looks like this: Code: MyProc proc ;returns error code in eax call SomeFunc ;if there was some error, pass it to caller cmp eax, 0 jnz r call SomeFunc2 ;handle one kind of error cmp eax, ERROR_SOMETHING je handler_for_something ;pass other errors cmp eax, 0 jnz r ;success xor eax, eax r: ret handler_for_something: ... endp |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.