Rust语言从入门到精通系列 - 零基础入门Win32 API开发(上)

6 分钟阅读

Rust 语言是一种快速、安全、并发的系统编程语言。它的设计目标是为了提供更好的内存安全和线程安全,同时保持高性能。而 winapi 模块则是 Rust 语言的一个重要组成部分,它提供了与 Windows 操作系统 API 的交互能力。本教程将介绍 winapi 模块的基础用法和进阶用法,以及最佳实践。

本篇主要介绍 Win32 API 基础用法,进阶相关的内容在 Rust语言从入门到精通系列 - 零基础入门Win32 API开发(下)

基础用法

Cargo.toml 文件引入依赖。

1
2
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["winuser"] }

获取系统错误信息

1
2
3
4
5
6
use winapi::um::errhandlingapi::GetLastError;

fn main() {
    let error_code = unsafe { GetLastError() };
    println!("Error code: {}", error_code);
}

获取桌面窗口句柄

1
2
3
4
5
6
7
use winapi::um::winuser::GetDesktopWindow;
use winapi::um::winuser::HWND;

fn main() {
    let hwnd: HWND = unsafe { GetDesktopWindow() };
    println!("{:?}", hwnd);
}

在代码中,使用 GetDesktopWindow 函数获取桌面窗口句柄。使用 unsafe 关键字调用该函数,因为该函数是一个裸指针函数,需要手动管理内存安全。

创建窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use std::ptr::null_mut;
use winapi::um::winuser::{CreateWindowExW, DefWindowProcW, RegisterClassW, CW_USEDEFAULT, MSG, WM_DESTROY, WNDCLASSW};

fn main() {
    let class_name = "MyWindowClass".to_wide_null();
    let window_name = "My Window".to_wide_null();
    let h_instance = unsafe { winapi::um::libloaderapi::GetModuleHandleW(null_mut()) };
    let wnd_class = WNDCLASSW {
        style: 0,
        lpfnWndProc: Some(DefWindowProcW),
        hInstance: h_instance,
        lpszClassName: class_name.as_ptr(),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hIcon: null_mut(),
        hCursor: null_mut(),
        hbrBackground: null_mut(),
        lpszMenuName: null_mut(),
    };
    let class_atom = unsafe { RegisterClassW(&wnd_class) };
    let hwnd = unsafe {
        CreateWindowExW(
            0,
            class_atom as *const _,
            window_name.as_ptr(),
            0,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            null_mut(),
            null_mut(),
            h_instance,
            null_mut(),
        )
    };
    let mut msg = MSG::default();
    loop {
        let ret = unsafe { winapi::um::winuser::GetMessageW(&mut msg, null_mut(), 0, 0) };
        if ret > 0 {
            unsafe {
                winapi::um::winuser::TranslateMessage(&msg);
                winapi::um::winuser::DispatchMessageW(&msg);
            }
        } else {
            break;
        }
    }
}

打开文件对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::ptr::null_mut;
use winapi::um::commdlg::{GetOpenFileNameW, OPENFILENAMEW};
use winapi::um::winuser::HWND;

fn main() {
    let mut ofn = OPENFILENAMEW {
        lStructSize: std::mem::size_of::<OPENFILENAMEW>() as u32,
        hwndOwner: HWND(null_mut()),
        lpstrFilter: "Text Files\0*.txt\0All Files\0*.*\0\0".to_wide_null().as_ptr(),
        lpstrFile: [0; 260].as_mut_ptr(),
        nMaxFile: 260,
        Flags: 0,
        lpstrDefExt: "txt\0".to_wide_null().as_ptr(),
        ..Default::default()
    };
    let ret = unsafe { GetOpenFileNameW(&mut ofn) };
    if ret != 0 {
        let file_name = ofn.lpstrFile.to_string_lossy();
        println!("Selected file: {}", file_name);
    }
}

读取注册表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::ptr::null_mut;
use winapi::um::winreg::{RegCloseKey, RegOpenKeyExW, HKEY_LOCAL_MACHINE, KEY_READ, LPDWORD, REG_SZ};
use widestring::U16CString;

fn main() {
    let sub_key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion".to_wide_null();
    let mut h_key = null_mut();
    let ret = unsafe { RegOpenKeyExW(HKEY_LOCAL_MACHINE, sub_key.as_ptr(), 0, KEY_READ, &mut h_key) };
    if ret == 0 {
        let mut buffer = [0u16; 1024];
        let mut buffer_size = (buffer.len() * 2) as u32;
        let mut value_type = REG_SZ;
        let ret = unsafe {
            winapi::um::winreg::RegQueryValueExW(
                h_key,
                "ProductName".to_wide_null().as_ptr(),
                null_mut(),
                &mut value_type,
                buffer.as_mut_ptr() as *mut _,
                &mut buffer_size,
            )
        };
        if ret == 0 {
            let value = U16CString::from_vec_with_nul(&buffer[..(buffer_size as usize)]).unwrap();
            println!("Product name: {}", value.to_string_lossy());
        }
        unsafe { RegCloseKey(h_key) };
    }
}

