Python での Windows API 操作方法

PythonBeginner
オンラインで実践に進む

はじめに

この包括的なチュートリアルでは、Python の ctypes ライブラリを使用して Windows API と対話する方法を説明します。Windows のシステム機能へのアクセス、プロセスの管理、および Windows 固有の機能を Python から直接利用する方法を学びます。この実験(Lab)の終わりには、Windows オペレーティングシステムとシームレスに統合し、強力なシステムレベルの操作を実行できるアプリケーションを構築する方法を理解できるようになります。

Python の ctypes ライブラリについて理解する

このステップでは、Windows API との対話の基盤となる Python の ctypes ライブラリの基本を説明します。 ctypes ライブラリは、Python とネイティブ C ライブラリ間のブリッジとして機能し、DLL (Dynamic Link Libraries) から関数を直接呼び出すことができます。

ctypes とは何か?

ctypes は、C 互換のデータ型を提供し、DLL または共有ライブラリ内の関数を呼び出すことができる Python 用の外部関数ライブラリです。これは特に以下の場合に役立ちます。

  • 低レベルのシステム機能へのアクセス
  • ハードウェアとの対話
  • プラットフォーム固有のライブラリからの関数の呼び出し

必要なパッケージのインストール

コードを書き始める前に、必要なパッケージをインストールしましょう。WebIDE でターミナルを開き、以下を実行します。

pip install pywin32-ctypes

これにより、環境で動作する互換性のあるライブラリがインストールされます。

ctypes の基本的な使用法

ctypes の仕組みを理解するために、簡単な Python スクリプトを作成しましょう。WebIDE で、/home/labex/project ディレクトリに ctypes_basics.py という名前の新しいファイルを作成し、次の内容を記述します。

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

次のコマンドを使用してスクリプトを実行します。

python3 ctypes_basics.py

次のような出力が表示されるはずです。

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

Python での C データ型の理解

ctypes は、C データ型に対応する Python 互換のラッパーを提供します。一般的な C データ型とその ctypes の同等物の参照表を以下に示します。

C 型 ctypes 型 Python 型
char c_char 1 文字の bytes オブジェクト
int c_int int
unsigned int c_uint int
long c_long int
void * c_void_p int または None
char * c_char_p bytes または None
wchar_t * c_wchar_p str または None

別の例でこれらのデータ型を調べてみましょう。 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)])

スクリプトを実行します。

python3 ctypes_types.py

期待される出力:

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

ctypes のこの基本的な理解は、より複雑なシステムライブラリ、特に Windows API と対話を進める上で役立ちます。

ctypes を使用したシステムライブラリへのアクセス

このステップでは、ctypes を使用してシステムライブラリにアクセスし、その関数を呼び出す方法を学びます。Linux 環境で作業しているため、Windows API へのアクセスにも適用される原則を説明しながら、Linux システムライブラリに焦点を当てます。

動的ライブラリのロード

Python では、ctypes が提供するいくつかのメソッドを使用して動的ライブラリをロードできます。

  • CDLL - 標準 C ライブラリをロードする場合
  • WinDLL - Windows DLL をロードする場合 (Windows 上)
  • OleDLL - COM ライブラリをロードする場合 (Windows 上)

標準ライブラリを使用してシステム情報を調べるために、system_info.py という名前のファイルを作成しましょう。

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

スクリプトを実行します。

python3 system_info.py

次のような、システムに関する詳細情報が表示されるはずです。

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

プロセスモニターの作成

次に、より実用的なアプリケーションを作成しましょう。それは、実行中のプロセスを一覧表示するシンプルなプロセスモニターです。 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")

プロセスモニターを実行します。

python3 process_monitor.py

次のような出力が表示されるはずです。これは 3 秒ごとに更新されます。

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

プロセスモニターを約 10 秒間実行してから、Ctrl+C を押して停止します。

