1 |
ret = ctypes.windll.kernel32.CreateProcessA(path, None, None, None, True, 0x00000010, None, None, byref(startup_info), byref(process_info)) |
Above simple code didn't work in the latest version of my program, but it works well in previous version.
After reading documentation from MSDN, I got that CreateProcessA will return TRUE(1) if execution succeed, and return FALSE(0) if failed. Then I put "print ret" right after that line of code, exactly the return value is 0 (FALSE).
But how to get more details about the error? We can use GetLastError() function.
1 |
err_code = ctypes.windll.kernel32.GetLastError() |
And after calling GetLastError(), it returns 2.
By looking up this error code page at MSDN, we can get the return value is actually ERROR_FILE_NOT_FOUND, meaning the system cannot find the file specified. But after printing the file path to console and checking the file path twice, I found the file existed actually.
Searching on google for some time, I found this post from stackoverflow
This guy met same problem: the executable file exists but calling CreateProcessA failed. The answer is use CreateProcessA with ANSI string, and use CreateProcessW with Unicode string. Then I tried CreateProcessW instead of CreateProcessA and it works perfectly!
Actually in previous version of the application, the executable path is read only from configuration file. But in the latest version, it can be loaded from registry key value, using _winreg.QueryValueEx. And this bug is introduced as well. So, is registry key value Unicode? or QueryValueEx returns an Unicode string?
Reading this documentation, we know that REG_SZ key value can either be ANSI or Unicode, it's depending on what function we use. And this bug post tells us that QueryValueEx will convert REG_SZ to unicode automatically, so finally we found where problem is.