Would there be a way to get the function name (as a string) that called SetLastError? Or, will I just have to live with the
"0xXXXXXXXX - Error description here"?
Sincerely,
Chip
i was looking at that myself
all the text is right there in the data segment - you just have to find a way to connect the dots
when the pe format exe gets loaded, windows looks through the list to resolve externals
problem is, it wasn't meant to be used the other way around
but, i think a routine could be written to create a "reverse" list
for smaller programs, you may be just as well off to create the list yourself
but, for larger programs that use a lot of different functions, the routine might save some space
I really would like to display an error as "[Function Name] - [Error Code] - [Error Description]."
I guess it would be more informative.
Well.. I am using this simple, little piece of code to show the errors:
fThrowError:
mov edi, alloc$(MAX_PATH)
mov edx, alloc$(MAX_PATH)
invoke GetLastError
invoke dw2hex, eax, edx
invoke wsprintf, edi, CADD("0x%s - %s"), edx, LastError$()
invoke MessageBoxA, NULL, edi, NULL, MB_OK or MB_ICONERROR
exit
it gets a bit wordy, but you can also display the address of the invoke
it shouldn't be too difficult to create an error routine that can be used for all invokes in the program
it could call GetLastError and give the error code
pass it the address of a string that identifies the invoke, as well as the address
.
.
INVOKE GetPriorityClass,
hProc
LabelA:
or eax,eax
jnz Success
mov edx,offset GetPrioClsStr ;"GetPriorityClass",0
mov ecx,offset LabelA
call DisplayError
jmp ErrorExit
Success:
.
.
ErrorExit:
.
.
DisplayError PROC
push ecx
push edx
INVOKE GetLastError
push eax
print chr$('Error: ')
pop eax
print str$(eax),' in Function: '
pop eax
print eax
print chr$(' at Address: ')
pop eax
sub eax,5
print uhex$(eax),13,10
ret
DisplayError ENDP
Thank you Dan. I'm not real hip with wordiness :P
This might be useful..
;hWnd = global handle to your main app window (you could just add it as a param to this function)
;AppName = a string giving the name of your main app (for the messagebox titlebar)
ErrorMessageBox proc pMsg:DWORD,errCode:DWORD
LOCAL buff[512]:BYTE
push edi
lea edi,[buff]
invoke lstrcpy, edi,pMsg
mov ecx,SIZEOF buff
lea edx,[edi+eax]
sub ecx,eax
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM,NULL,errCode,0,edx,ecx,NULL
invoke MessageBox, hWnd,edi,ADDR AppName,MB_OK or MB_ICONERROR
pop edi
ret
ErrorMessageBox endp
Example usage..
errFileOpen db "Error opening file.",13,10,0
invoke GetLastError
invoke ErrorMessageBox, ADDR errFileOpen,eax
As for getting the function name, it's quite a bit extra as they're not (normally) stored in the exe. You could add them as debug info and then extract them from that. Or, build a table of the offsets of functions in your code, then use that in the error function to see who the caller was (get the return address from the stack, and see where that falls in the table.)
Hi Tedd
i see them in my exe file
in this case, it is a console app using these command lines
ml /c /coff %1.asm
Link /SUBSYSTEM:CONSOLE /OPT:NOREF %1.obj
ExitProcess.¥.FillConsoleOutputAttribute..Æ.FlushConsoleInputBuffer.ï.GetConsoleCursorInfo..ò.
GetConsoleMode..õ.GetConsoleScreenBufferInfo....GetCurrentProcess...GetCurrentThread..H.
GetPriorityClass..T.GetProcessAffinityMask..j.GetStdHandle..y.GetSystemTimeAsFileTime.p.
SetConsoleCursorInfo..q.SetConsoleCursorPosition..r.SetConsoleMode..u.SetConsoleTextAttribute.v.
SetConsoleTitleA....SetPriorityClass..ž.SetProcessAffinityMask...SetThreadPriority.».Sleep.â.
VirtualProtect..û.WriteFile.kernel32.dll..Î._getch...._kbhit..msvcrt.dll
Ah, my mistake - I read it wrong; I took it as 'which of the functions in my code produced the error.'
Yeah, all the imported functions have their names stored in the executable so they can be imported. There's also a table produced when the program is loaded that receives the addresses of each of those functions, so they can be called. So that could potentially be used for finding which function was called last, but it would be quite hackish and depend on not doing anything at all after calling the function.
I presume the reason for checking where the error was produced is for debugging, so you can find where you went wrong in your code. A much easier way would be to use the _LINE_ directive to tell the error reporter which line it was called from, but masm doesn't appear to have one (or I can't find it) ::)
But, given a source address, you can still easily find where it was called from, and look what was called before that - it should be clear from the error message what caused it (should :bdg)
what i was thinking was writing a little routine that locates the address of the text string for a specific function call
that way, you do not have to duplicate the text in data (saves space AND some typing - lol)
as i mentioned in an earlier post, the tables are made backwards for that purpose, of course
besides the IAT that gets filled with resolved addresses, each string has an offset into the IAT (presumably)
also, at the end of each group of text strings is the library
in other words, all the kernel32.dll's are listed, then text that says "kernel32.dll"
then, as in my example, the msvcrt.dll functions are listed
you could write an error routine that had a disply like this
Error: 5 in Function: SetProcessAffinityMask from Library: kernel32.dll at Address 40201234
the text for "SetProcessAffinityMask" and "kernel32.dll" could come from the already existing list
i know this isn't that hard, and when i get done working on my current project, i may have a look at it
EDIT - the list should always contain ExitProcess and is alphabetically arranged, too
>the text for "SetProcessAffinityMask" and "kernel32.dll" could come from the already existing list
I think the only way in masm is to use GetProcAddress and import all of your functions, but that would be a pain.
Is there any way of making a .obj file with the import section to link with? Lots of externdef's for the .inc file though...
the text exists in the .data section
no reason you couldn't locate it and use it
the IAT entry is easy enough - the invoke has that address
lol Sinsi - you're gonna make me stop working on my current project and make this - lol
Quote from: Tedd on August 24, 2009, 11:51:47 AM
A much easier way would be to use the _LINE_ directive to tell the error reporter which line it was called from, but masm doesn't appear to have one
Try this one :bg
include \masm32\include\masm32rt.inc
.code
start:
MsgBox 0, str$(@Line), "The error line", MB_OK
exit
end start
that's cool Jochen - a masm "pre-assigned equate"
it needs a little adjustment - lol
the line where you get the line number is at least one more than the error line (actually, 3 would be good)
mov eax,@Line
dec eax
i was looking in the masm manual - there are a few other useful values, also
@Date and @Time can be used to mark a file with assembly time info
EDIT - what i meant by "actually, 3 would be good" - a typical example
INVOKE SomeErrorProneFunction,parm2,parm1
or eax,eax
jnz Successful
push dword ptr @Line
INVOKE GetLastError
pop edx
sub edx,3 ;adjust to point to the invoke line
I thought I'd seen one before :cheekygreen:
this works too...
INVOKE SomeErrorProneFunction,parm2,parm1
or eax,eax
jnz Successful
push dword ptr @Line-3 ;adjust to point to the invoke line
INVOKE GetLastError
pop edx
What would have been really cool: If M$ had decided to store the address of the function that called SetLastError...
Here_I_am:
invoke SetLastError, ERROR_INVALID_NAME
00401031 |. 6A 7B push 7B ; /Error = ERROR_INVALID_NAME
00401033 |. E8 BA000000 call <jmp.&kernel32.SetLastError> ; \SetLastError
...
004010F2 $- FF25 68114000 jmp near [<&kernel32.SetLastError>; ntdll.RtlSetLastWin32Error
...
RtlSetLastWin3> 8BFF mov edi, edi ; ntdll.7C920738
7C920342 55 push ebp
7C920343 8BEC mov ebp, esp
7C920345 64:A1 18000000 mov eax, fs:[18]
7C92034B 8B4D 08 mov ecx, [ebp+8]
7C92034E 8948 34 mov [eax+34], ecx
7C920351 5D pop ebp
7C920352 C2 0400 retn 4
Interesting proc, isn't it? 50% are just useless; this should do the job:
7C920345 64:A1 18000000 mov eax, fs:[18]
7C92034B 8B4D 08 mov ecx, [esp+8]
7C92034E 8948 34 mov [eax+34], ecx
7C920352 C2 0400 retn 4
Or, a little bit shorter:
MyError proc
db 64h, 0A1h, 18h, 0h, 0h, 0h ; mov eax, fs:[18]
pop ecx
pop [eax+34h]
jmp ecx
MyError endp
i have developed the basis for extracting the function name, as well as the library name
we all know about the IAT jmp tables
the data table that those jumps point to gets adjusted at load-time (relocated) to indicate the function addresses
but ! :bg there is also another copy of that table that does not get
altered at load-time that may be used to get the addresses of the strings
at the end of "our" .CONST segment definitions is a 4-aligned dword that points to the table (after adding the 400000h)
INCLUDE \masm32\include\masm32rt.inc
.const
OurConstDefines db 1
END_OF_CONST_SEGMENT EQU $
.data
.data?
.code
start: INVOKE GetCurrentProcess
mov edx,END_OF_CONST_SEGMENT+3
and dl,0FCh
mov ecx,400000h
mov edx,[edx]
add edx,ecx
inc ecx
inc ecx
loop00: mov eax,[edx]
or eax,eax
jz exit00
add eax,ecx
push ecx
push edx
print eax
print chr$(13,10)
pop edx
add edx,4
pop ecx
jmp loop00
exit00: exit
END start
i put GetCurrentProcess in there just to have another function in the list
GetStdHandle and WriteFile are from the print macro, of course
output:
GetCurrentProcess
GetStdHandle
WriteFile
ExitProcess
in these tables are dwords that (again, after adding the 400000h) point to the strings
each string is preceeded by a 2-aligned word value that we may ignore and terminated with a 0
there is a seperate table for each library - the tables are seperated by a dword 0
also, at the end of each string group is the name of the library that the group of functions came from
i am working on code that will also support msvcrt.dll errors....
maybe someone in here knows how to do what i want to do
i can use FormatMessage to get API error descriptions - no problem, there
i want to apply the same technique for msvcrt.dll errors
here is my current code, to give you an idea...
include \masm32\include\masm32rt.inc
.data
DirName db 'C:\xyzzy',0 ;a path that does not exist
crtName db 'msvcrt.dll',0
.data?
ErrBuf db 1024 dup(?)
.code
start:
push offset DirName
call crt__chdir
add esp,4
call crt__errno
push dword ptr [eax]
invoke GetModuleHandle,offset crtName
pop edx
invoke FormatMessage,FORMAT_MESSAGE_FROM_HMODULE,eax,edx,0,offset ErrBuf,1024,NULL
print offset ErrBuf
exit
end start
i am getting the correct error code (2=path not found) from errno
the module handle should be right
so, it boils down to FormatMessage not doing what i was hoping
any suggestions ?
EDIT - there aren't that many crt error codes - i suppose i could make a table and map them to winAPI codes
I tried a C app where I attempted to get the error descriptions with _RTC_GetErrDesc (http://msdn.microsoft.com/en-us/library/a74161bs(VS.80).aspx), but I could only get a few of them because _RTC_NumErrors always reported 4, even when I linked with the debug library. In any case, there is a list here (http://msdn.microsoft.com/en-us/library/t3ayayh1(VS.80,loband).aspx).
thanks for the tip, Michael
that function is not part of msvcrt
it is defined in rtcapi.h
i am guessing (lol) that it is in RunTmChk.lib
i get lost looking at C code - let alone their include files - lol
the rtcapi.h file i have says these functions are deprecated
any ideas how to declare the externdef ?
FormatMessage will try to look up a string for the error code from the module you give it a handle to - if there are none, obviously it can't find them. In general, just use FORMAT_MESSAGE_FROM_SYSTEM (as in the code I posted) - that covers all standard error codes, and many from modules that have already been loaded by the system.
thanks Tedd - that part works
the msvcrt error codes are different, however
the text strings are in the msvcrt.dll file - they just aren't available as resources
i may be able to use perror or one of the other crt functions to get the strings
it's just a matter of playing with it - lol
a good experience, anyways - i get to know my way around the crt a bit
got it
crt_strerror returns the address of the string
EDIT
there are two functions, crt__strerror and crt_strerror
they don't behave like the documentation suggests (big surprise)
crt_strerror seems to be the one to use
msvcrt.dll error message list
0 No error
1 Operation not permitted
2 No such file or directory
3 No such process
4 Interrupted function call
5 Input/output error
6 No such device or address
7 Arg list too long
8 Exec format error
9 Bad file descriptor
10 No child processes
11 Resource temporarily unavailable
12 Not enough space
13 Permission denied
14 Bad address
15 Unknown error
16 Resource device
17 File exists
18 Improper link
19 No such device
20 Not a directory
21 Is a directory
22 Invalid argument
23 Too many open files in system
24 Too many open files
25 Inappropriate I/O control operation
26 Unknown error
27 File too large
28 No space left on device
29 Invalid seek
30 Read-only file system
31 Too many links
32 Broken pipe
33 Domain error
34 Result too large
35 Unknown error
36 Resource deadlock avoided
37 Unknown error
38 Filename too long
39 No locks available
40 Function not implemented
41 Directory not empty
42 Illegal byte sequence
now, i just have to figure out crt__DOSerrno and WSAGetLastError - lol