これらの例では、以下を学習しました。

  1. ctypes を使用してシステムライブラリをロードし、使用する
  2. システム情報にアクセスする
  3. 実用的なプロセス監視ツールを作成する

これらの原則は、ctypes を介して Windows API と対話するために使用する原則と同じですが、ライブラリ名と関数呼び出しが異なります。

ctypes を使用した C 構造体の作成と使用

このステップでは、Python で C 構造体を定義して使用する方法を学びます。構造体は、システム API を操作する際に不可欠です。これは、Python とシステムライブラリ間でデータを交換するために一般的に使用されるからです。

Python での C 構造体の理解

C 構造体は、ctypes.Structure クラスを使用して Python で表現できます。これにより、C 関数が期待するメモリレイアウトに一致する複雑なデータ構造を作成できます。

構造体を操作する方法を調べるために、struct_example.py という名前のファイルを作成しましょう。

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

スクリプトを実行します。

python3 struct_example.py

次のような出力が表示されるはずです。

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

システム時間ユーティリティの作成

次に、C 構造体を使用してシステム時間関数と対話する実用的なアプリケーションを作成しましょう。 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()}")

時間ユーティリティを実行します。

python3 time_utility.py

次のような出力が表示されるはずです。

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

ctypes を使用したメモリ管理の理解

ctypes と C 構造体を操作する場合、メモリ管理を理解することが重要です。これを実証するために、もう 1 つの例である memory_management.py を作成しましょう。

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

メモリ管理スクリプトを実行します。

python3 memory_management.py

次のような出力が表示されるはずです。

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

これらの例では、以下を学習しました。

  1. ctypes を使用して Python で C 構造体を定義し、使用する
  2. 構造体へのポインタを作成し、その内容にアクセスする
  3. ネストされた構造体と構造体の配列を作成する
  4. システム時間関数を使用して実用的なアプリケーションを構築する
  5. ctypes を使用する場合のメモリを管理する

これらのスキルは、Windows API 関数を操作する際に不可欠です。Windows API 関数は、情報をやり取りするために複雑なデータ構造を頻繁に必要とします。

完全なシステムモニターアプリケーションの構築

この最後のステップでは、これまでのすべての知識を組み合わせて、包括的なシステムモニターアプリケーションを構築します。このアプリケーションは、ctypes を使用してシステム情報を収集し、リアルタイムのメトリクスを表示し、システムライブラリと対話するより大きな Python アプリケーションを構造化する方法を示します。

システムモニターの作成

次の内容で、system_monitor.py という名前の新しいファイルを作成します。

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

システムモニターを実行します。

python3 system_monitor.py

システムモニターは、システムに関するリアルタイム情報を表示し、2 秒ごとに更新されます。次のダッシュボードが表示されるはずです。

  • システムの稼働時間
  • CPU 使用率
  • メモリ情報 (合計、使用済み、利用可能)
  • ディスク使用量
  • プロセス数

メトリクスがどのように変化するかを観察するために、モニターをしばらく実行してから、Ctrl+C を押して終了します。

システムモニター構造の理解

システムモニターの主要なコンポーネントを分解してみましょう。

  1. クラス構造: オブジェクト指向プログラミングを使用してコードを整理する
  2. メソッドの編成: 機能を個別のメソッドに分離する
  3. エラー処理: try-except ブロックを使用して潜在的なエラーを処理する
  4. データフォーマット: 生データを人間が読める形式に変換する
  5. リアルタイム更新: 定期的な更新に time.sleep() を使用したループを使用する

ドキュメントの追加

次に、システムモニターのドキュメントファイルを追加しましょう。 README.md という名前のファイルを作成します。

## Python System Monitor

Python を使用してシステムライブラリと対話するために `ctypes` を使用して構築された、包括的なシステム監視アプリケーション。

### 特徴

- リアルタイムの CPU 使用率の監視
- メモリ使用量の追跡
- ディスク容量の分析
- プロセス数
- システム稼働時間の表示

