杰瑞科技汇

Python如何调用Windows API实现系统交互?

Of course! The "Windows API" (WinAPI) is the core set of functions, messages, structures, and interfaces provided by the Windows operating system. It's the native way for applications to interact with the OS.

Python如何调用Windows API实现系统交互?-图1
(图片来源网络,侵删)

While you can call WinAPI functions directly from Python, it's complex. The most common and powerful way to do it is through a wrapper library. Here's a comprehensive guide covering the main approaches.


The Main Approaches to Using the Windows API in Python

There are three primary methods, ranging from the easiest to the most complex:

  1. pywin32 (The Gold Standard): A mature, stable, and comprehensive set of Python extensions for Windows. It's the recommended choice for most tasks.
  2. ctypes (The Built-in Standard): Python's built-in foreign function library. It's powerful but requires you to manually define every function, structure, and constant. It's great for learning and for when you don't want external dependencies.
  3. windll (A ctypes Shortcut): A slightly more direct way to access WinAPI functions than ctypes, but it has limitations and is less flexible.

pywin32 - The Recommended Method

pywin32 is the most robust and feature-rich way to interact with the Windows API from Python. It provides Python classes and functions that wrap the underlying WinAPI calls.

Installation

pip install pywin32

Key Features of pywin32

  • win32api: Provides direct access to many core WinAPI functions.
  • win32gui: For graphical user interface (GUI) manipulation.
  • win32con: A huge collection of Windows constants (e.g., SW_SHOWNORMAL, MB_OK).
  • win32com: For interacting with COM objects (e.g., controlling Microsoft Office, Outlook).
  • win32service: For creating and managing Windows services.

Example: Getting System Information with win32api

This example uses win32api.GetVersionEx to get detailed OS version information.

Python如何调用Windows API实现系统交互?-图2
(图片来源网络,侵删)
import win32api
import win32con
def get_os_info():
    """Retrieves detailed OS information using the WinAPI."""
    try:
        # GetVersionEx is the modern function. The version info structure is passed as a dictionary.
        os_version = win32api.GetVersionEx()
        print("--- OS Information ---")
        print(f"OS Version: {os_version['dwMajorVersion']}.{os_version['dwMinorVersion']}")
        print(f"Build Number: {os_version['dwBuildNumber']}")
        print(f"Platform ID: {os_version['dwPlatformId']}")
        print(f"Service Pack: '{os_version['szCSDVersion']}'")
        print(f"Suite Mask: {hex(os_version['wSuiteMask'])}")
        print(f"Product Type: {os_version['wProductType']}")
    except Exception as e:
        print(f"An error occurred: {e}")
if __name__ == "__main__":
    get_os_info()

Example: Creating a Simple GUI Window with win32gui

This example creates a basic window that responds to the WM_CLOSE message.

import win32gui
import win32con
# The window procedure callback function
def window_proc(hwnd, msg, wparam, lparam):
    """
    This function handles messages sent to the window.
    """
    if msg == win32con.WM_CLOSE:
        # When the user clicks the 'X', destroy the window and exit the message loop.
        win32gui.DestroyWindow(hwnd)
        return 0
    elif msg == win32con.WM_DESTROY:
        # This message is sent after the window is destroyed.
        win32gui.PostQuitMessage(0) # Signal the end of the message loop
        return 0
    # For all other messages, use the default window procedure.
    return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
def main():
    # Register the window class
    wc = win32gui.WNDCLASS()
    wc.hInstance = win32api.GetModuleHandle(None)
    wc.lpszClassName = "MyPythonWindow"
    wc.lpfnWndProc = window_proc # Assign our callback function
    wc.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW
    wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
    # Register the class
    class_atom = win32gui.RegisterClass(wc)
    # Create the window
    hwnd = win32gui.CreateWindow(
        class_atom,          # Window class name
        "Hello, WinAPI!",    # Window title
        win32con.WS_OVERLAPPEDWINDOW, # Window style
        100, 100,            # x, y position
        400, 300,            # width, height
        0,                   # Parent window handle
        0,                   # Menu handle
        wc.hInstance,        # Application instance handle
        None                 # Window creation data
    )
    # Show and update the window
    win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
    win32gui.UpdateWindow(hwnd)
    # The Message Loop
    msg = win32gui.PeekMessage(hwnd, 0, 0, win32con.PM_REMOVE)
    while msg != 0:
        win32gui.TranslateMessage(msg)
        win32gui.DispatchMessage(msg)
        msg = win32gui.PeekMessage(hwnd, 0, 0, win32con.PM_REMOVE)
if __name__ == "__main__":
    main()