注册热键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use winapi::um::winuser::{RegisterHotKey, UnregisterHotKey};
use winapi::um::winuser::{HWND, WM_HOTKEY};

fn main() {
    let hwnd: HWND = std::ptr::null_mut();
    let id: i32 = 1;
    let modifiers: u32 = 0x0002; // MOD_CONTROL
    let vk: u32 = 0x43; // 'C'
    let result: i32 = unsafe { RegisterHotKey(hwnd, id, modifiers, vk) };
    if result == 0 {
        println!("Failed to register hotkey.");
        return;
    }
    loop {
        let mut msg = std::mem::zeroed();
        let result = unsafe { winapi::um::winuser::GetMessageW(&mut msg, hwnd, 0, 0) };
        if result == -1 {
            println!("Failed to get message.");
            break;
        }
        if msg.message == WM_HOTKEY {
            println!("Hotkey pressed.");
            break;
        }
        unsafe { winapi::um::winuser::TranslateMessage(&msg) };
        unsafe { winapi::um::winuser::DispatchMessageW(&msg) };
    }
    unsafe { UnregisterHotKey(hwnd, id) };
}

在代码中,使用 RegisterHotKey 函数注册一个热键,当用户按下 Ctrl+C 时,会收到 WM_HOTKEY 消息。使用 GetMessageW 函数获取消息,使用 TranslateMessage 函数翻译消息,使用 DispatchMessageW 函数分发消息。使用 UnregisterHotKey 函数注销热键。

创建进程

在 Windows 系统中,可以使用 CreateProcess 函数创建一个进程。使用 winapi 模块可以方便地调用 CreateProcess 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use winapi::um::processthreadsapi::CreateProcessA;
use winapi::um::winbase::CREATE_NEW_CONSOLE;
use winapi::um::winnt::{PROCESS_INFORMATION, STARTUPINFOA};
use std::ffi::CString;
use std::mem::{size_of, zeroed};
use std::ptr::null_mut;

fn main() {
    let command_line = CString::new("notepad.exe").unwrap();
    let mut startup_info: STARTUPINFOA = unsafe { zeroed() };
    startup_info.cb = size_of::<STARTUPINFOA>() as u32;
    let mut process_info: PROCESS_INFORMATION = unsafe { zeroed() };
    let result = unsafe {
        CreateProcessA(
            null_mut(),
            command_line.as_ptr() as *mut _,
            null_mut(),
            null_mut(),
            0,
            CREATE_NEW_CONSOLE,
            null_mut(),
            null_mut(),
            &mut startup_info,
            &mut process_info,
        )
    };
    if result == 0 {
        println!("Failed to create process");
    } else {
        println!("Process created successfully");
    }
}

上述代码中,首先使用 CString 将字符串转换为 C 类型的字符串,然后使用 CreateProcessA 函数创建进程。CreateProcessA 函数的第一个参数是可执行文件的路径,第二个参数是命令行参数,第三个参数是进程的安全属性,第四个参数是主线程的安全属性,第五个参数是是否继承句柄,第六个参数是创建进程的标志,第七个参数是环境变量,第八个参数是当前目录,第九个参数是启动信息,第十个参数是进程信息。在这个示例中,使用了 null_mut()表示不设置进程和主线程的安全属性,使用了 CREATE_NEW_CONSOLE 表示创建一个新的控制台窗口。如果 CreateProcessA 函数返回 0,则说明创建进程失败。否则,创建进程成功。

创建线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::ptr::null_mut;
use std::thread;
use winapi::um::processthreadsapi::{CreateThread, GetCurrentThreadId, GetExitCodeThread, TerminateThread};
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::{INFINITE, WAIT_OBJECT_0};

fn thread_proc() -> u32 {
    println!("Thread ID: {}", unsafe { GetCurrentThreadId() });
    0
}

fn main() {
    let handle = unsafe { CreateThread(null_mut(), 0, Some(thread_proc), null_mut(), 0, null_mut()) };
    let ret = unsafe { WaitForSingleObject(handle, INFINITE) };
    if ret == WAIT_OBJECT_0 {
        let mut exit_code = 0;
        unsafe { GetExitCodeThread(handle, &mut exit_code) };
        println!("Exit code: {}", exit_code);
    }
    unsafe { TerminateThread(handle, 0) };
}

创建共享内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use std::ptr::null_mut;
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::memoryapi::{MapViewOfFile, UnmapViewOfFile, CreateFileMappingW, FILE_MAP_ALL_ACCESS};
use winapi::um::winnt::{HANDLE, PAGE_READWRITE, SECTION_ALL_ACCESS};