### 要件

- Python 3.6 以降
- Linux オペレーティングシステム

### 実行方法

メインスクリプトを実行するだけです。

```bash
python system_monitor.py
### 仕組み

このアプリケーションは、Python の `ctypes` ライブラリを使用してシステムライブラリとインターフェースし、低レベルのシステム情報にアクセスします。また、`/proc` ファイルシステムを使用して、システムの状態に関する追加のメトリクスを収集します。

監視はリアルタイムで行われ、2 秒ごとに更新されます (設定可能)。

### アーキテクチャ

このアプリケーションは、次の主要コンポーネントを備えたオブジェクト指向アプローチに従います。

1. **SystemMonitor クラス**: 監視を調整するメインクラス
2. **データ収集メソッド**: さまざまなシステムメトリクスを収集するためのメソッド
3. **表示メソッド**: 収集されたデータをフォーマットして表示するためのメソッド

### モニターの拡張

新しい監視機能をを追加するには:

1. 目的のデータを収集するために、`SystemMonitor` クラスに新しいメソッドを作成します
2. 新しい情報を表示するように `display_dashboard` メソッドを更新します
3. 堅牢性のために適切なエラー処理を確保します

### 学習リソース

- [Python ctypes ドキュメント](https://docs.python.org/3/library/ctypes.html)
- [Linux Proc ファイルシステムドキュメント](https://man7.org/linux/man-pages/man5/proc.5.html)

### レビューと概要

このラボで達成した内容を確認しましょう。

1. Python の `ctypes` ライブラリを使用してシステムライブラリと対話する方法を学びました
2. Python での C データ型と構造体を調べました
3. いくつかの実用的なアプリケーションを作成しました。
   - 基本的なシステム情報の取得
   - プロセス監視
   - 時間ユーティリティ
   - メモリ管理のデモンストレーション
   - 包括的なシステムモニター

4. 以下を学習しました。
   - 動的ライブラリをロードする
   - Python から C 関数を呼び出す
   - C 構造体を定義して操作する
   - ポインタとメモリアドレスを操作する
   - システムレベルのエラーを処理する

これらのスキルは、Windows システムで開発する際に Windows API 関数を操作するための基礎となります。原則は同じです。libc の代わりに、Windows 固有のライブラリ (kernel32.dll、user32.dll など) をロードしますが、構造体の定義、関数の呼び出し、データの処理に対するアプローチは一貫しています。

まとめ

このラボでは、Python の ctypes ライブラリを使用してシステムライブラリと対話し、システムレベルの操作を実行する方法を学びました。このラボは Linux 環境で実施されましたが、学んだ原則とテクニックは、Windows API プログラミングにも直接適用できます。

このラボからの主なポイント:

  1. ctypes の基本の理解: 動的ライブラリのロード、C データ型の定義、Python からのシステム関数の呼び出し方法を学びました。

  2. C 構造体の操作: システムライブラリとの複雑なデータ交換に不可欠な、Python での C 構造体の作成と操作を習得しました。

  3. メモリ管理: システムライブラリを操作する際のメモリ割り当て、ポインタ、およびメモリ管理に関する洞察を得ました。

  4. 実用的なアプリケーションの構築: 知識を応用して、包括的なシステムモニターで最高潮に達する、役立つアプリケーションを作成しました。

  5. システム統合: ctypes ブリッジを介して、Python を低レベルのシステム機能と統合する方法を確認しました。

これらのスキルは、Linux または Windows のいずれであっても、オペレーティングシステムと低レベルで対話する必要があるアプリケーションを開発するための強固な基盤を提供します。Windows システムで作業する場合は、同じアプローチを使用しますが、Windows 固有のライブラリと API 関数を使用します。

さらなる学習には、このラボで学んだテクニックを使用して、ウィンドウ管理、グラフィカルインターフェース、システムサービス、またはネットワーク機能に関する Windows 固有の API を探索することが含まれます。