PInvoke calls return success but Marshal.GetLastWin32Error shows error codes - is this normal?

I’m pretty new to working with PInvoke and native Windows CE API functions. I’ve been trying to find info about this online but haven’t had much luck.

I’m making several API calls in sequence to check if my application is already running and then bring it to the front if needed. The whole thing works perfectly, but I wanted to add proper error handling and logging for when things go wrong.

While testing my error handling code, I noticed something weird. I get error codes from Marshal.GetLastWin32Error even when my API calls seem to work fine and return valid results that work with the next API call.

For example, when I call CreateProcessSnapshot to get a snapshot of running processes, it returns a handle that works great with other functions. But right after the call, Marshal.GetLastWin32Error returns error code 6 (Invalid Handle). I know it’s this specific call causing the error because GetLastWin32Error returns 0 right before I make the call.

Same thing happens with ProcessFirst and ProcessNext calls. They return the process data I need, but sometimes they also set error code 6.

Should I be worried about this? Everything is working fine right now. Do I need to care about error codes if my calls are returning valid results that work with subsequent API calls? I’m concerned about deploying something that might be unstable or won’t give me useful error information when things actually break.

Here’s what my logging shows:

string apiName = "CreateProcessSnapshot";
int errorBefore = Marshal.GetLastWin32Error();
IntPtr snapshotHandle = CreateProcessSnapshot(SnapshotFlags.Process, 0);
int errorAfter = Marshal.GetLastWin32Error();
Console.WriteLine($"API: {apiName}, Handle: {snapshotHandle}, Before: {errorBefore}, After: {errorAfter}");

// Output: API: CreateProcessSnapshot, Handle: 701562880, Before: 0, After: 6

bool firstResult = ProcessFirst(snapshotHandle, ref processInfo);
int errorAfterFirst = Marshal.GetLastWin32Error();
Console.WriteLine($"ProcessFirst: {firstResult}, Error: {errorAfterFirst}");

// Output: ProcessFirst: True, Error: 6

bool nextResult = ProcessNext(snapshotHandle, ref processInfo);
int errorAfterNext = Marshal.GetLastWin32Error();
Console.WriteLine($"ProcessNext: {nextResult}, Error: {errorAfterNext}");

// Output: ProcessNext: True, Error: 0

My main questions are: Why do I get error codes when the calls succeed? And when the error code is the same before and after a call, how can I tell if it’s a new error or just the old one still there?

yeah, totally normal for win32 apis. those error codes don’t clear out after calls, so you’re just seeing old errors. only check GetLastWin32Error when the function fails or gives INVALID_HANDLE_VALUE.

You’re overthinking this. Windows CE APIs behave exactly like this - error codes stick around in the thread’s error state until something clears them, which doesn’t happen after every successful call. CreateProcessSnapshot probably has internal operations that fail during snapshot creation, but the function still succeeds and gives you a working handle. I’ve seen this exact thing when doing process enumeration on embedded Windows systems. Here’s the key: GetLastWin32Error only matters right after a function shows it failed through its return value. Your handles work and your booleans return true, so those error codes are just leftover noise from previous operations or internal API stuff. Just focus your error handling on the actual return values when functions succeed.

Yeah, that’s normal Windows CE behavior. Error codes stick around in the thread until the next API call succeeds or fails - that’s why you’re seeing old error codes. CreateProcessSnapshot returning a valid handle but showing error 6 probably means it’s doing some internal stuff that fails even though the main function works fine. I’ve hit this same issue with snapshot APIs on embedded systems. Only check GetLastWin32Error when the function actually tells you it failed. For CreateProcessSnapshot, check if the handle equals INVALID_HANDLE_VALUE first. For ProcessFirst and ProcessNext, only check errors when they return false. Your code’s probably working fine - those error codes are just misleading.