fn main() {
    let file_name = "Global\\MySharedMemory".to_wide_null();
    let file_size = 4096;
    let h_file_mapping = unsafe {
        CreateFileMappingW(
            INVALID_HANDLE_VALUE,
            null_mut(),
            PAGE_READWRITE,
            0,
            file_size,
            file_name.as_ptr(),
        )
    };
    let mut p_shared_memory = null_mut();
    if !h_file_mapping.is_null() {
        p_shared_memory = unsafe {
            MapViewOfFile(
                h_file_mapping,
                FILE_MAP_ALL_ACCESS,
                0,
                0,
                file_size,
            )
        };
    }
    if !p_shared_memory.is_null() {
        let p_data = p_shared_memory as *mut u8;
        for i in 0..file_size {
            unsafe { *p_data.offset(i as isize) = i as u8 };
        }
        unsafe { UnmapViewOfFile(p_shared_memory) };
    }
    if !h_file_mapping.is_null() {
        unsafe { CloseHandle(h_file_mapping) };
    }
}

获取系统时间

获取系统时间是一个常见的操作,可以用于记录日志或计算时间差等。下面是一个使用 winapi 模块获取系统时间的示例:

1
2
3
4
5
6
7
8
9
10
use winapi::um::sysinfoapi::GetSystemTime;
use winapi::um::winbase::SYSTEMTIME;

fn main() {
    unsafe {
        let mut st: SYSTEMTIME = std::mem::zeroed();
        GetSystemTime(&mut st as *mut SYSTEMTIME);
        println!("{}-{}-{} {}:{}:{}", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
    }
}

在这个示例中,我们使用了 sysinfoapi 模块中的 GetSystemTime 函数来获取系统时间。该函数的参数为一个 SYSTEMTIME 结构体指针,用于存储系统时间。我们使用 std::mem::zeroed()创建了一个空的 SYSTEMTIME 结构体,然后将其地址传递给 GetSystemTime 函数。最后,我们将获取到的系统时间打印出来。

使用 COM 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::ptr::null_mut;
use winapi::um::combaseapi::{CoInitializeEx, CoCreateInstance, CoUninitialize};
use winapi::um::objbase::COINIT_APARTMENTTHREADED;
use winapi::um::objidl::ISequentialStream;
use winapi::um::unknwnbase::IUnknown;
use winapi::um::winnt::{HRESULT, S_OK};
use winapi::um::winuser::MessageBoxW;
use winapi::Interface;

const CLSID_XMLHTTPREQUEST: winapi::CLSID = winapi::CLSID {
    Data1: 0xED8C108E,
    Data2: 0x4349,
    Data3: 0x11D2,
    Data4: [0x91, 0xA4, 0x00, 0xC0, 0x4F, 0x79, 0xF8, 0x06],
};

const IID_IUNKNOWN: winapi::IID = winapi::IID {
    Data1: 0x00000000,
    Data2: 0x0000,
    Data3: 0x0000,
    Data4: [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46],
};

fn main() {
    unsafe {
        CoInitializeEx(null_mut(), COINIT_APARTMENTTHREADED);
        let mut p_xml_http_request: *mut IUnknown = null_mut();
        let hr = CoCreateInstance(
            &CLSID_XMLHTTPREQUEST,
            null_mut(),
            winapi::um::combaseapi::CLSCTX_INPROC_SERVER,
            &IID_IUNKNOWN,
            &mut p_xml_http_request as *mut _ as *mut *mut _,
        );
        if hr == S_OK {
            let mut p_stream: *mut ISequentialStream = null_mut();
            let hr = (*p_xml_http_request).QueryInterface(
                &ISequentialStream::uuidof(),
                &mut p_stream as *mut _ as *mut *mut _,
            );
            if hr == S_OK {
                let message = "Hello, world!".to_wide_null();
                let mut bytes_written = 0;
                let hr = (*p_stream).Write(
                    message.as_ptr() as *const _,
                    (message.len() * 2) as u32,
                    &mut bytes_written as *mut _,
                );
                if hr == S_OK {
                    MessageBoxW(
                        null_mut(),
                        "Data written successfully!".to_wide_null().as_ptr(),
                        "Success\0".to_wide_null().as_ptr(),
                        0,
                    );
                }
            }
            (*p_stream).Release();
        }
        (*p_xml_http_request).Release();
        CoUninitialize();
    }
}

结论

本教程介绍了 winapi 模块的基础用法和进阶用法,并提供了示例代码。在使用 winapi 模块时,需要注意数据类型、结构体、常量和指针类型的正确使用。通过使用 winapi 模块,Rust 程序可以访问 Windows 操作系统的核心功能,实现更加丰富的功能。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 TinyZ Zzh (包含链接: https://tinyzzh.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。 如有任何疑问,请 与我联系 (tinyzzh815@gmail.com)

TinyZ Zzh

TinyZ Zzh

专注于高并发服务器、网络游戏相关(Java、PHP、Unity3D、Unreal Engine等)技术,热爱游戏事业, 正在努力实现自我价值当中。

评论

  点击开始评论...