Como Interagir com a API do Windows em Python

PythonBeginner
Pratique Agora

Introdução

Este tutorial abrangente irá guiá-lo através do uso da biblioteca ctypes do Python para interagir com a API do Windows. Você aprenderá como acessar a funcionalidade do sistema Windows, gerenciar processos e aproveitar recursos específicos do Windows diretamente do Python. Ao final deste laboratório, você entenderá como construir aplicações que podem se integrar perfeitamente com o sistema operacional Windows e realizar operações poderosas em nível de sistema.

Compreendendo a Biblioteca ctypes do Python

Nesta etapa, exploraremos os fundamentos da biblioteca ctypes do Python, que serve como base para interagir com a API do Windows. A biblioteca ctypes atua como uma ponte entre o Python e as bibliotecas C nativas, permitindo-nos chamar funções diretamente de DLLs (Dynamic Link Libraries).

O que é ctypes?

ctypes é uma biblioteca de funções estrangeiras para Python que fornece tipos de dados compatíveis com C e permite chamar funções em DLLs ou bibliotecas compartilhadas. É particularmente útil para:

  • Acessar funções de sistema de baixo nível
  • Interagir com hardware
  • Chamar funções de bibliotecas específicas da plataforma

Instalando Pacotes Necessários

Antes de começarmos a escrever código, vamos instalar os pacotes necessários. Abra um terminal no WebIDE e execute:

pip install pywin32-ctypes

Isso instalará uma biblioteca compatível que funciona com nosso ambiente.

Uso Básico de ctypes

Vamos criar um script Python simples para entender como ctypes funciona. No WebIDE, crie um novo arquivo chamado ctypes_basics.py no diretório /home/labex/project com o seguinte conteúdo:

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")

Execute o script usando:

python3 ctypes_basics.py

Você deve ver uma saída semelhante a esta:

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

Compreendendo os Tipos de Dados C em Python

ctypes fornece wrappers compatíveis com Python para tipos de dados C. Aqui está uma tabela de referência dos tipos de dados C comuns e seus equivalentes ctypes:

Tipo C Tipo ctypes Tipo Python
char c_char Objeto bytes de 1 caractere
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

Vamos explorar esses tipos de dados com outro exemplo. Crie um arquivo chamado 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)])

Execute o script:

python3 ctypes_types.py

Saída esperada:

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

Esta compreensão fundamental de ctypes nos ajudará à medida que avançamos para interagir com bibliotecas de sistema mais complexas e com a API do Windows especificamente.

Acessando Bibliotecas do Sistema com ctypes

Nesta etapa, aprenderemos como acessar bibliotecas do sistema e chamar suas funções usando ctypes. Como estamos trabalhando em um ambiente Linux, focaremos nas bibliotecas do sistema Linux, enquanto explicamos os princípios que também se aplicam ao acesso à API do Windows.

Carregando Bibliotecas Dinâmicas

Em Python, podemos carregar bibliotecas dinâmicas usando vários métodos fornecidos por ctypes:

  • CDLL - para carregar bibliotecas C padrão
  • WinDLL - para carregar DLLs do Windows (quando no Windows)
  • OleDLL - para carregar bibliotecas COM (quando no Windows)

Vamos criar um arquivo chamado system_info.py para explorar informações do sistema usando bibliotecas padrão:

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}")

Execute o script:

python3 system_info.py

Você deve ver informações detalhadas sobre seu sistema, semelhante a esta:

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

Criando um Monitor de Processos

Agora, vamos criar uma aplicação mais prática: um monitor de processos simples que listará os processos em execução. Crie um arquivo chamado 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")

Execute o monitor de processos:

python3 process_monitor.py

Você deve ver uma saída semelhante a esta, que será atualizada a cada 3 segundos:

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
...

Deixe o monitor de processos rodar por cerca de 10 segundos e, em seguida, pressione Ctrl+C para pará-lo.

Nestes exemplos, aprendemos como:

  1. Carregar e usar bibliotecas do sistema com ctypes
  2. Acessar informações do sistema
  3. Criar uma ferramenta prática de monitoramento de processos

Esses princípios são os mesmos que você usaria para interagir com a API do Windows por meio de ctypes, apenas com nomes de bibliotecas e chamadas de funções diferentes.

Criando e Usando Structs C com ctypes

Nesta etapa, aprenderemos como definir e usar estruturas C em Python. Structs são essenciais ao trabalhar com APIs do sistema, pois são comumente usadas para trocar dados entre Python e bibliotecas do sistema.

Compreendendo Estruturas C em Python

Estruturas C podem ser representadas em Python usando a classe ctypes.Structure. Isso nos permite criar estruturas de dados complexas que correspondem ao layout de memória esperado pelas funções C.

Vamos criar um arquivo chamado struct_example.py para explorar como trabalhar com estruturas:

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}")

Execute o script:

python3 struct_example.py

Você deve ver uma saída semelhante a esta:

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

Criando um Utilitário de Tempo do Sistema

Agora, vamos criar uma aplicação prática que usa estruturas C para interagir com as funções de tempo do sistema. Crie um arquivo chamado 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()}")

Execute o utilitário de tempo:

python3 time_utility.py

Você deve ver uma saída semelhante a esta:

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

Compreendendo o Gerenciamento de Memória com ctypes

Ao trabalhar com ctypes e estruturas C, é importante entender o gerenciamento de memória. Vamos criar mais um exemplo, memory_management.py, para demonstrar isso:

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()

Execute o script de gerenciamento de memória:

python3 memory_management.py

Você deve ver uma saída semelhante a esta:

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...

