I'm not 100% sure but it could be related to how you might be invoking the EXE in your script. To mimic your situation, I created a simple WPF application that immediately exits via Environment.Exit(3); and then a simple test.cmd script:

@echo off
start /wait TestApp.exe
echo %ERRORLEVEL%

What was echoed was in fact 3.

Update: I noticed that the ERRORLEVEL is not properly set if start is used without the /wait option. I believe this is due to the fact that start will not wait for the application to exit before continuing. Therefore you can use start /wait TestApp.exe or simply call TestApp.exe. I have updated the example above.