Add missing _winapi functions (#7185)

* Add missing _winapi functions and fix WinHandle int conversion

Add 13 functions: ReadFile, SetNamedPipeHandleState, CreateFileMapping,
OpenFileMapping, MapViewOfFile, UnmapViewOfFile, VirtualQuerySize,
CopyFile2, ResetEvent, CreateMutex, OpenEventW, LoadLibrary,
_mimetypes_read_windows_registry.

Add constants: INVALID_HANDLE_VALUE, FILE_MAP_READ/WRITE/COPY/EXECUTE.

Change WinHandle integer type from usize to isize so negative values
like INVALID_HANDLE_VALUE (-1) can be passed from Python.

* Align _winapi module with CPython

- Rename winapi.rs to _winapi.rs with #[path] attribute
- Rename CreateMutex to CreateMutexW
- Add missing constants: ERROR_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD,
  PROCESS_ALL_ACCESS, 10 STARTF_ constants, LOCALE_NAME_SYSTEM_DEFAULT,
  LOCALE_NAME_USER_DEFAULT, COPY_FILE_DIRECTORY
- Fix OpenMutexW return type and ReleaseMutex param type to use WinHandle

* Fix ReadFile/WriteFile overlapped keyword argument

Use FromArgs structs so overlapped parameter can be passed as
a keyword argument (overlapped=True), matching the CPython API.

* Remove extra constants and LoadLibrary not in CPython _winapi

Remove 19 constants (WAIT_ABANDONED, CREATE_ALWAYS, CREATE_NEW,
OPEN_ALWAYS, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 8 FILE_FLAG_*,
3 FILE_SHARE_*, NMPWAIT_NOWAIT, NMPWAIT_USE_DEFAULT_WAIT) and
LoadLibrary function that are not present in CPython's _winapi module.

* Fix utf8_mode default to 0 and add PYTHONUTF8 env var support

Default utf8_mode was incorrectly set to 1, causing text-mode
subprocess to always decode as UTF-8 instead of locale encoding.
Changed default to 0 to match CPython 3.13 behavior on Windows.
Added PYTHONUTF8 environment variable handling with -X utf8 override.

* Fix CopyFile2 to raise proper OSError subclass

Use std::io::Error::from_raw_os_error instead of vm.new_os_error so
that winerror attribute is set and errno-to-exception mapping works
(e.g. ERROR_ACCESS_DENIED → PermissionError).

* Fix syntax_non_utf8 test to not depend on locale encoding

Use explicit encoding='latin-1' so the test works regardless of
the system locale (e.g. C/POSIX locale uses ASCII by default).
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-02-22 19:54:27 +09:00
1 parent 2458e34091
commit ab2876f33c
8 files changed
+554 -108

No files matched your search

+1
View File
@@ -147,6 +147,7 @@
// "stat"
"FIRMLINK",
// CPython internal names
"PYTHONUTF",
"sysdict",
"settraceallthreads",
"setprofileallthreads"
@@ -15,16 +15,17 @@ mod _winapi {
windows::{WinHandle, WindowsSysResult},
};
use core::ptr::{null, null_mut};
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE, MAX_PATH};
use windows_sys::Win32::Foundation::{HANDLE, MAX_PATH};
#[pyattr]
use windows_sys::Win32::{
Foundation::{
DUPLICATE_CLOSE_SOURCE, DUPLICATE_SAME_ACCESS, ERROR_ALREADY_EXISTS, ERROR_BROKEN_PIPE,
ERROR_IO_PENDING, ERROR_MORE_DATA, ERROR_NETNAME_DELETED, ERROR_NO_DATA,
ERROR_NO_SYSTEM_RESOURCES, ERROR_OPERATION_ABORTED, ERROR_PIPE_BUSY,
ERROR_PIPE_CONNECTED, ERROR_SEM_TIMEOUT, GENERIC_READ, GENERIC_WRITE, STILL_ACTIVE,
WAIT_ABANDONED, WAIT_ABANDONED_0, WAIT_OBJECT_0, WAIT_TIMEOUT,
DUPLICATE_CLOSE_SOURCE, DUPLICATE_SAME_ACCESS, ERROR_ACCESS_DENIED,
ERROR_ALREADY_EXISTS, ERROR_BROKEN_PIPE, ERROR_IO_PENDING, ERROR_MORE_DATA,
ERROR_NETNAME_DELETED, ERROR_NO_DATA, ERROR_NO_SYSTEM_RESOURCES,
ERROR_OPERATION_ABORTED, ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED,
ERROR_PRIVILEGE_NOT_HELD, ERROR_SEM_TIMEOUT, GENERIC_READ, GENERIC_WRITE, STILL_ACTIVE,
WAIT_ABANDONED_0, WAIT_OBJECT_0, WAIT_TIMEOUT,
},
Globalization::{
LCMAP_FULLWIDTH, LCMAP_HALFWIDTH, LCMAP_HIRAGANA, LCMAP_KATAKANA,
@@ -32,79 +33,45 @@ mod _winapi {
LCMAP_TRADITIONAL_CHINESE, LCMAP_UPPERCASE,
},
Storage::FileSystem::{
COPY_FILE_ALLOW_DECRYPTED_DESTINATION,
COPY_FILE_COPY_SYMLINK,
COPY_FILE_FAIL_IF_EXISTS,
COPY_FILE_NO_BUFFERING,
COPY_FILE_NO_OFFLOAD,
COPY_FILE_OPEN_SOURCE_FOR_WRITE,
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC,
COPY_FILE_REQUEST_SECURITY_PRIVILEGES,
COPY_FILE_RESTARTABLE,
COPY_FILE_RESUME_FROM_PAUSE,
COPYFILE2_CALLBACK_CHUNK_FINISHED,
COPYFILE2_CALLBACK_CHUNK_STARTED,
COPYFILE2_CALLBACK_ERROR,
COPYFILE2_CALLBACK_POLL_CONTINUE,
COPYFILE2_CALLBACK_STREAM_FINISHED,
COPYFILE2_CALLBACK_STREAM_STARTED,
COPYFILE2_PROGRESS_CANCEL,
COPYFILE2_PROGRESS_CONTINUE,
COPYFILE2_PROGRESS_PAUSE,
COPYFILE2_PROGRESS_QUIET,
COPYFILE2_PROGRESS_STOP,
CREATE_ALWAYS,
// CreateFile constants
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_DELETE_ON_CLOSE,
FILE_FLAG_FIRST_PIPE_INSTANCE,
FILE_FLAG_NO_BUFFERING,
FILE_FLAG_OPEN_REPARSE_POINT,
FILE_FLAG_OVERLAPPED,
FILE_FLAG_POSIX_SEMANTICS,
FILE_FLAG_RANDOM_ACCESS,
FILE_FLAG_SEQUENTIAL_SCAN,
FILE_FLAG_WRITE_THROUGH,
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_SHARE_DELETE,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
FILE_TYPE_CHAR,
FILE_TYPE_DISK,
FILE_TYPE_PIPE,
FILE_TYPE_REMOTE,
FILE_TYPE_UNKNOWN,
OPEN_ALWAYS,
OPEN_EXISTING,
PIPE_ACCESS_DUPLEX,
PIPE_ACCESS_INBOUND,
SYNCHRONIZE,
TRUNCATE_EXISTING,
COPY_FILE_ALLOW_DECRYPTED_DESTINATION, COPY_FILE_COPY_SYMLINK,
COPY_FILE_FAIL_IF_EXISTS, COPY_FILE_NO_BUFFERING, COPY_FILE_NO_OFFLOAD,
COPY_FILE_OPEN_SOURCE_FOR_WRITE, COPY_FILE_REQUEST_COMPRESSED_TRAFFIC,
COPY_FILE_REQUEST_SECURITY_PRIVILEGES, COPY_FILE_RESTARTABLE,
COPY_FILE_RESUME_FROM_PAUSE, COPYFILE2_CALLBACK_CHUNK_FINISHED,
COPYFILE2_CALLBACK_CHUNK_STARTED, COPYFILE2_CALLBACK_ERROR,
COPYFILE2_CALLBACK_POLL_CONTINUE, COPYFILE2_CALLBACK_STREAM_FINISHED,
COPYFILE2_CALLBACK_STREAM_STARTED, COPYFILE2_PROGRESS_CANCEL,
COPYFILE2_PROGRESS_CONTINUE, COPYFILE2_PROGRESS_PAUSE, COPYFILE2_PROGRESS_QUIET,
COPYFILE2_PROGRESS_STOP, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED,
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE,
FILE_TYPE_REMOTE, FILE_TYPE_UNKNOWN, OPEN_EXISTING, PIPE_ACCESS_DUPLEX,
PIPE_ACCESS_INBOUND, SYNCHRONIZE,
},
System::{
Console::{STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE},
Memory::{
FILE_MAP_ALL_ACCESS, MEM_COMMIT, MEM_FREE, MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE,
FILE_MAP_ALL_ACCESS, FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ,
FILE_MAP_WRITE, MEM_COMMIT, MEM_FREE, MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE,
MEM_RESERVE, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,
PAGE_EXECUTE_WRITECOPY, PAGE_GUARD, PAGE_NOACCESS, PAGE_NOCACHE, PAGE_READONLY,
PAGE_READWRITE, PAGE_WRITECOMBINE, PAGE_WRITECOPY, SEC_COMMIT, SEC_IMAGE,
SEC_LARGE_PAGES, SEC_NOCACHE, SEC_RESERVE, SEC_WRITECOMBINE,
},
Pipes::{
NMPWAIT_NOWAIT, NMPWAIT_USE_DEFAULT_WAIT, NMPWAIT_WAIT_FOREVER,
PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT,
NMPWAIT_WAIT_FOREVER, PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE,
PIPE_UNLIMITED_INSTANCES, PIPE_WAIT,
},
SystemServices::LOCALE_NAME_MAX_LENGTH,
Threading::{
ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS,
CREATE_BREAKAWAY_FROM_JOB, CREATE_DEFAULT_ERROR_MODE, CREATE_NEW_CONSOLE,
CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW, DETACHED_PROCESS, HIGH_PRIORITY_CLASS,
IDLE_PRIORITY_CLASS, INFINITE, NORMAL_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
REALTIME_PRIORITY_CLASS, STARTF_FORCEOFFFEEDBACK, STARTF_FORCEONFEEDBACK,
STARTF_USESHOWWINDOW, STARTF_USESTDHANDLES,
IDLE_PRIORITY_CLASS, INFINITE, NORMAL_PRIORITY_CLASS, PROCESS_ALL_ACCESS,
PROCESS_DUP_HANDLE, REALTIME_PRIORITY_CLASS, STARTF_FORCEOFFFEEDBACK,
STARTF_FORCEONFEEDBACK, STARTF_PREVENTPINNING, STARTF_RUNFULLSCREEN,
STARTF_TITLEISAPPID, STARTF_TITLEISLINKNAME, STARTF_UNTRUSTEDSOURCE,
STARTF_USECOUNTCHARS, STARTF_USEFILLATTRIBUTE, STARTF_USEHOTKEY,
STARTF_USEPOSITION, STARTF_USESHOWWINDOW, STARTF_USESIZE, STARTF_USESTDHANDLES,
},
},
UI::WindowsAndMessaging::SW_HIDE,
@@ -113,6 +80,12 @@ mod _winapi {
#[pyattr]
const NULL: isize = 0;
#[pyattr]
const INVALID_HANDLE_VALUE: isize = -1;
#[pyattr]
const COPY_FILE_DIRECTORY: u32 = 0x00000080;
#[pyfunction]
fn CloseHandle(handle: WinHandle) -> WindowsSysResult<i32> {
WindowsSysResult(unsafe { windows_sys::Win32::Foundation::CloseHandle(handle.0) })
@@ -150,7 +123,7 @@ mod _winapi {
)
};
if handle == INVALID_HANDLE_VALUE {
if handle == windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}
@@ -163,7 +136,7 @@ mod _winapi {
vm: &VirtualMachine,
) -> PyResult<Option<WinHandle>> {
let handle = unsafe { windows_sys::Win32::System::Console::GetStdHandle(std_handle) };
if handle == INVALID_HANDLE_VALUE {
if handle == windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}
Ok(if handle.is_null() {
@@ -306,9 +279,9 @@ mod _winapi {
}
si_attr!(dwFlags);
si_attr!(wShowWindow);
si_attr!(hStdInput, usize);
si_attr!(hStdOutput, usize);
si_attr!(hStdError, usize);
si_attr!(hStdInput, isize);
si_attr!(hStdOutput, isize);
si_attr!(hStdError, isize);
let mut env = args
.env_mapping
@@ -638,18 +611,6 @@ mod _winapi {
})
}
// TODO: ctypes.LibraryLoader.LoadLibrary
#[allow(dead_code)]
fn LoadLibrary(path: PyStrRef, vm: &VirtualMachine) -> PyResult<isize> {
let path_wide = path.as_wtf8().to_wide_with_nul();
let handle =
unsafe { windows_sys::Win32::System::LibraryLoader::LoadLibraryW(path_wide.as_ptr()) };
if handle.is_null() {
return Err(vm.new_runtime_error("LoadLibrary failed"));
}
Ok(handle as isize)
}
#[pyfunction]
fn GetModuleFileName(handle: isize, vm: &VirtualMachine) -> PyResult<String> {
let mut path: Vec<u16> = vec![0; MAX_PATH as usize];
@@ -675,7 +636,7 @@ mod _winapi {
inherit_handle: bool,
name: PyStrRef,
vm: &VirtualMachine,
) -> PyResult<isize> {
) -> PyResult<WinHandle> {
let name_wide = name.as_wtf8().to_wide_with_nul();
let handle = unsafe {
windows_sys::Win32::System::Threading::OpenMutexW(
@@ -684,23 +645,29 @@ mod _winapi {
name_wide.as_ptr(),
)
};
if handle == INVALID_HANDLE_VALUE {
if handle.is_null() {
return Err(vm.new_last_os_error());
}
Ok(handle as _)
Ok(WinHandle(handle))
}
#[pyfunction]
fn ReleaseMutex(handle: isize) -> WindowsSysResult<i32> {
WindowsSysResult(unsafe {
windows_sys::Win32::System::Threading::ReleaseMutex(handle as _)
})
fn ReleaseMutex(handle: WinHandle) -> WindowsSysResult<i32> {
WindowsSysResult(unsafe { windows_sys::Win32::System::Threading::ReleaseMutex(handle.0) })
}
// LOCALE_NAME_INVARIANT is an empty string in Windows API
#[pyattr]
const LOCALE_NAME_INVARIANT: &str = "";
#[pyattr]
const LOCALE_NAME_SYSTEM_DEFAULT: &str = "!x-sys-default-locale";
#[pyattr(name = "LOCALE_NAME_USER_DEFAULT")]
fn locale_name_user_default(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.none()
}
/// LCMapStringEx - Map a string to another string using locale-specific rules
/// This is used by ntpath.normcase() for proper Windows case conversion
#[pyfunction]
@@ -815,7 +782,7 @@ mod _winapi {
)
};
if handle == INVALID_HANDLE_VALUE {
if handle == windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}
@@ -1193,7 +1160,7 @@ mod _winapi {
initial_state: bool,
name: Option<PyStrRef>,
vm: &VirtualMachine,
) -> PyResult<Option<WinHandle>> {
) -> PyResult<WinHandle> {
use windows_sys::Win32::System::Threading::CreateEventW as WinCreateEventW;
let _ = security_attributes; // Ignored, always NULL
@@ -1210,15 +1177,11 @@ mod _winapi {
)
};
if handle == INVALID_HANDLE_VALUE {
if handle.is_null() {
return Err(vm.new_last_os_error());
}
if handle.is_null() {
return Ok(None);
}
Ok(Some(WinHandle(handle)))
Ok(WinHandle(handle))
}
/// SetEvent - Set the specified event object to the signaled state.
@@ -1235,18 +1198,24 @@ mod _winapi {
Ok(())
}
#[derive(FromArgs)]
struct WriteFileArgs {
#[pyarg(positional)]
handle: WinHandle,
#[pyarg(positional)]
buffer: crate::function::ArgBytesLike,
#[pyarg(named, default = false)]
overlapped: bool,
}
/// WriteFile - Write data to a file or I/O device.
#[pyfunction]
fn WriteFile(
handle: WinHandle,
buffer: crate::function::ArgBytesLike,
use_overlapped: OptionalArg<bool>,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
fn WriteFile(args: WriteFileArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
use windows_sys::Win32::Storage::FileSystem::WriteFile as WinWriteFile;
let use_overlapped = use_overlapped.unwrap_or(false);
let buf = buffer.borrow_buf();
let handle = args.handle;
let use_overlapped = args.overlapped;
let buf = args.buffer.borrow_buf();
let len = core::cmp::min(buf.len(), u32::MAX as usize) as u32;
if use_overlapped {
@@ -1316,6 +1285,184 @@ mod _winapi {
.into())
}
#[derive(FromArgs)]
struct ReadFileArgs {
#[pyarg(positional)]
handle: WinHandle,
#[pyarg(positional)]
size: u32,
#[pyarg(named, default = false)]
overlapped: bool,
}
/// ReadFile - Read data from a file or I/O device.
#[pyfunction]
fn ReadFile(args: ReadFileArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
use windows_sys::Win32::Storage::FileSystem::ReadFile as WinReadFile;
let handle = args.handle;
let size = args.size;
let use_overlapped = args.overlapped;
if use_overlapped {
use windows_sys::Win32::Foundation::ERROR_IO_PENDING;
let ov = Overlapped::new_with_handle(handle.0);
let err = {
let mut inner = ov.inner.lock();
inner.read_buffer = Some(vec![0u8; size as usize]);
let read_buf = inner.read_buffer.as_mut().unwrap();
let mut nread: u32 = 0;
let ret = unsafe {
WinReadFile(
handle.0,
read_buf.as_mut_ptr() as *mut _,
size,
&mut nread,
&mut inner.overlapped,
)
};
let err = if ret == 0 {
unsafe { windows_sys::Win32::Foundation::GetLastError() }
} else {
0
};
if ret == 0 && err != ERROR_IO_PENDING && err != ERROR_MORE_DATA {
return Err(vm.new_last_os_error());
}
if ret == 0 && err == ERROR_IO_PENDING {
inner.pending = true;
}
err
};
let result = vm
.ctx
.new_tuple(vec![ov.into_pyobject(vm), vm.ctx.new_int(err).into()]);
return Ok(result.into());
}
let mut buf = vec![0u8; size as usize];
let mut nread: u32 = 0;
let ret = unsafe {
WinReadFile(
handle.0,
buf.as_mut_ptr() as *mut _,
size,
&mut nread,
null_mut(),
)
};
let err = if ret == 0 {
unsafe { windows_sys::Win32::Foundation::GetLastError() }
} else {
0
};
if ret == 0 && err != ERROR_MORE_DATA {
return Err(vm.new_last_os_error());
}
buf.truncate(nread as usize);
Ok(vm
.ctx
.new_tuple(vec![
vm.ctx.new_bytes(buf).into(),
vm.ctx.new_int(err).into(),
])
.into())
}
/// SetNamedPipeHandleState - Set the read mode and other options of a named pipe.
#[pyfunction]
fn SetNamedPipeHandleState(
named_pipe: WinHandle,
mode: PyObjectRef,
max_collection_count: PyObjectRef,
collect_data_timeout: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
use windows_sys::Win32::System::Pipes::SetNamedPipeHandleState as WinSetNamedPipeHandleState;
let mut dw_args: [u32; 3] = [0; 3];
let mut p_args: [*mut u32; 3] = [null_mut(); 3];
let objs = [&mode, &max_collection_count, &collect_data_timeout];
for (i, obj) in objs.iter().enumerate() {
if !vm.is_none(obj) {
dw_args[i] = u32::try_from_object(vm, (*obj).clone())?;
p_args[i] = &mut dw_args[i];
}
}
let ret =
unsafe { WinSetNamedPipeHandleState(named_pipe.0, p_args[0], p_args[1], p_args[2]) };
if ret == 0 {
return Err(vm.new_last_os_error());
}
Ok(())
}
/// ResetEvent - Reset the specified event object to the nonsignaled state.
#[pyfunction]
fn ResetEvent(event: WinHandle, vm: &VirtualMachine) -> PyResult<()> {
use windows_sys::Win32::System::Threading::ResetEvent as WinResetEvent;
let ret = unsafe { WinResetEvent(event.0) };
if ret == 0 {
return Err(vm.new_last_os_error());
}
Ok(())
}
/// CreateMutexW - Create or open a named or unnamed mutex object.
#[pyfunction]
fn CreateMutexW(
security_attributes: isize,
initial_owner: bool,
name: Option<PyStrRef>,
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::System::Threading::CreateMutexW as WinCreateMutexW;
let _ = security_attributes;
let name_wide = name.map(|n| n.as_wtf8().to_wide_with_nul());
let name_ptr = name_wide.as_ref().map_or(null(), |n| n.as_ptr());
let handle = unsafe { WinCreateMutexW(null(), i32::from(initial_owner), name_ptr) };
if handle.is_null() {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
/// OpenEventW - Open an existing named event object.
#[pyfunction]
fn OpenEventW(
desired_access: u32,
inherit_handle: bool,
name: PyStrRef,
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::System::Threading::OpenEventW as WinOpenEventW;
let name_wide = name.as_wtf8().to_wide_with_nul();
let handle = unsafe {
WinOpenEventW(
desired_access,
i32::from(inherit_handle),
name_wide.as_ptr(),
)
};
if handle.is_null() {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
const MAXIMUM_WAIT_OBJECTS: usize = 64;
/// BatchedWaitForMultipleObjects - Wait for multiple handles, supporting more than 64.
@@ -1642,4 +1789,272 @@ mod _winapi {
Ok(vm.ctx.new_list(triggered_indices).into())
}
}
/// CreateFileMapping - Create or open a named or unnamed file mapping object.
#[pyfunction]
fn CreateFileMapping(
file_handle: WinHandle,
_security_attributes: PyObjectRef,
protect: u32,
max_size_high: u32,
max_size_low: u32,
name: Option<PyStrRef>,
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::System::Memory::CreateFileMappingW;
let name_wide = name.as_ref().map(|n| n.as_wtf8().to_wide_with_nul());
let name_ptr = name_wide.as_ref().map_or(null(), |n| n.as_ptr());
let handle = unsafe {
CreateFileMappingW(
file_handle.0,
null(),
protect,
max_size_high,
max_size_low,
name_ptr,
)
};
if handle.is_null() {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
/// OpenFileMapping - Open a named file mapping object.
#[pyfunction]
fn OpenFileMapping(
desired_access: u32,
inherit_handle: bool,
name: PyStrRef,
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::System::Memory::OpenFileMappingW;
let name_wide = name.as_wtf8().to_wide_with_nul();
let handle = unsafe {
OpenFileMappingW(
desired_access,
i32::from(inherit_handle),
name_wide.as_ptr(),
)
};
if handle.is_null() {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
/// MapViewOfFile - Map a view of a file mapping into the address space.
#[pyfunction]
fn MapViewOfFile(
file_map: WinHandle,
desired_access: u32,
file_offset_high: u32,
file_offset_low: u32,
number_bytes: usize,
vm: &VirtualMachine,
) -> PyResult<isize> {
let address = unsafe {
windows_sys::Win32::System::Memory::MapViewOfFile(
file_map.0,
desired_access,
file_offset_high,
file_offset_low,
number_bytes,
)
};
let ptr = address.Value;
if ptr.is_null() {
return Err(vm.new_last_os_error());
}
Ok(ptr as isize)
}
/// UnmapViewOfFile - Unmap a mapped view of a file.
#[pyfunction]
fn UnmapViewOfFile(address: isize, vm: &VirtualMachine) -> PyResult<()> {
use windows_sys::Win32::System::Memory::MEMORY_MAPPED_VIEW_ADDRESS;
let view = MEMORY_MAPPED_VIEW_ADDRESS {
Value: address as *mut core::ffi::c_void,
};
let ret = unsafe { windows_sys::Win32::System::Memory::UnmapViewOfFile(view) };
if ret == 0 {
return Err(vm.new_last_os_error());
}
Ok(())
}
/// VirtualQuerySize - Return the size of a memory region.
#[pyfunction]
fn VirtualQuerySize(address: isize, vm: &VirtualMachine) -> PyResult<usize> {
use windows_sys::Win32::System::Memory::{MEMORY_BASIC_INFORMATION, VirtualQuery};
let mut mbi: MEMORY_BASIC_INFORMATION = unsafe { core::mem::zeroed() };
let ret = unsafe {
VirtualQuery(
address as *const core::ffi::c_void,
&mut mbi,
core::mem::size_of::<MEMORY_BASIC_INFORMATION>(),
)
};
if ret == 0 {
return Err(vm.new_last_os_error());
}
Ok(mbi.RegionSize)
}
/// CopyFile2 - Copy a file with extended parameters.
#[pyfunction]
fn CopyFile2(
existing_file_name: PyStrRef,
new_file_name: PyStrRef,
flags: u32,
_progress_routine: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
use windows_sys::Win32::Storage::FileSystem::{
COPYFILE2_EXTENDED_PARAMETERS, CopyFile2 as WinCopyFile2,
};
let src_wide = existing_file_name.as_wtf8().to_wide_with_nul();
let dst_wide = new_file_name.as_wtf8().to_wide_with_nul();
let mut params: COPYFILE2_EXTENDED_PARAMETERS = unsafe { core::mem::zeroed() };
params.dwSize = core::mem::size_of::<COPYFILE2_EXTENDED_PARAMETERS>() as u32;
params.dwCopyFlags = flags;
let hr = unsafe { WinCopyFile2(src_wide.as_ptr(), dst_wide.as_ptr(), &params) };
if hr < 0 {
// HRESULT failure - convert to Windows error code
let err = if (hr as u32 >> 16) == 0x8007 {
(hr as u32) & 0xFFFF
} else {
hr as u32
};
return Err(std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm));
}
Ok(())
}
/// _mimetypes_read_windows_registry - Read MIME type associations from registry.
#[pyfunction]
fn _mimetypes_read_windows_registry(
on_type_read: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
use windows_sys::Win32::System::Registry::{
HKEY, HKEY_CLASSES_ROOT, KEY_READ, REG_SZ, RegCloseKey, RegEnumKeyExW, RegOpenKeyExW,
RegQueryValueExW,
};
let mut hkcr: HKEY = null_mut() as HKEY;
let err = unsafe { RegOpenKeyExW(HKEY_CLASSES_ROOT, null(), 0, KEY_READ, &mut hkcr) };
if err != 0 {
return Err(vm.new_os_error(err as i32));
}
scopeguard::defer! { unsafe { RegCloseKey(hkcr) }; }
let mut i: u32 = 0;
let mut entries: Vec<(String, String)> = Vec::new();
loop {
let mut ext_buf = [0u16; 128];
let mut cch_ext: u32 = ext_buf.len() as u32;
let err = unsafe {
RegEnumKeyExW(
hkcr,
i,
ext_buf.as_mut_ptr(),
&mut cch_ext,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
)
};
i += 1;
if err == windows_sys::Win32::Foundation::ERROR_NO_MORE_ITEMS {
break;
}
if err != 0 && err != windows_sys::Win32::Foundation::ERROR_MORE_DATA {
return Err(vm.new_os_error(err as i32));
}
// Only process keys starting with '.'
if cch_ext == 0 || ext_buf[0] != b'.' as u16 {
continue;
}
let ext_wide = &ext_buf[..cch_ext as usize];
// Open subkey to read Content Type
let mut subkey: HKEY = null_mut() as HKEY;
let err = unsafe { RegOpenKeyExW(hkcr, ext_buf.as_ptr(), 0, KEY_READ, &mut subkey) };
if err == windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND
|| err == windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED
{
continue;
}
if err != 0 {
return Err(vm.new_os_error(err as i32));
}
let content_type_key: Vec<u16> = "Content Type\0".encode_utf16().collect();
let mut type_buf = [0u16; 256];
let mut cb_type: u32 = (type_buf.len() * 2) as u32;
let mut reg_type: u32 = 0;
let err = unsafe {
RegQueryValueExW(
subkey,
content_type_key.as_ptr(),
null_mut(),
&mut reg_type,
type_buf.as_mut_ptr() as *mut u8,
&mut cb_type,
)
};
unsafe { RegCloseKey(subkey) };
if err != 0 || reg_type != REG_SZ || cb_type == 0 {
continue;
}
// Convert wide strings to Rust strings
let type_len = (cb_type as usize / 2).saturating_sub(1); // exclude null terminator
let type_str = String::from_utf16_lossy(&type_buf[..type_len]);
let ext_str = String::from_utf16_lossy(ext_wide);
if type_str.is_empty() {
continue;
}
entries.push((type_str, ext_str));
// Flush buffer periodically to call Python callback
if entries.len() >= 64 {
for (mime_type, ext) in entries.drain(..) {
on_type_read.call((vm.ctx.new_str(mime_type), vm.ctx.new_str(ext)), vm)?;
}
}
}
// Process remaining entries
for (mime_type, ext) in entries {
on_type_read.call((vm.ctx.new_str(mime_type), vm.ctx.new_str(ext)), vm)?;
}
Ok(())
}
}
+1
View File
@@ -64,6 +64,7 @@ mod _wmi;
pub(crate) mod signal;
pub mod sys;
#[cfg(all(feature = "host_env", windows))]
#[path = "_winapi.rs"]
mod winapi;
#[cfg(all(feature = "host_env", windows))]
mod winreg;
+5 -1
View File
@@ -1584,7 +1584,11 @@ mod sys {
hash_randomization: settings.hash_seed.is_none() as u8,
isolated: settings.isolated as u8,
dev_mode: settings.dev_mode,
utf8_mode: settings.utf8_mode,
utf8_mode: if settings.utf8_mode < 0 {
1
} else {
settings.utf8_mode as u8
},
int_max_str_digits: settings.int_max_str_digits,
safe_path: settings.safe_path,
warn_default_encoding: settings.warn_default_encoding as u8,
+2 -2
View File
@@ -124,7 +124,7 @@ pub struct Settings {
pub stdio_encoding: Option<String>,
/// PYTHONIOENCODING - stdio error handler
pub stdio_errors: Option<String>,
pub utf8_mode: u8,
pub utf8_mode: i8,
/// --check-hash-based-pycs
pub check_hash_pycs_mode: CheckHashPycsMode,
@@ -211,7 +211,7 @@ impl Default for Settings {
allow_external_library: cfg!(feature = "importlib"),
stdio_encoding: None,
stdio_errors: None,
utf8_mode: 1,
utf8_mode: -1,
int_max_str_digits: 4300,
#[cfg(feature = "flame-it")]
profile_output: None,
+1 -1
View File
@@ -61,7 +61,7 @@ impl<T: WindowsSysResultValue> ToPyResult for WindowsSysResult<T> {
}
}
type HandleInt = usize; // TODO: change to isize when fully ported to windows-rs
type HandleInt = isize;
impl TryFromObject for WinHandle {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
+1 -1
View File
@@ -5,5 +5,5 @@ from testutils import assert_raises
dir_path = os.path.dirname(os.path.realpath(__file__))
with assert_raises(SyntaxError):
with open(os.path.join(dir_path , "non_utf8.txt")) as f:
with open(os.path.join(dir_path , "non_utf8.txt"), encoding="latin-1") as f:
eval(f.read())
+25
View File
@@ -260,6 +260,24 @@ pub fn parse_opts() -> Result<(Settings, RunMode), lexopt::Error> {
settings.check_hash_pycs_mode = args.check_hash_based_pycs;
if let Some(val) = get_env("PYTHONUTF8")
&& let Some(val_str) = val.to_str()
&& !val_str.is_empty()
{
settings.utf8_mode = match val_str {
"1" => 1,
"0" => 0,
_ => {
error!(
"Fatal Python error: config_init_utf8_mode: \
PYTHONUTF8=N: N is missing or invalid\n\
Python runtime state: preinitialized"
);
std::process::exit(1);
}
};
}
let xopts = args.implementation_option.into_iter().map(|s| {
let (name, value) = match s.split_once('=') {
Some((name, value)) => (name.to_owned(), Some(value)),
@@ -320,6 +338,13 @@ pub fn parse_opts() -> Result<(Settings, RunMode), lexopt::Error> {
});
settings.xoptions.extend(xopts);
// Resolve utf8_mode if not explicitly set by PYTHONUTF8 or -X utf8.
// Default to UTF-8 mode since RustPython's locale encoding detection
// is incomplete. Users can set PYTHONUTF8=0 or -X utf8=0 to disable.
if settings.utf8_mode < 0 {
settings.utf8_mode = 1;
}
settings.warn_default_encoding =
settings.warn_default_encoding || env_bool("PYTHONWARNDEFAULTENCODING");
settings.faulthandler = settings.faulthandler || env_bool("PYTHONFAULTHANDLER");