Nestes exemplos, aprendemos como:

  1. Definir e usar estruturas C em Python com ctypes
  2. Criar ponteiros para estruturas e acessar seus conteúdos
  3. Criar estruturas aninhadas e matrizes de estruturas
  4. Construir aplicações práticas usando funções de tempo do sistema
  5. Gerenciar memória ao trabalhar com ctypes

Essas habilidades são essenciais ao trabalhar com funções da API do Windows, que frequentemente exigem estruturas de dados complexas para passar informações de um lado para o outro.

Construindo uma Aplicação Completa de Monitoramento do Sistema

Nesta etapa final, reuniremos todo o nosso conhecimento para construir uma aplicação abrangente de monitoramento do sistema. Esta aplicação usará ctypes para coletar informações do sistema, exibir métricas em tempo real e demonstrar como estruturar uma aplicação Python maior que interage com bibliotecas do sistema.

Criando o Monitor do Sistema

Crie um novo arquivo chamado system_monitor.py com o seguinte conteúdo:

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()

Execute o monitor do sistema:

python3 system_monitor.py

O monitor do sistema exibirá informações em tempo real sobre seu sistema, atualizando a cada 2 segundos. Você deve ver um painel com:

  • Tempo de atividade do sistema
  • Uso da CPU
  • Informações de memória (total, usado, disponível)
  • Uso do disco
  • Contagem de processos

Deixe o monitor rodar por alguns instantes para observar como as métricas mudam e, em seguida, pressione Ctrl+C para sair.

Compreendendo a Estrutura do Monitor do Sistema

Vamos detalhar os principais componentes do nosso monitor do sistema:

  1. Estrutura da Classe: Usando programação orientada a objetos para organizar nosso código
  2. Organização de Métodos: Separando a funcionalidade em métodos distintos
  3. Tratamento de Erros: Usando blocos try-except para lidar com possíveis erros
  4. Formatação de Dados: Convertendo dados brutos em formatos legíveis por humanos
  5. Atualizações em Tempo Real: Usando um loop com time.sleep() para atualizações periódicas

Adicionando Documentação

Agora, vamos adicionar um arquivo de documentação para o nosso monitor do sistema. Crie um arquivo chamado README.md:

## Python System Monitor

A comprehensive system monitoring application built with Python using `ctypes` for system interactions.

### Features

- Real-time CPU usage monitoring
- Memory usage tracking
- Disk space analysis
- Process count
- System uptime display

### Requirements

- Python 3.6 or higher
- Linux operating system

### How to Run

Simply execute the main script:

```bash
python system_monitor.py
### How It Works

This application uses Python's `ctypes` library to interface with system libraries and access low-level system information. It also uses the `/proc` filesystem to gather additional metrics about the system state.

The monitoring happens in real-time with updates every 2 seconds (configurable).

### Architecture

The application follows an object-oriented approach with these key components:

1. **SystemMonitor class**: Main class that orchestrates the monitoring
2. **Data Collection Methods**: Methods for gathering different system metrics
3. **Display Methods**: Methods for formatting and displaying the collected data

### Extending the Monitor

To add new monitoring capabilities:

1. Create a new method in the `SystemMonitor` class to collect the desired data
2. Update the `display_dashboard` method to show the new information
3. Ensure proper error handling for robustness

### Learning Resources

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

### Review and Summary

Let's review what we've accomplished in this lab:

1. We learned how to use Python's `ctypes` library to interact with system libraries
2. We explored C data types and structures in Python
3. We created several practical applications:
   - Basic system information retrieval
   - Process monitoring
   - Time utilities
   - Memory management demonstration
   - Comprehensive system monitor

4. We learned how to:
   - Load dynamic libraries
   - Call C functions from Python
   - Define and manipulate C structures
   - Work with pointers and memory addresses
   - Handle system-level errors

These skills form the foundation for working with the Windows API when developing on Windows systems. The principles remain the same - you'll load Windows-specific libraries (like kernel32.dll, user32.dll, etc.) instead of libc, but the approach to defining structures, calling functions, and handling the data remains consistent.

Resumo

Neste laboratório, você aprendeu como usar a biblioteca ctypes do Python para interagir com bibliotecas do sistema e realizar operações em nível de sistema. Embora o laboratório tenha sido conduzido em um ambiente Linux, os princípios e técnicas que você aprendeu também se aplicam diretamente à programação da API do Windows.

Principais conclusões deste laboratório:

  1. Compreendendo os Fundamentos do ctypes: Você aprendeu como carregar bibliotecas dinâmicas, definir tipos de dados C e chamar funções do sistema a partir do Python.

  2. Trabalhando com Estruturas C: Você dominou a criação e manipulação de estruturas C em Python, essencial para trocar dados complexos com bibliotecas do sistema.

  3. Gerenciamento de Memória: Você obteve informações sobre alocação de memória, ponteiros e gerenciamento de memória ao trabalhar com bibliotecas do sistema.

  4. Construindo Aplicações Práticas: Você aplicou seu conhecimento para criar aplicações úteis, culminando em um monitor de sistema abrangente.

  5. Integração do Sistema: Você viu como o Python pode ser integrado à funcionalidade do sistema de baixo nível por meio da ponte ctypes.

Essas habilidades fornecem uma base sólida para o desenvolvimento de aplicações que precisam interagir com o sistema operacional em um nível baixo, seja no Linux ou no Windows. Ao trabalhar em sistemas Windows, você usaria a mesma abordagem, mas com bibliotecas e funções de API específicas do Windows.

Aprendizado adicional pode envolver a exploração de APIs específicas do Windows para gerenciamento de janelas, interfaces gráficas, serviços do sistema ou recursos de rede usando as técnicas que você aprendeu neste laboratório.