1

The following code is supposed to start a new process calc.exe in debug mode. However, it fails with the code 2 or ERROR_FILE_NOT_FOUND. However, this file calc.exe does exist on the system. What could be wrong with this code? Is there an issue with the path that can be improved so this works?

from ctypes import *
kernel32 = windll.kernel32

WORD      = c_ushort
DWORD     = c_ulong
LPBYTE    = POINTER(c_ubyte)
LPTSTR    = POINTER(c_char)
HANDLE    = c_void_p

class STARTUPINFO(Structure):
    _fields_ = [
        ("cb",            DWORD),
        ("lpReserved",    LPTSTR),
        ("lpDesktop",     LPTSTR),
        ("lpTitle",       LPTSTR),
        ("dwX",           DWORD),
        ("dwY",           DWORD),
        ("dwXSize",       DWORD),
        ("dwYSize",       DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute",DWORD),
        ("dwFlags",       DWORD),
        ("wShowWindow",   WORD),
        ("cbReserved2",   WORD),
        ("lpReserved2",   LPBYTE),
        ("hStdInput",     HANDLE),
        ("hStdOutput",    HANDLE),
        ("hStdError",     HANDLE),
    ]

class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess",    HANDLE),
        ("hThread",     HANDLE),
        ("dwProcessId", DWORD),
        ("dwThreadId",  DWORD),
    ]

DEBUG_PROCESS = 0x00000001
creation_flags = DEBUG_PROCESS

startupinfo         = STARTUPINFO()
startupinfo.dwFlags     = 0x1
startupinfo.wShowWindow = 0x0
startupinfo.cb = sizeof(startupinfo)

process_information = PROCESS_INFORMATION()

result = kernel32.CreateProcessA("C:\\Windows\\System32\\calc.exe",
                        None,
                        None,
                        None,
                        None,
                        creation_flags,
                        None,
                        None,
                        byref(startupinfo),
                        byref(process_information)
)

print(result)
print(kernel32.GetLastError())
2
  • 1
    Did you post incomplete code, or are you not specifying argtypes for CreateProcessA? Commented Jun 28, 2024 at 4:53
  • not deliberately incomplete code, but thanks for the info! Commented Jun 28, 2024 at 22:44

1 Answer 1

2

There are a number of things that are wrong:

code00.py:

#!/usr/bin/env python

import ctypes as cts
import sys
from ctypes import wintypes as wts


class STARTUPINFOA(cts.Structure):
    _fields_ = (
        ("cb", wts.DWORD),
        ("lpReserved", wts.LPSTR),
        ("lpDesktop", wts.LPSTR),
        ("lpTitle", wts.LPSTR),
        ("dwX", wts.DWORD),
        ("dwY", wts.DWORD),
        ("dwXSize", wts.DWORD),
        ("dwYSize", wts.DWORD),
        ("dwXCountChars", wts.DWORD),
        ("dwYCountChars", wts.DWORD),
        ("dwFillAttribute", wts.DWORD),
        ("dwFlags", wts.DWORD),
        ("wShowWindow", wts.WORD),
        ("cbReserved2", wts.WORD),
        ("lpReserved2", wts.LPBYTE),
        ("hStdInput", wts.HANDLE),
        ("hStdOutput", wts.HANDLE),
        ("hStdError", wts.HANDLE),
    )
LPSTARTUPINFOA = cts.POINTER(STARTUPINFOA)

class PROCESS_INFORMATION(cts.Structure):
    _fields_ = (
        ("hProcess", wts.HANDLE),
        ("hThread", wts.HANDLE),
        ("dwProcessId", wts.DWORD),
        ("dwThreadId", wts.DWORD),
    )
LPPROCESS_INFORMATION = cts.POINTER(PROCESS_INFORMATION)

LPSECURITY_ATTRIBUTES = cts.c_void_p

DEBUG_PROCESS = 0x00000001


def main(*argv):
    kernel32 = cts.windll.kernel32

    GetLastError = kernel32.GetLastError
    GetLastError.argtypes = ()
    GetLastError.restype = wts.DWORD

    CreateProcessA = kernel32.CreateProcessA
    CreateProcessA.argtypes = (
        wts.LPCSTR, wts.LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES,
        wts.BOOL, wts.DWORD, wts.LPVOID, wts.LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION
    )
    CreateProcessA.restype = wts.BOOL


    creation_flags = DEBUG_PROCESS
    creation_flags = 0  # Not automatically killed when Python terminates

    startupinfo = STARTUPINFOA()
    startupinfo.cb = cts.sizeof(STARTUPINFOA)
    startupinfo.dwFlags = 0x1
    startupinfo.wShowWindow = 0x0

    process_information = PROCESS_INFORMATION()

    result = kernel32.CreateProcessA(
        b"C:\\Windows\\System32\\calc.exe",
        None,
        None,
        None,
        False,
        creation_flags,
        None,
        None,
        cts.byref(startupinfo),
        cts.byref(process_information)
    )

    print(f"CreateProcessA returned {result}")
    if not result:
        print(f"  Error: {GetLastError()}")
    else:
        print(f"  Created process (PId: {process_information.dwProcessId},"
              f" TId: {process_information.dwThreadId})")


if __name__ == "__main__":
    print(
        "Python {:s} {:03d}bit on {:s}\n".format(
            " ".join(elem.strip() for elem in sys.version.split("\n")),
            64 if sys.maxsize > 0x100000000 else 32,
            sys.platform,
        )
    )
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

Output:

(py_pc064_03.10_test0) [cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078680399]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> python ./code00.py
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32

CreateProcessA returned 1
  Created process (PId: 32444, TId: 22832)

Done.


[prompt]> :: Calculator window is pops up

As a CTypes alternative, you could try [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions which is a Python wrapper over WinAPIs (and contains lots of boilerplate code that Python programmers would not have to write). Documentation (WiP) can be found at [GitHub.MHammond]: Python for Win32 Extensions Help (or [ME.TimGolden]: Python for Win32 Extensions Help).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.