Comment Interagir avec l'API Windows en Python

PythonBeginner
Pratiquer maintenant

Introduction

Ce tutoriel complet vous guidera à travers l'utilisation de la bibliothèque ctypes de Python pour interagir avec l'API Windows. Vous apprendrez comment accéder aux fonctionnalités du système Windows, gérer les processus et exploiter les fonctionnalités spécifiques à Windows directement depuis Python. À la fin de ce lab, vous comprendrez comment construire des applications qui peuvent s'intégrer de manière transparente avec le système d'exploitation Windows et effectuer des opérations puissantes au niveau du système.

Comprendre la bibliothèque ctypes de Python

Dans cette étape, nous allons explorer les bases de la bibliothèque ctypes de Python, qui sert de fondation pour interagir avec l'API Windows. La bibliothèque ctypes agit comme un pont entre Python et les bibliothèques C natives, nous permettant d'appeler des fonctions directement à partir de DLL (Dynamic Link Libraries).

Qu'est-ce que ctypes ?

ctypes est une bibliothèque de fonctions étrangères pour Python qui fournit des types de données compatibles C et permet d'appeler des fonctions dans des DLL ou des bibliothèques partagées. Elle est particulièrement utile pour :

  • Accéder aux fonctions système de bas niveau
  • Interagir avec le matériel
  • Appeler des fonctions à partir de bibliothèques spécifiques à la plateforme

Installation des packages requis

Avant de commencer à écrire du code, installons les packages nécessaires. Ouvrez un terminal dans le WebIDE et exécutez :

pip install pywin32-ctypes

Cela installera une bibliothèque compatible qui fonctionne avec notre environnement.

Utilisation de base de ctypes

Créons un script Python simple pour comprendre comment ctypes fonctionne. Dans le WebIDE, créez un nouveau fichier nommé ctypes_basics.py dans le répertoire /home/labex/project avec le contenu suivant :

import ctypes

## Load a standard C library
libc = ctypes.CDLL('libc.so.6')

## Call a simple C function
print("Random number from C library:", libc.rand())

## Find out the size of int type on this machine
print("Size of int:", ctypes.sizeof(ctypes.c_int), "bytes")

## Create and use a C-compatible string
message = ctypes.create_string_buffer(b"Hello from ctypes!")
print("C-compatible string:", message.value.decode())
print("String buffer size:", ctypes.sizeof(message), "bytes")

Exécutez le script en utilisant :

python3 ctypes_basics.py

Vous devriez voir une sortie similaire à celle-ci :

Random number from C library: 1804289383
Size of int: 4 bytes
C-compatible string: Hello from ctypes!
String buffer size: 19 bytes

Comprendre les types de données C en Python

ctypes fournit des wrappers compatibles Python pour les types de données C. Voici un tableau de référence des types de données C courants et leurs équivalents ctypes :

Type C Type ctypes Type Python
char c_char Objet bytes de 1 caractère
int c_int int
unsigned int c_uint int
long c_long int
void * c_void_p int ou None
char * c_char_p bytes ou None
wchar_t * c_wchar_p str ou None

Explorons ces types de données avec un autre exemple. Créez un fichier nommé ctypes_types.py :

import ctypes

## Integer types
i = ctypes.c_int(42)
ui = ctypes.c_uint(123)
print(f"Integer: {i.value}, Unsigned Integer: {ui.value}")

## Floating point types
f = ctypes.c_float(3.14)
d = ctypes.c_double(2.71828)
print(f"Float: {f.value}, Double: {d.value}")

## Character and string types
c = ctypes.c_char(b'A')
s = ctypes.c_char_p(b"Hello, World!")
print(f"Character: {c.value.decode()}, String: {s.value.decode()}")

## Create an array of integers
int_array = (ctypes.c_int * 5)(1, 2, 3, 4, 5)
print("Array elements:", [int_array[i] for i in range(5)])

Exécutez le script :

python3 ctypes_types.py

Sortie attendue :

Integer: 42, Unsigned Integer: 123
Float: 3.140000104904175, Double: 2.71828
Character: A, String: Hello, World!
Array elements: [1, 2, 3, 4, 5]

Cette compréhension fondamentale de ctypes nous aidera à interagir avec des bibliothèques système plus complexes et, plus spécifiquement, avec l'API Windows.

Accéder aux bibliothèques système avec ctypes