ctypes - The Built-in Power Tool

ctypes is part of the Python standard library. It allows you to call functions in shared libraries (like user32.dll or kernel32.dll). You have to define everything manually, which is tedious but gives you ultimate control.

Example: Getting the Current User Name

We'll call the GetUserNameW function from advapi32.dll.

import ctypes
from ctypes import wintypes
# Define the function signature
# GetUserNameW(LPWSTR, LPDWORD) -> BOOL
# LPWSTR is a pointer to a wide character string (unicode)
# LPDWORD is a pointer to a double word (a 32-bit unsigned integer)
GetUserNameW = ctypes.windll.advapi32.GetUserNameW
GetUserNameW.argtypes = [wintypes.LPWSTR, wintypes.LPDWORD]
GetUserNameW.restype = wintypes.BOOL # The function returns a boolean (TRUE/FALSE)
def get_current_user():
    """Gets the current Windows username using ctypes."""
    # Create a buffer to hold the username.
    # We'll allocate a buffer of 256 characters, which is more than enough.
    buffer_size = wintypes.DWORD(256)
    buffer = ctypes.create_unicode_buffer(buffer_size.value)
    # Call the function
    if GetUserNameW(buffer, ctypes.byref(buffer_size)):
        return buffer.value
    else:
        # Get the last error code if the function failed
        error_code = ctypes.windll.kernel32.GetLastError()
        print(f"Error getting username. Error code: {error_code}")
        return None
if __name__ == "__main__":
    username = get_current_user()
    if username:
        print(f"Current user is: {username}")

windll - The Quick but Limited ctypes

ctypes.windll is a convenience module for calling functions from *.dll files. It automatically sets the restype to wintypes.HANDLE or wintypes.BOOL and uses wintypes.c_int for arguments. This makes it quicker for simple functions but can be error-prone for more complex ones.

Python如何调用Windows API实现系统交互?-图3
(图片来源网络,侵删)

Example: Getting the System Drive Letters

This example calls GetLogicalDriveStringsW from kernel32.dll.

import ctypes
# GetLogicalDriveStringsW returns a buffer with all drive letters separated by nulls.
# It's a bit tricky to parse.
GetLogicalDriveStringsW = ctypes.windll.kernel32.GetLogicalDriveStringsW
GetLogicalDriveStringsW.argtypes = [wintypes.DWORD, wintypes.LPWSTR]
GetLogicalDriveStringsW.restype = wintypes.DWORD
def get_drive_letters():
    """Gets a list of all available drive letters."""
    # The buffer needs to be large enough to hold all drive strings.
    # The format is "A:\0B:\0C:\0\0"
    buffer_size = 256
    buffer = ctypes.create_unicode_buffer(buffer_size)
    # Call the function
    if GetLogicalDriveStringsW(buffer_size, buffer):
        # The function returns the size of the buffer used.
        # The buffer contains a list of null-terminated strings, ending with a double null.
        drives = []
        current_pos = 0
        while True:
            # Find the next null terminator
            null_pos = buffer[current_pos:].find('\0')
            if null_pos == -1 or null_pos == 0:
                break
            # Extract the drive letter and add it to our list
            drive = buffer[current_pos:current_pos + null_pos]
            drives.append(drive)
            current_pos += null_pos + 1
        return drives
    else:
        print("Failed to get drive letters.")
        return None
if __name__ == "__main__":
    drives = get_drive_letters()
    if drives:
        print("Available drives:")
        for drive in drives:
            print(f"- {drive}")

Comparison and Recommendation

Feature pywin32 ctypes windll
Ease of Use Excellent Fair Good (for simple calls)
Documentation Excellent Good (requires MSDN lookup) Poor (assumes types)
Dependencies pip install pywin32 None (built-in) None (built-in)
Coverage Excellent Excellent (you can call anything) Limited (only *.dll)
Type Safety High (Python objects) Low (manual definition) Low (automatic assumptions)
Best For Most general-purpose tasks, GUI, COM, services. Learning the API, calling functions not in pywin32. Quick, simple function calls where you know the types.

Recommendation

  • For almost all projects, start with pywin32. It's the most Pythonic, safest, and most productive way to use the Windows API. The time saved by not having to manually define every function and constant is immense.
  • Use ctypes when you need to call a very specific, obscure WinAPI function that isn't wrapped by pywin32, or if you want to understand the low-level mechanics without any external dependencies.
  • Use windll for very quick, one-off scripts where you are confident in the function's signature and return type. Be cautious, as its automatic type guessing can lead to subtle bugs.
分享:
扫描分享到社交APP
上一篇
下一篇