Monday, April 11, 2011

Windows Mobile API calls - success but GetLastWin32Error returns error code - should I be worried?

I am a newbie to PInvoke calls. I have googled this as it seems like a simple enough question but no joy.

I am making muliple Windows Mobile API calls in a row (to detect if my app is already running and then re-activate it). Everything works fine and dandy but I wanted to put in logging etc for the times when it doesn't work OK.

While writing this code I found that I will get error codes even when my calls apparently return a valid result and the valid result is used successfully by a subsequent call (proving it's valid I think.)

E.g. I call CreateToolhelp32Snapshot which gives me back a handle to a snapshot of currently running processes. Calling Marshal.GetLatWin32Error immediatley after returns Error 6 which apparently means Invalid Handle. But the returned value is used successfully by the subsequent calls to other methods and the whole process works. It's definitely this particular call that sets Error 6 because if you call Marshal.GetLatWin32Error just before the call it returns 0.

Calls to Process32First and Process32Next exhibit similar behaviour i.e. they give me process information happily but sometimes set Error 6. I (think) I know this is happening because I call Marshal.GetLatWin32Error immediatley before and after each call and sometimes it is 0 before and 6 after. Currently I am always getting a successful result (either a handle that works with subsequent calls or a value that casts to 1 if int or true if bool and process info successfully copied into my buffer).

So.....should I care? It's all working.... Do I care what error gets set if I get back a result that is used successfully by the rest of the process? My gut says yes I do, surely? Either way, how do I actually know if everything is OK if I apparently get a successful result back but an error code is also set? I am having nightmares about releasing an unstable system that won't be giving me useful error information back........

UPDATE

This is the results of my loggong code to try and illustrate what I'm talking about for anyone interested. In brief, the code checks for already-running copies of itself, then for already-running copies of the client and then reactivates client if it was found.

Method name: CreateToolhelp32Snapshot, Result: 605618176, Pre-call error code: 0, Post-call error code: 6
Method name: Process32First, Result: True, Pre-call error code: 6, Post-call error code: 6               
Method name: Process32Next, Result: True, Pre-call error code: 6, Post-call error code: 6                
Method name: Process32Next, Result: True, Pre-call error code: 6, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                                                                            
Method name: Process32Next, Result: False, Pre-call error code: 0, Post-call error code: 18              
Method name: CloseToolhelp32Snapshot, Result: True, Pre-call error code: 18, Post-call error code: 6     
Method name: CreateToolhelp32Snapshot, Result: 605618176, Pre-call error code: 6, Post-call error code: 0
Method name: Process32First, Result: True, Pre-call error code: 0, Post-call error code: 0               
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                
Method name: Process32Next, Result: True, Pre-call error code: 0, Post-call error code: 0                                                                      
Method name: Process32Next, Result: False, Pre-call error code: 0, Post-call error code: 18              
Method name: CloseToolhelp32Snapshot, Result: True, Pre-call error code: 18, Post-call error code: 0     
Method name: ShowWindow, Result: True, Pre-call error code: 0, Post-call error code: 120                 
Method name: SetForegroundWindow, Result: True, Pre-call error code: 120, Post-call error code: 6

The results are always true except for the last calls to Process32Next for which failure with code 18 indicates the last entry was reached. And I've just noticed ShowWindow is resulting in Error 120 which is not supported so I'll take that call out.

So my two actual questions are:

Why the errors when I got a success?
And, when the pre-call and post-call errors are the same, how do I know if this was the old error or a second occurence of the same error?

From stackoverflow
  • You shouldn't be calling GetLastWin32Error (GetLastError) unless CreateToolhelp32Snapshot returns INVALID_HANDLE_VALUE.

    See http://msdn.microsoft.com/en-us/library/ms682489(VS.85).aspx

    J M : This is the bit I don't get. The link doesn't say you can *only* call GetLastWin32Error if you get INVALID_HANDLE_VALUE. And the documentation for GetLastWin32Error says it stores errors from the last PInvoke call where SetError is true. Is the last error a work of fiction in this case?
    J M : Or is it just that it doesn't matter unless you get ceratin values back as specified by MSDN?
    J M : Nm, I narrowed my google search based on what you said and found this: http://blogs.msdn.com/adam_nathan/archive/2003/04/25/56643.aspx
    J M : ...which said there are two rules: "Even if an API supports SetLastError/GetLastError, the value returned by GetLastError is only meaningful if the API you just called actually fails. How that failure is indicated depends on the API."
    J M : and backing up sharptooth: "Not all Win32 APIs make use of this mechanism. Check MSDN to find out which ones do and which don't."
    ctacke : +1 - only call it *if* an error code has been returned, otherwise the result is meaningless.
    J M : Thanks ctacke - I was wondering if you would have some input. I was looking into using the OpenNetCf library but got shot down before first base because the client doesn't want any 3rd party libraries. Your answer to another thread helped me out with another of my PInvoke queries.
  • You expect GetLastError() to return 0 after each successful call of any function. But this implies that those functions call SetLastError(0) in their epilogue which is not always done. That's why you can't distinguish between an old error code and a new one. Only call GetLastError() for cases MSDN tells you to do so.

    J M : Is this not handled by the SetLastError = true part of the DllImport attribute? [DllImport("toolhelp.dll", SetLastError = true)] public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processid);
    sharptooth : The description of DllImport attribute in MSDN says that SetLastError you mention here only influences marshalling the GetLastError() to the caller. It is not the WinAPI SetLastError(), just a modifier to the attribute.
    J M : Yes thanks and I also found a blog that expands on what you and Andrew have said - see above comments

0 comments:

Post a Comment