Dans cette étape, nous allons apprendre à accéder aux bibliothèques système et à appeler leurs fonctions en utilisant ctypes. Étant donné que nous travaillons dans un environnement Linux, nous nous concentrerons sur les bibliothèques système Linux tout en expliquant les principes qui s'appliquent également à l'accès à l'API Windows.

Chargement des bibliothèques dynamiques

En Python, nous pouvons charger des bibliothèques dynamiques en utilisant plusieurs méthodes fournies par ctypes :

  • CDLL - pour charger les bibliothèques C standard
  • WinDLL - pour charger les DLL Windows (lorsqu'on est sous Windows)
  • OleDLL - pour charger les bibliothèques COM (lorsqu'on est sous Windows)

Créons un fichier nommé system_info.py pour explorer les informations système en utilisant les bibliothèques standard :

import ctypes
import os
import platform

print(f"Python Platform: {platform.platform()}")
print(f"System: {platform.system()}")
print(f"Machine: {platform.machine()}")
print(f"Processor: {platform.processor()}")

## Load the C standard library
libc = ctypes.CDLL('libc.so.6')

## Get system information
print("\n--- System Information ---")

## Get hostname
hostname_buffer = ctypes.create_string_buffer(1024)
if libc.gethostname(hostname_buffer, ctypes.sizeof(hostname_buffer)) == 0:
    print(f"Hostname: {hostname_buffer.value.decode()}")
else:
    print("Failed to get hostname")

## Get user information
uid = os.getuid()
pwd = libc.getpwuid(uid)
if pwd:
    print(f"Current user ID: {uid}")
else:
    print(f"Current user ID: {uid} (failed to get user info)")

## Memory page size
page_size = libc.getpagesize()
print(f"Memory page size: {page_size} bytes")

## Get system uptime (if available)
try:
    class timespec(ctypes.Structure):
        _fields_ = [
            ('tv_sec', ctypes.c_long),
            ('tv_nsec', ctypes.c_long)
        ]

    time_buf = timespec()
    CLOCK_BOOTTIME = 7  ## Linux specific
    if hasattr(libc, 'clock_gettime') and libc.clock_gettime(CLOCK_BOOTTIME, ctypes.byref(time_buf)) == 0:
        uptime_seconds = time_buf.tv_sec
        days, remainder = divmod(uptime_seconds, 86400)
        hours, remainder = divmod(remainder, 3600)
        minutes, seconds = divmod(remainder, 60)
        print(f"System uptime: {days} days, {hours} hours, {minutes} minutes, {seconds} seconds")
    else:
        print("Could not get system uptime")
except Exception as e:
    print(f"Error getting uptime: {e}")

Exécutez le script :

python3 system_info.py

Vous devriez voir des informations détaillées sur votre système, similaires à ceci :

Python Platform: Linux-5.15.0-1031-aws-x86_64-with-glibc2.35
System: Linux
Machine: x86_64
Processor: x86_64

--- System Information ---
Hostname: labex-container
Current user ID: 1000
Memory page size: 4096 bytes
System uptime: 0 days, 1 hours, 23 minutes, 45 seconds

Création d'un moniteur de processus

Maintenant, créons une application plus pratique : un simple moniteur de processus qui listera les processus en cours d'exécution. Créez un fichier nommé process_monitor.py :

import ctypes
import os
import time
from datetime import datetime

def list_processes():
    """List all running processes using /proc filesystem"""
    processes = []

    ## On Linux, process information is available in the /proc filesystem
    for pid in os.listdir('/proc'):
        if pid.isdigit():
            try:
                ## Read process name from /proc/[pid]/comm
                with open(f'/proc/{pid}/comm', 'r') as f:
                    name = f.read().strip()

                ## Get process status
                with open(f'/proc/{pid}/status', 'r') as f:
                    status_lines = f.readlines()
                    status = {}
                    for line in status_lines:
                        if ':' in line:
                            key, value = line.split(':', 1)
                            status[key.strip()] = value.strip()

                ## Get memory usage (VmRSS is physical memory used)
                memory_kb = int(status.get('VmRSS', '0 kB').split()[0]) if 'VmRSS' in status else 0

                processes.append({
                    'pid': int(pid),
                    'name': name,
                    'state': status.get('State', 'Unknown'),
                    'memory_kb': memory_kb
                })
            except (IOError, FileNotFoundError, ProcessLookupError):
                ## Process might have terminated while we were reading
                continue

    return processes

def display_processes(processes, top_n=10):
    """Display processes sorted by memory usage"""
    ## Sort processes by memory usage (highest first)
    sorted_processes = sorted(processes, key=lambda p: p['memory_kb'], reverse=True)

    ## Display only top N processes
    print(f"\nTop {top_n} processes by memory usage at {datetime.now().strftime('%H:%M:%S')}:")
    print(f"{'PID':<7} {'NAME':<20} {'STATE':<10} {'MEMORY (KB)':<12}")
    print("-" * 50)

    for proc in sorted_processes[:top_n]:
        print(f"{proc['pid']:<7} {proc['name'][:19]:<20} {proc['state']:<10} {proc['memory_kb']:<12}")

## Monitor processes continuously
print("Simple Process Monitor")
print("Press Ctrl+C to exit")

try:
    while True:
        processes = list_processes()
        display_processes(processes)
        time.sleep(3)  ## Update every 3 seconds
except KeyboardInterrupt:
    print("\nProcess monitoring stopped")

Exécutez le moniteur de processus :

python3 process_monitor.py

Vous devriez voir une sortie similaire à ceci, qui se rafraîchira toutes les 3 secondes :

Simple Process Monitor
Press Ctrl+C to exit

Top 10 processes by memory usage at 14:30:25:
PID     NAME                 STATE      MEMORY (KB)
-------------------------------------------------------
1234    python3              S (sleeping) 56789
2345    node                 S (sleeping) 34567
3456    code                 S (sleeping) 23456
...

Laissez le moniteur de processus s'exécuter pendant environ 10 secondes, puis appuyez sur Ctrl+C pour l'arrêter.

Dans ces exemples, nous avons appris à :

  1. Charger et utiliser les bibliothèques système avec ctypes
  2. Accéder aux informations système
  3. Créer un outil pratique de surveillance des processus

Ces principes sont les mêmes que ceux que vous utiliseriez pour interagir avec l'API Windows via ctypes, juste avec des noms de bibliothèques et des appels de fonctions différents.

Création et utilisation de structures C avec ctypes

Dans cette étape, nous allons apprendre à définir et à utiliser des structures C en Python. Les structures sont essentielles lorsque l'on travaille avec les API système, car elles sont couramment utilisées pour échanger des données entre Python et les bibliothèques système.

Comprendre les structures C en Python

Les structures C peuvent être représentées en Python en utilisant la classe ctypes.Structure. Cela nous permet de créer des structures de données complexes qui correspondent à la disposition de la mémoire attendue par les fonctions C.

Créons un fichier nommé struct_example.py pour explorer comment travailler avec les structures :

import ctypes

## Define a simple C structure
class Point(ctypes.Structure):
    _fields_ = [
        ("x", ctypes.c_int),
        ("y", ctypes.c_int)
    ]

## Create an instance of the Point structure
p = Point(10, 20)
print(f"Point coordinates: ({p.x}, {p.y})")

## Modify structure fields
p.x = 100
p.y = 200
print(f"Updated coordinates: ({p.x}, {p.y})")

## Create a Point from a dictionary
values = {"x": 30, "y": 40}
p2 = Point(**values)
print(f"Point from dictionary: ({p2.x}, {p2.y})")

## Get the raw memory view (pointer) of the structure
p_ptr = ctypes.pointer(p)
print(f"Memory address of p: {ctypes.addressof(p)}")
print(f"Access via pointer: ({p_ptr.contents.x}, {p_ptr.contents.y})")

## Define a nested structure
class Rectangle(ctypes.Structure):
    _fields_ = [
        ("top_left", Point),
        ("bottom_right", Point),
        ("color", ctypes.c_int)
    ]

## Create a rectangle with two points
rect = Rectangle(Point(0, 0), Point(100, 100), 0xFF0000)
print(f"Rectangle: Top-Left: ({rect.top_left.x}, {rect.top_left.y}), "
      f"Bottom-Right: ({rect.bottom_right.x}, {rect.bottom_right.y}), "
      f"Color: {hex(rect.color)}")

## Calculate the area (demonstrating structure manipulation)
width = rect.bottom_right.x - rect.top_left.x
height = rect.bottom_right.y - rect.top_left.y
print(f"Rectangle area: {width * height}")

Exécutez le script :

python3 struct_example.py

Vous devriez voir une sortie similaire à ceci :

Point coordinates: (10, 20)
Updated coordinates: (100, 200)
Point from dictionary: (30, 40)
Memory address of p: 140737345462208
Access via pointer: (100, 200)
Rectangle: Top-Left: (0, 0), Bottom-Right: (100, 100), Color: 0xff0000
Rectangle area: 10000

Création d'un utilitaire de temps système

Maintenant, créons une application pratique qui utilise les structures C pour interagir avec les fonctions de temps système. Créez un fichier nommé time_utility.py :

import ctypes
import time
from datetime import datetime

## Define the timespec structure (used in Linux for high-resolution time)
class timespec(ctypes.Structure):
    _fields_ = [
        ("tv_sec", ctypes.c_long),
        ("tv_nsec", ctypes.c_long)
    ]

## Define the tm structure (used for calendar time representation)
class tm(ctypes.Structure):
    _fields_ = [
        ("tm_sec", ctypes.c_int),     ## seconds (0 - 60)
        ("tm_min", ctypes.c_int),     ## minutes (0 - 59)
        ("tm_hour", ctypes.c_int),    ## hours (0 - 23)
        ("tm_mday", ctypes.c_int),    ## day of month (1 - 31)
        ("tm_mon", ctypes.c_int),     ## month of year (0 - 11)
        ("tm_year", ctypes.c_int),    ## year - 1900
        ("tm_wday", ctypes.c_int),    ## day of week (0 - 6, Sunday = 0)
        ("tm_yday", ctypes.c_int),    ## day of year (0 - 365)
        ("tm_isdst", ctypes.c_int)    ## is daylight saving time in effect
    ]

## Load the C library
libc = ctypes.CDLL("libc.so.6")

def get_system_time():
    """Get the current system time using C functions"""
    ## Get current time as seconds since epoch
    time_t_ptr = ctypes.pointer(ctypes.c_long())
    libc.time(time_t_ptr)  ## time() function gets current time

    ## Convert to printable time string
    time_val = time_t_ptr.contents.value
    time_str = ctypes.string_at(libc.ctime(time_t_ptr))

    return {
        "timestamp": time_val,
        "formatted_time": time_str.decode().strip()
    }

def get_high_resolution_time():
    """Get high resolution time using clock_gettime"""
    ts = timespec()

    ## CLOCK_REALTIME is usually 0
    CLOCK_REALTIME = 0

    ## Call clock_gettime to fill the timespec structure
    if libc.clock_gettime(CLOCK_REALTIME, ctypes.byref(ts)) != 0:
        raise OSError("Failed to get time")

    return {
        "seconds": ts.tv_sec,
        "nanoseconds": ts.tv_nsec,
        "precise_time": ts.tv_sec + (ts.tv_nsec / 1_000_000_000)
    }

def time_breakdown():
    """Break down the current time into its components using localtime"""
    ## Get current time
    time_t_ptr = ctypes.pointer(ctypes.c_long())
    libc.time(time_t_ptr)

    ## Get local time
    tm_ptr = libc.localtime(time_t_ptr)
    tm_struct = ctypes.cast(tm_ptr, ctypes.POINTER(tm)).contents

    ## Return time components
    return {
        "year": 1900 + tm_struct.tm_year,
        "month": 1 + tm_struct.tm_mon,  ## tm_mon is 0-11, we adjust to 1-12
        "day": tm_struct.tm_mday,
        "hour": tm_struct.tm_hour,
        "minute": tm_struct.tm_min,
        "second": tm_struct.tm_sec,
        "weekday": tm_struct.tm_wday,  ## 0 is Sunday
        "yearday": tm_struct.tm_yday + 1  ## tm_yday is 0-365, we adjust to 1-366
    }

## Main program
print("Time Utility using C Structures")
print("-" * 40)

## Get and display system time
sys_time = get_system_time()
print(f"System time: {sys_time['formatted_time']}")
print(f"Timestamp (seconds since epoch): {sys_time['timestamp']}")

## Get and display high-resolution time
hi_res = get_high_resolution_time()
print(f"\nHigh resolution time:")
print(f"Seconds: {hi_res['seconds']}")
print(f"Nanoseconds: {hi_res['nanoseconds']}")
print(f"Precise time: {hi_res['precise_time']}")

## Get and display time breakdown
components = time_breakdown()
print(f"\nTime breakdown:")
print(f"Date: {components['year']}-{components['month']:02d}-{components['day']:02d}")
print(f"Time: {components['hour']:02d}:{components['minute']:02d}:{components['second']:02d}")
print(f"Day of week: {components['weekday']} (0=Sunday)")
print(f"Day of year: {components['yearday']}")

## Compare with Python's datetime
now = datetime.now()
print(f"\nPython datetime: {now}")
print(f"Python timestamp: {time.time()}")

Exécutez l'utilitaire de temps :

python3 time_utility.py

Vous devriez voir une sortie similaire à ceci :

Time Utility using C Structures
----------------------------------------
System time: Wed Jun 14 15:22:36 2023
Timestamp (seconds since epoch): 1686756156

High resolution time:
Seconds: 1686756156
Nanoseconds: 923456789
Precise time: 1686756156.923457

Time breakdown:
Date: 2023-06-14
Time: 15:22:36
Day of week: 3 (0=Sunday)
Day of year: 165

Python datetime: 2023-06-14 15:22:36.923499
Python timestamp: 1686756156.9234989

Comprendre la gestion de la mémoire avec ctypes

Lorsque vous travaillez avec ctypes et les structures C, il est important de comprendre la gestion de la mémoire. Créons un autre exemple, memory_management.py, pour le démontrer :

import ctypes
import gc  ## Garbage collector module

## Define a simple structure
class MyStruct(ctypes.Structure):
    _fields_ = [
        ("id", ctypes.c_int),
        ("value", ctypes.c_double),
        ("name", ctypes.c_char * 32)  ## Fixed-size character array
    ]

def demonstrate_memory_management():
    print("Memory Management with ctypes")
    print("-" * 40)

    ## Create a structure instance
    my_data = MyStruct(
        id=1,
        value=3.14159,
        name=b"Example"
    )

    print(f"Structure size: {ctypes.sizeof(my_data)} bytes")
    print(f"Memory address: {hex(ctypes.addressof(my_data))}")

    ## Create a pointer to the structure
    data_ptr = ctypes.pointer(my_data)
    print(f"Pointer value: {hex(ctypes.cast(data_ptr, ctypes.c_void_p).value)}")

    ## Access through pointer
    print(f"Access via pointer: id={data_ptr.contents.id}, value={data_ptr.contents.value}")

    ## Allocate memory for a new structure
    new_struct_ptr = ctypes.POINTER(MyStruct)()
    new_struct_ptr = ctypes.cast(
        ctypes.create_string_buffer(ctypes.sizeof(MyStruct)),
        ctypes.POINTER(MyStruct)
    )

    ## Initialize the allocated memory
    new_struct = new_struct_ptr.contents
    new_struct.id = 2
    new_struct.value = 2.71828
    new_struct.name = b"Allocated"

    print(f"\nAllocated structure memory address: {hex(ctypes.addressof(new_struct))}")
    print(f"Allocated structure content: id={new_struct.id}, value={new_struct.value}, name={new_struct.name.decode()}")

    ## Create an array of structures
    StructArray = MyStruct * 3
    struct_array = StructArray(
        MyStruct(10, 1.1, b"First"),
        MyStruct(20, 2.2, b"Second"),
        MyStruct(30, 3.3, b"Third")
    )

    print("\nArray of structures:")
    for i, item in enumerate(struct_array):
        print(f"  [{i}] id={item.id}, value={item.value}, name={item.name.decode()}")

    ## Force garbage collection
    print("\nForcing garbage collection...")
    gc.collect()

    ## Memory is automatically managed by Python

## Run the demonstration
demonstrate_memory_management()

Exécutez le script de gestion de la mémoire :

python3 memory_management.py

Vous devriez voir une sortie similaire à ceci :

Memory Management with ctypes
----------------------------------------
Structure size: 48 bytes
Memory address: 0x7f3c2e32b040
Pointer value: 0x7f3c2e32b040
Access via pointer: id=1, value=3.14159

Allocated structure memory address: 0x7f3c2e32b0a0
Allocated structure content: id=2, value=2.71828, name=Allocated

Array of structures:
  [0] id=10, value=1.1, name=First
  [1] id=20, value=2.2, name=Second
  [2] id=30, value=3.3, name=Third

Forcing garbage collection...

Dans ces exemples, nous avons appris à :

  1. Définir et utiliser des structures C en Python avec ctypes
  2. Créer des pointeurs vers des structures et accéder à leur contenu
  3. Créer des structures imbriquées et des tableaux de structures
  4. Construire des applications pratiques en utilisant les fonctions de temps système
  5. Gérer la mémoire lorsque l'on travaille avec ctypes

Ces compétences sont essentielles lorsque l'on travaille avec les fonctions de l'API Windows, qui nécessitent fréquemment des structures de données complexes pour le transfert d'informations.

Création d'une application complète de surveillance du système

Dans cette dernière étape, nous allons combiner toutes nos connaissances pour créer une application complète de surveillance du système. Cette application utilisera ctypes pour collecter des informations système, afficher des métriques en temps réel et démontrer comment structurer une application Python plus grande qui interagit avec les bibliothèques système.

Création du moniteur système

Créez un nouveau fichier nommé system_monitor.py avec le contenu suivant :

import ctypes
import os
import time
import platform
from datetime import datetime

class SystemMonitor:
    """System monitoring application using ctypes"""

    def __init__(self):
        """Initialize the system monitor"""
        self.libc = ctypes.CDLL("libc.so.6")
        self.running = False

        ## Define a timespec structure for time-related functions
        class timespec(ctypes.Structure):
            _fields_ = [
                ("tv_sec", ctypes.c_long),
                ("tv_nsec", ctypes.c_long)
            ]
        self.timespec = timespec

        ## Print basic system information
        print(f"System Monitor for {platform.system()} {platform.release()}")
        print(f"Python Version: {platform.python_version()}")
        print(f"Machine: {platform.machine()}")
        print("-" * 50)

    def get_uptime(self):
        """Get system uptime information"""
        try:
            CLOCK_BOOTTIME = 7  ## Linux specific
            time_buf = self.timespec()

            if hasattr(self.libc, 'clock_gettime') and self.libc.clock_gettime(CLOCK_BOOTTIME, ctypes.byref(time_buf)) == 0:
                uptime_seconds = time_buf.tv_sec
                days, remainder = divmod(uptime_seconds, 86400)
                hours, remainder = divmod(remainder, 3600)
                minutes, seconds = divmod(remainder, 60)
                return {
                    "total_seconds": uptime_seconds,
                    "days": days,
                    "hours": hours,
                    "minutes": minutes,
                    "seconds": seconds
                }
            return None
        except Exception as e:
            print(f"Error getting uptime: {e}")
            return None

    def get_memory_info(self):
        """Get memory usage information using /proc/meminfo"""
        memory_info = {}
        try:
            with open('/proc/meminfo', 'r') as f:
                for line in f:
                    if ':' in line:
                        key, value = line.split(':', 1)
                        ## Remove 'kB' and convert to integer
                        value = value.strip()
                        if 'kB' in value:
                            value = int(value.split()[0]) * 1024  ## Convert to bytes
                        memory_info[key.strip()] = value

            ## Calculate memory usage percentage
            if 'MemTotal' in memory_info and 'MemAvailable' in memory_info:
                total = int(memory_info['MemTotal'])
                available = int(memory_info['MemAvailable'])
                used = total - available
                memory_info['UsedPercentage'] = (used / total) * 100

            return memory_info
        except Exception as e:
            print(f"Error getting memory info: {e}")
            return {}

    def get_cpu_info(self):
        """Get CPU information using /proc/stat"""
        cpu_info = {'cpu_percent': 0}
        try:
            ## We need two readings to calculate CPU usage
            def get_cpu_sample():
                with open('/proc/stat', 'r') as f:
                    line = f.readline()
                cpu_values = [int(x) for x in line.split()[1:8]]
                return sum(cpu_values), cpu_values[3]  ## Return total and idle

            ## First sample
            total1, idle1 = get_cpu_sample()
            time.sleep(0.5)  ## Wait for 0.5 second

            ## Second sample
            total2, idle2 = get_cpu_sample()

            ## Calculate CPU usage
            total_delta = total2 - total1
            idle_delta = idle2 - idle1

            if total_delta > 0:
                cpu_info['cpu_percent'] = 100 * (1 - idle_delta / total_delta)

            return cpu_info
        except Exception as e:
            print(f"Error getting CPU info: {e}")
            return cpu_info

    def get_disk_info(self):
        """Get disk usage information"""
        try:
            ## Get disk usage for the root filesystem
            stat = os.statvfs('/')

            ## Calculate total, free, and used space
            total = stat.f_blocks * stat.f_frsize
            free = stat.f_bfree * stat.f_frsize
            used = total - free

            return {
                'total_bytes': total,
                'free_bytes': free,
                'used_bytes': used,
                'used_percent': (used / total) * 100 if total > 0 else 0
            }
        except Exception as e:
            print(f"Error getting disk info: {e}")
            return {}

    def get_process_count(self):
        """Count running processes"""
        try:
            return len([p for p in os.listdir('/proc') if p.isdigit()])
        except Exception as e:
            print(f"Error counting processes: {e}")
            return 0

    def format_bytes(self, bytes_value):
        """Format bytes into a human-readable format"""
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if bytes_value < 1024 or unit == 'TB':
                return f"{bytes_value:.2f} {unit}"
            bytes_value /= 1024

    def display_dashboard(self):
        """Display the system monitoring dashboard"""
        ## Clear the screen (works in most terminals)
        print("\033c", end="")

        ## Display header
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"System Monitor - {current_time}")
        print("-" * 50)

        ## Display uptime
        uptime = self.get_uptime()
        if uptime:
            print(f"Uptime: {uptime['days']} days, {uptime['hours']} hours, "
                  f"{uptime['minutes']} minutes, {uptime['seconds']} seconds")

        ## Display CPU usage
        cpu_info = self.get_cpu_info()
        print(f"CPU Usage: {cpu_info['cpu_percent']:.1f}%")

        ## Display memory information
        memory_info = self.get_memory_info()
        if memory_info and 'MemTotal' in memory_info:
            print("\nMemory Information:")
            total = int(memory_info['MemTotal'])
            available = int(memory_info.get('MemAvailable', 0))
            used = total - available
            print(f"  Total: {self.format_bytes(total)}")
            print(f"  Used: {self.format_bytes(used)} ({memory_info.get('UsedPercentage', 0):.1f}%)")
            print(f"  Available: {self.format_bytes(available)}")
            if 'SwapTotal' in memory_info:
                swap_total = int(memory_info['SwapTotal'])
                swap_free = int(memory_info.get('SwapFree', 0))
                swap_used = swap_total - swap_free
                if swap_total > 0:
                    print(f"  Swap Used: {self.format_bytes(swap_used)} "
                          f"({(swap_used / swap_total) * 100:.1f}%)")

        ## Display disk information
        disk_info = self.get_disk_info()
        if disk_info:
            print("\nDisk Information (/):")
            print(f"  Total: {self.format_bytes(disk_info['total_bytes'])}")
            print(f"  Used: {self.format_bytes(disk_info['used_bytes'])} "
                  f"({disk_info['used_percent']:.1f}%)")
            print(f"  Free: {self.format_bytes(disk_info['free_bytes'])}")

        ## Display process count
        process_count = self.get_process_count()
        print(f"\nRunning Processes: {process_count}")

        print("\nPress Ctrl+C to exit")

    def start_monitoring(self, interval=2):
        """Start the system monitoring with the specified refresh interval"""
        self.running = True
        try:
            while self.running:
                self.display_dashboard()
                time.sleep(interval)
        except KeyboardInterrupt:
            print("\nMonitoring stopped.")
            self.running = False

## Create and start the system monitor
if __name__ == "__main__":
    monitor = SystemMonitor()
    monitor.start_monitoring()

Exécutez le moniteur système :

python3 system_monitor.py

Le moniteur système affichera des informations en temps réel sur votre système, se mettant à jour toutes les 2 secondes. Vous devriez voir un tableau de bord avec :

  • Le temps de fonctionnement du système (Uptime)
  • L'utilisation du CPU
  • Les informations sur la mémoire (totale, utilisée, disponible)
  • L'utilisation du disque
  • Le nombre de processus

Laissez le moniteur s'exécuter pendant quelques instants pour observer comment les métriques changent, puis appuyez sur Ctrl+C pour quitter.

Comprendre la structure du moniteur système

Décomposons les principaux composants de notre moniteur système :

  1. Structure de classe : Utilisation de la programmation orientée objet pour organiser notre code
  2. Organisation des méthodes : Séparation des fonctionnalités en méthodes distinctes
  3. Gestion des erreurs : Utilisation de blocs try-except pour gérer les erreurs potentielles
  4. Formatage des données : Conversion des données brutes en formats lisibles par l'homme
  5. Mises à jour en temps réel : Utilisation d'une boucle avec time.sleep() pour les mises à jour périodiques

Ajout de documentation

Maintenant, ajoutons un fichier de documentation pour notre moniteur système. Créez un fichier nommé README.md :

Python System Monitor

Une application complète de surveillance du système construite avec Python en utilisant ctypes pour les interactions système.

Fonctionnalités

  • Surveillance en temps réel de l'utilisation du CPU
  • Suivi de l'utilisation de la mémoire
  • Analyse de l'espace disque
  • Comptage des processus
  • Affichage du temps de fonctionnement du système (Uptime)

Exigences

  • Python 3.6 ou supérieur
  • Système d'exploitation Linux

Comment exécuter

Exécutez simplement le script principal :

python system_monitor.py
### Comment ça marche

Cette application utilise la bibliothèque `ctypes` de Python pour interagir avec les bibliothèques système et accéder aux informations système de bas niveau. Elle utilise également le système de fichiers `/proc` pour collecter des métriques supplémentaires sur l'état du système.

La surveillance se fait en temps réel avec des mises à jour toutes les 2 secondes (configurable).

### Architecture

L'application suit une approche orientée objet avec ces composants clés :

1. **Classe SystemMonitor** : Classe principale qui orchestre la surveillance
2. **Méthodes de collecte de données** : Méthodes pour collecter différentes métriques système
3. **Méthodes d'affichage** : Méthodes pour formater et afficher les données collectées

### Extension du moniteur

Pour ajouter de nouvelles capacités de surveillance :

1. Créez une nouvelle méthode dans la classe `SystemMonitor` pour collecter les données souhaitées
2. Mettez à jour la méthode `display_dashboard` pour afficher les nouvelles informations
3. Assurez-vous d'une gestion appropriée des erreurs pour la robustesse

### Ressources d'apprentissage

- [Documentation Python ctypes](https://docs.python.org/3/library/ctypes.html)
- [Documentation Linux Proc Filesystem](https://man7.org/linux/man-pages/man5/proc.5.html)

### Révision et résumé

Passons en revue ce que nous avons accompli dans ce laboratoire :

1. Nous avons appris à utiliser la bibliothèque `ctypes` de Python pour interagir avec les bibliothèques système
2. Nous avons exploré les types de données et les structures C en Python
3. Nous avons créé plusieurs applications pratiques :
   - Récupération d'informations système de base
   - Surveillance des processus
   - Utilitaires de temps
   - Démonstration de la gestion de la mémoire
   - Moniteur système complet

4. Nous avons appris à :
   - Charger des bibliothèques dynamiques
   - Appeler des fonctions C depuis Python
   - Définir et manipuler des structures C
   - Travailler avec des pointeurs et des adresses mémoire
   - Gérer les erreurs au niveau du système

Ces compétences constituent la base pour travailler avec l'API Windows lors du développement sur les systèmes Windows. Les principes restent les mêmes - vous chargerez des bibliothèques spécifiques à Windows (comme kernel32.dll, user32.dll, etc.) au lieu de libc, mais l'approche pour définir les structures, appeler les fonctions et gérer les données reste cohérente.

Résumé

Dans ce laboratoire, vous avez appris à utiliser la bibliothèque ctypes de Python pour interagir avec les bibliothèques système et effectuer des opérations au niveau du système. Bien que le laboratoire ait été mené dans un environnement Linux, les principes et les techniques que vous avez appris s'appliquent également directement à la programmation de l'API Windows.

Points clés à retenir de ce laboratoire :

  1. Comprendre les fondamentaux de ctypes : Vous avez appris à charger des bibliothèques dynamiques, à définir des types de données C et à appeler des fonctions système depuis Python.

  2. Travailler avec les structures C : Vous avez maîtrisé la création et la manipulation de structures C en Python, ce qui est essentiel pour échanger des données complexes avec les bibliothèques système.

  3. Gestion de la mémoire : Vous avez acquis des connaissances sur l'allocation de mémoire, les pointeurs et la gestion de la mémoire lorsque vous travaillez avec les bibliothèques système.

  4. Création d'applications pratiques : Vous avez appliqué vos connaissances pour créer des applications utiles, aboutissant à un moniteur système complet.

  5. Intégration système : Vous avez vu comment Python peut être intégré à des fonctionnalités système de bas niveau grâce au pont ctypes.

Ces compétences fournissent une base solide pour le développement d'applications qui doivent interagir avec le système d'exploitation à un niveau bas, que ce soit sur Linux ou Windows. Lorsque vous travaillez sur des systèmes Windows, vous utiliserez la même approche, mais avec des bibliothèques et des fonctions API spécifiques à Windows.

L'apprentissage ultérieur pourrait impliquer l'exploration des API spécifiques à Windows pour la gestion des fenêtres, les interfaces graphiques, les services système ou les capacités de mise en réseau en utilisant les techniques que vous avez apprises dans ce laboratoire.