Convert CFRunLoopTimer to Windows API - winapi

I've been trying to convert a CFRunLoopTimer Function to a windows api (to make it work on windows). I've been trying to use SetTimer using the HWND, but haven't had luck with callback. Here is the unmodified original code:
use std::ffi::c_void;
use std::{ptr, mem};
use cacao::core_foundation::runloop::__CFRunLoopTimer;
use cacao::core_foundation::{runloop, date};
extern "C" fn callback(_timer: runloop::CFRunLoopTimerRef, info: *mut c_void) {
unsafe {
let timer = info as *mut Timer;
((*timer).func)();
}
}
// Helper to run a timer on the UI thread.
pub struct Timer {
timer: Option<*mut __CFRunLoopTimer>,
func: Box<dyn FnMut()>
}
impl Timer {
pub fn new(interval: f64, func: Box<dyn FnMut()>) -> Box<Self> {
unsafe {
let mut s = Box::new(Timer { timer: None, func });
s.timer = Some(runloop::CFRunLoopTimerCreate(
ptr::null(),
date::CFAbsoluteTimeGetCurrent() + interval,
interval,
0,
0,
callback,
&mut runloop::CFRunLoopTimerContext {
info: &mut *s as *mut _ as *mut c_void,
..mem::zeroed()
},
));
runloop::CFRunLoopAddTimer(
runloop::CFRunLoopGetCurrent(),
s.timer.unwrap(),
runloop::kCFRunLoopCommonModes,
);
s
}
}
}
impl Drop for Timer {
fn drop(&mut self) {
unsafe {
runloop::CFRunLoopTimerInvalidate(self.timer.unwrap());
}
}
}
unsafe impl Sync for Timer {}
unsafe impl Send for Timer {}
This is the existing code that I have.
use winapi::shared::windef::HWND;
use winapi::um::winuser::{ SetTimer, KillTimer }; // WM_TIMER
use winapi::shared::basetsd::{ UINT_PTR };
use winapi::shared::minwindef::{ UINT, DWORD };
const WIN_FRAME_TIMER: usize = 4242;
pub struct Timer {
handle: HWND,
idevent: usize,
func: Box<dyn FnMut()>
}
impl Timer {
pub fn new(handle: HWND, interval: f64, func: Box<dyn Fn()>) -> Box<Self> {
unsafe {
SetTimer(handle, 1234, 100, Some(callback));
}
Box::new(Self { handle: handle, idevent: 1234, func:Box::new(|| {})})
}
}
impl Drop for Timer {
fn drop(&mut self) {
unsafe { KillTimer(self.handle, self.idevent); }
}
}
unsafe impl Sync for Timer {}
unsafe impl Send for Timer {}
extern "system" fn callback(hwnd: HWND, uMsg: UINT, idEvent: UINT_PTR, dwTime: DWORD){
}
type Callback = Box<dyn Fn(&str)>;
Well, if you are up to a challenge, help is welcome

Related

DeviceIoControl (kernel32.lib)

Inspired by balena.io's drivelist, I create a Rust version. The idea is simple, using Windows' Setup API to read all connected storages list (with mount points/drive letters), using winapi crate
///file main.rs
use core::{slice, ffi};
use std::{ptr::{null_mut, addr_of, null}, mem::{zeroed, size_of, MaybeUninit, align_of, transmute}, str::from_utf8};
use winapi::{um::{setupapi::{SetupDiGetClassDevsA, DIGCF_PRESENT, DIGCF_DEVICEINTERFACE, SP_DEVINFO_DATA, SetupDiEnumDeviceInfo, HDEVINFO, PSP_DEVINFO_DATA, SetupDiGetDeviceRegistryPropertyW, SPDRP_FRIENDLYNAME, SPDRP_REMOVAL_POLICY, SPDRP_ENUMERATOR_NAME, SetupDiDestroyDeviceInfoList, SP_DEVICE_INTERFACE_DATA, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetailW, PSP_DEVICE_INTERFACE_DETAIL_DATA_W, SP_DEVICE_INTERFACE_DETAIL_DATA_W}, winioctl::{GUID_DEVINTERFACE_DISK, VOLUME_DISK_EXTENTS, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, PVOLUME_DISK_EXTENTS}, handleapi::{INVALID_HANDLE_VALUE, CloseHandle}, cfgmgr32::{CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL, CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL}, errhandlingapi::GetLastError, fileapi::{CreateFileW, OPEN_EXISTING}, winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_ATTRIBUTE_NORMAL}, ioapiset::DeviceIoControl}, shared::{minwindef::MAX_PATH, winerror::{ERROR_NO_MORE_ITEMS, ERROR_INSUFFICIENT_BUFFER}}, ctypes::c_void};
fn get_detail_data(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA)
{
let mut h_device=INVALID_HANDLE_VALUE;
let mut index=0_u32;
unsafe{
loop {
if h_device!= INVALID_HANDLE_VALUE {
CloseHandle(h_device);
h_device=INVALID_HANDLE_VALUE;
}
let mut device_interface_data:SP_DEVICE_INTERFACE_DATA=zeroed();
device_interface_data.cbSize=size_of::<SP_DEVICE_INTERFACE_DATA>() as _;
if SetupDiEnumDeviceInterfaces(h_dev_info, device_info_data, &GUID_DEVINTERFACE_DISK, index, &mut device_interface_data) == 0 {
let error_code=GetLastError();
if error_code!=ERROR_NO_MORE_ITEMS {
panic!("SetupDiEnumDeviceInterfaces: Error {}",error_code);
}
break;
} else {
let mut size={
let mut required_size=MaybeUninit::<u32>::uninit();
if SetupDiGetDeviceInterfaceDetailW(h_dev_info, &mut device_interface_data, null_mut(), 0, required_size.as_mut_ptr(), null_mut())==0 {
if GetLastError()==ERROR_INSUFFICIENT_BUFFER {
required_size.assume_init()
} else {
panic!("Error SetupDiGetDeviceInterfaceDetailW");
}
} else {
0
}
};
let mut buf:Vec<u8>=Vec::with_capacity(TryInto::<usize>::try_into(size).unwrap() + align_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>()-1);
let align_offset=buf.as_mut_ptr().align_offset(align_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>());
let device_iface_detail =&mut *(buf.as_mut_ptr().offset(align_offset.try_into().unwrap()) as *mut MaybeUninit<SP_DEVICE_INTERFACE_DETAIL_DATA_W>);
device_iface_detail.write(SP_DEVICE_INTERFACE_DETAIL_DATA_W {
cbSize: size_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>().try_into().unwrap(),
DevicePath: [0],
});
if SetupDiGetDeviceInterfaceDetailW(h_dev_info, &mut device_interface_data, device_iface_detail.as_mut_ptr(), size, &mut size, null_mut())==0 {
println!("Error {}, Couldn't SetupDiGetDeviceInterfaceDetailW",GetLastError());
break;
}
let device_detail_data=device_iface_detail.assume_init_ref();
h_device=CreateFileW(device_detail_data.DevicePath.as_ptr(), 0, FILE_SHARE_READ, null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null_mut());
if h_device==INVALID_HANDLE_VALUE {
println!("Couldn't open handle to device: Error {}",GetLastError());
break;
}
get_device_number(h_device);
}
index+=1;
}
if h_device!= INVALID_HANDLE_VALUE {
CloseHandle(h_device);
h_device=INVALID_HANDLE_VALUE;
}
}
}
fn get_device_number(h_device:*mut c_void)
{
unsafe {
let mut size=0_u32;
let mut disk_extents=MaybeUninit::<VOLUME_DISK_EXTENTS>::uninit();
disk_extents.write(zeroed());
let result=DeviceIoControl(h_device, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, null_mut(), 0, disk_extents.as_mut_ptr() as _, size_of::<VOLUME_DISK_EXTENTS>() as _, &mut size, null_mut());
/* It returns 0!! */
println!("Bytes returned: {}",size);
if result!=0 {
println!("Success");
} else {
/* This will be executed */
println!("get_device_number fail. Error {}",GetLastError());
}
}
}
fn get_enumerator_name(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA) -> String
{
unsafe {
let mut buffer:[u8;MAX_PATH]=zeroed();
if SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_ENUMERATOR_NAME, null_mut(), &mut buffer as _, (size_of::<u8>() * MAX_PATH) as _, null_mut()) != 0 {
ansi_to_string(&buffer)
} else {
"".to_string()
}
}
}
fn get_friendly_name(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA) -> String
{
unsafe {
let mut buffer:[u8;MAX_PATH]=zeroed();
if SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_FRIENDLYNAME, null_mut(), &mut buffer as _, (size_of::<u8>() * MAX_PATH) as _, null_mut()) != 0 {
ansi_to_string(&buffer)
} else {
"".to_string()
}
}
}
fn is_removable(h_dev_info:HDEVINFO,device_info_data:PSP_DEVINFO_DATA)->bool
{
unsafe {
let mut result=0_u8;
SetupDiGetDeviceRegistryPropertyW(h_dev_info, device_info_data, SPDRP_REMOVAL_POLICY, null_mut(), &mut result as _, size_of::<u32>() as _, null_mut());
match result as u32
{
CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL|CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL =>true,
_=>false
}
}
}
fn is_usb_drive(enumerator_name:&str) -> bool
{
["USBSTOR", "UASPSTOR", "VUSBSTOR","RTUSER", "CMIUCR", "EUCR","ETRONSTOR", "ASUSSTPT"].contains(&enumerator_name)
}
fn ansi_to_string(unsafe_utf8:&[u8])->String
{
match from_utf8(&unsafe_utf8.iter().filter(|c| **c != 0).map(|c| *c).collect::<Vec<u8>>() as _)
{
Err(err)=>{
println!("Error {}",err);
"".to_string()
},
Ok(res)=>res.trim().to_string()
}
}
fn main() {
unsafe {
let h_device_info=SetupDiGetClassDevsA(&GUID_DEVINTERFACE_DISK, null_mut(), null_mut(), DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if h_device_info!=INVALID_HANDLE_VALUE {
let mut i=0;
let mut device_info_data:SP_DEVINFO_DATA=zeroed();
device_info_data.cbSize=size_of::<SP_DEVINFO_DATA>() as _;
while SetupDiEnumDeviceInfo(h_device_info, i, &mut device_info_data)!=0
{
let enumerator_name=get_enumerator_name(h_device_info, &mut device_info_data);
let friendly_name=get_friendly_name(h_device_info, &mut device_info_data);
if friendly_name.is_empty() {
continue;
}
println!("Name: {}",friendly_name);
println!("Is USB drive: {}",is_usb_drive(&enumerator_name));
println!("Is removable: {}",is_removable(h_device_info, &mut device_info_data));
get_detail_data(h_device_info, &mut device_info_data);
i+=1;
}
}
SetupDiDestroyDeviceInfoList(h_device_info);
}
}
Terminal output:
Name: SKHynix_HFM512GDHTNI-87A0B
Is USB drive: false
Is removable: false
DevicePath: \\?\scsi#disk&ven_nvme&prod_skhynix_hfm512gd#5&8980ef4&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
Bytes returned: 0
get_device_number fail. Error 1
Everything went fine until DeviceIoControl always returns 0 and GetLastError() returns 1 (inside get_device_number() function). Can someone guide me figuring out what went wrong?
Thanks in advance

Building clean and flexible binary trees in Rust

I'm using binary trees to create a simple computation graph. I understand that linked lists are a pain in Rust, but it's a very convenient data structure for what I'm doing. I tried using Box and Rc<RefCell> for the children nodes, but it didn't work out how I wanted, so I used unsafe:
use std::ops::{Add, Mul};
#[derive(Debug, Copy, Clone)]
struct MyStruct {
value: i32,
lchild: Option<*mut MyStruct>,
rchild: Option<*mut MyStruct>,
}
impl MyStruct {
unsafe fn print_tree(&mut self, set_to_zero: bool) {
if set_to_zero {
self.value = 0;
}
println!("{:?}", self);
let mut nodes = vec![self.lchild, self.rchild];
while nodes.len() > 0 {
let child;
match nodes.pop() {
Some(popped_child) => child = popped_child.unwrap(),
None => continue,
}
if set_to_zero {
(*child).value = 0;
}
println!("{:?}", *child);
if !(*child).lchild.is_none() {
nodes.push((*child).lchild);
}
if !(*child).rchild.is_none() {
nodes.push((*child).rchild);
}
}
println!("");
}
}
impl Add for MyStruct {
type Output = Self;
fn add(self, other: Self) -> MyStruct {
MyStruct{
value: self.value + other.value,
lchild: Some(&self as *const _ as *mut _),
rchild: Some(&other as *const _ as *mut _),
}
}
}
impl Mul for MyStruct {
type Output = Self;
fn mul(self, other: Self) -> Self {
MyStruct{
value: self.value * other.value,
lchild: Some(&self as *const _ as *mut _),
rchild: Some(&other as *const _ as *mut _),
}
}
}
fn main() {
let mut tree: MyStruct;
{
let a = MyStruct{ value: 10, lchild: None, rchild: None };
let b = MyStruct{ value: 20, lchild: None, rchild: None };
let c = a + b;
println!("c.value: {}", c.value); // 30
let mut d = a + b;
println!("d.value: {}", d.value); // 30
d.value = 40;
println!("d.value: {}", d.value); // 40
let mut e = c * d;
println!("e.value: {}", e.value); // 1200
unsafe {
e.print_tree(false); // correct values
e.print_tree(true); // all zeros
e.print_tree(false); // all zeros, everything is set correctly
}
tree = e;
}
unsafe { tree.print_tree(false); } // same here, only zeros
}
Link to the playground
I honestly don't mind that much using unsafe, but is there a safe way doing it? How bad is the use of unsafe here?
You can just box both of the children, since you have a unidirectional tree:
use std::ops::{Add, Mul};
use std::fmt;
#[derive(Clone)]
struct MyStruct {
value: i32,
lchild: Option<Box<MyStruct>>,
rchild: Option<Box<MyStruct>>,
}
impl fmt::Debug for MyStruct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("MyStruct")
.field("value", &self.value)
.field("lchild", &self.lchild.as_deref())
.field("rchild", &self.rchild.as_deref())
.finish()
}
}
impl MyStruct {
fn print_tree(&mut self, set_to_zero: bool) {
if set_to_zero {
self.value = 0;
}
println!("MyStruct {{ value: {:?}, lchild: {:?}, rchild: {:?} }}", self.value, &self.lchild as *const _, &self.rchild as *const _);
if let Some(child) = &mut self.lchild {
child.print_tree(set_to_zero);
}
if let Some(child) = &mut self.rchild {
child.print_tree(set_to_zero);
}
}
}
impl Add for MyStruct {
type Output = Self;
fn add(self, other: Self) -> MyStruct {
MyStruct {
value: self.value + other.value,
lchild: Some(Box::new(self)),
rchild: Some(Box::new(other)),
}
}
}
impl Mul for MyStruct {
type Output = Self;
fn mul(self, other: Self) -> Self {
MyStruct {
value: self.value * other.value,
lchild: Some(Box::new(self)),
rchild: Some(Box::new(other)),
}
}
}
fn main() {
let tree = {
let a = MyStruct {
value: 10,
lchild: None,
rchild: None,
};
let b = MyStruct {
value: 20,
lchild: None,
rchild: None,
};
let c = a.clone() + b.clone();
println!("c.value: {}", c.value); // 30
let mut d = a.clone() + b.clone();
println!("d.value: {}", d.value); // 30
d.value = 40;
println!("d.value: {}", d.value); // 40
let mut e = c * d;
println!("e.value: {}", e.value); // 1200
println!("");
e.print_tree(false); // correct values
println!("");
e.print_tree(true); // all zeros
println!("");
e.print_tree(false); // all zeros, everything is set correctly
println!("");
e
};
dbg!(tree);
}
I implemented Debug manually and reimplemented print_tree recursively. I don't know if there is a way to implement print_tree as mutable like that without recursion, but it's certainly possible if you take &self instead (removing the set_to_zero stuff).
playground
Edit: Turns out it is possible to mutably iterate over the tree values without recursion. The following code is derived from the playground in this comment by #Shepmaster.
impl MyStruct {
fn zero_tree(&mut self) {
let mut node_stack = vec![self];
let mut value_stack = vec![];
// collect mutable references to each value
while let Some(MyStruct { value, lchild, rchild }) = node_stack.pop() {
value_stack.push(value);
if let Some(child) = lchild {
node_stack.push(child);
}
if let Some(child) = rchild {
node_stack.push(child);
}
}
// iterate over mutable references to values
for value in value_stack {
*value = 0;
}
}
}

Title text not showing in Win32 application written in Rust

After following the Opening a Win32 Window tutorial, when I use the cargo run command, it shows a window that does not have a title.
#![allow(unused)]
#[link(name = "Kernel32")]
extern "system" {
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
}
#[link(name = "User32")]
extern "system" {
pub fn RegisterClassW(lpWndClass: *const WNDCLASSW) -> ATOM;
pub fn GetLastError() -> DWORD;
pub fn CreateWindowExW(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND;
pub fn ShowWindow(hWnd: HWND, nCmdShow: c_int) -> BOOL;
pub fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
pub fn PostQuitMessage(nExitCode: c_int);
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
pub fn GetMessageW(
lpMsg: *mut MSG,
hWnd: HWND,
wMsgFilterMin: UINT,
wMsgFilterMax: UINT,
) -> BOOL;
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
}
pub type c_int = i32;
pub type c_uint = u32;
pub type HANDLE = PVOID;
pub type HBRUSH = HANDLE;
pub type HCURSOR = HICON;
pub type HICON = HANDLE;
pub type HINSTANCE = HANDLE;
pub type HWND = HANDLE;
pub type LONG_PTR = isize;
pub type LPARAM = LONG_PTR;
pub type LPCWSTR = *const WCHAR;
pub type LRESULT = LONG_PTR;
pub type PVOID = *mut core::ffi::c_void;
pub type UINT = c_uint;
pub type UINT_PTR = usize;
pub type WCHAR = wchar_t;
pub type wchar_t = u16;
pub type WPARAM = UINT_PTR;
pub type HMODULE = HINSTANCE;
pub type ATOM = WORD;
pub type WORD = c_ushort;
pub type c_ushort = u16;
pub type DWORD = u32;
pub type HMENU = HANDLE;
pub type LPVOID = *mut core::ffi::c_void;
pub type BOOL = c_int;
pub type LONG = c_long;
pub type c_long = i32;
pub type WNDPROC = Option<
unsafe extern "system" fn(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT,
>;
#[repr(C)]
pub struct WNDCLASSW {
pub style: UINT,
pub lpfnWndProc: WNDPROC,
pub cbClsExtra: c_int,
pub cbWndExtra: c_int,
pub hInstance: HINSTANCE,
pub hIcon: HICON,
pub hCursor: HCURSOR,
pub hbrBackground: HBRUSH,
pub lpszMenuName: LPCWSTR,
pub lpszClassName: LPCWSTR,
}
#[repr(C)]
pub struct MSG {
pub hwnd: HWND,
pub message: UINT,
pub wParam: WPARAM,
pub lParam: LPARAM,
pub time: DWORD,
pub pt: POINT,
pub lPrivate: DWORD,
}
#[repr(C)]
pub struct POINT {
pub x: LONG,
pub y: LONG,
}
impl Default for MSG {
#[inline]
#[must_use]
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
impl Default for WNDCLASSW {
#[inline]
#[must_use]
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
pub fn wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(Some(0)).collect()
}
const WS_OVERLAPPED: u32 = 0x00000000;
const WS_CAPTION: u32 = 0x00C00000;
const WS_SYSMENU: u32 = 0x00080000;
const WS_THICKFRAME: u32 = 0x00040000;
const WS_MINIMIZEBOX: u32 = 0x00020000;
const WS_MAXIMIZEBOX: u32 = 0x00010000;
const WS_OVERLAPPEDWINDOW: u32 =
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const CW_USEDEFAULT: c_int = 0x80000000_u32 as c_int;
const SW_SHOW: i32 = 5;
const WM_NCCREATE: u32 = 0x0081;
const WM_DESTROY: u32 = 0x0002;
const WM_CLOSE: u32 = 0x0010;
extern "system" fn window_proc(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT {
unsafe {
match uMsg {
WM_NCCREATE => {
return 1;
}
WM_DESTROY => {
PostQuitMessage(0);
return 0;
}
WM_CLOSE => {
DestroyWindow(hwnd);
return 0;
}
_ => {
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
}
}
}
fn main() {
let hInstance = unsafe { GetModuleHandleW(core::ptr::null()) };
let sample_window_class_wn = wide_null("Sample window class");
let mut wc = WNDCLASSW::default();
wc.lpfnWndProc = Some(window_proc);
wc.hInstance = hInstance;
wc.lpszClassName = sample_window_class_wn.as_ptr();
unsafe {
if RegisterClassW(&wc) == 0 {
println!(
"Not able to register window class. Error code: {}",
GetLastError()
);
}
}
let sample_window_name_wn = wide_null("sample window");
let hwnd = unsafe {
CreateWindowExW(
0,
sample_window_class_wn.as_ptr(),
sample_window_name_wn.as_ptr(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
core::ptr::null_mut(),
core::ptr::null_mut(),
hInstance,
core::ptr::null_mut(),
)
};
unsafe {
ShowWindow(hwnd, SW_SHOW);
}
unsafe {
UpdateWindow(hwnd);
}
let mut msg = MSG::default();
unsafe {
while GetMessageW(&mut msg, hwnd, 0, 0) > 0 {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
The problem is the handling of WM_NCCREATE. The MSDN documentation is not very clear about this, but this message is used so that the window class is able to filter and/or modify the creation parameters of the window. These parameters are received in the lParam argument that is actually a CREATESTRUCT*.
The idea is that you do the cast with CREATESTRUCT *cs = (CREATESTRUCT *)lParam;, then you inspect and/or modify the contents of that struct and finally you pass on the message to DefWindowProc() to continue creation or return 0 to abort.
But it looks like the linked tutorial is doing return 1 without forwarding it to DefWindowProc(). Just don't do that: most messages should be forwarded to DefWindowProc() except those you know you should not.
In your particular example, you can remove the return 1 from the WM_NCCREATE branch and move the DefWindowProc() call to outside of the match:
match uMsg {
WM_NCCREATE => {
//...
//no return
}
WM_DESTROY => {
PostQuitMessage(0);
return 0;
}
WM_CLOSE => {
DestroyWindow(hwnd);
return 0;
}
_ => {}
}
DefWindowProcW(hwnd, uMsg, wParam, lParam)
NOTE: Although the Windows source code is not (yet) publicly available, the next best thing is the source code of Wine. You can check what DefWindowProc(WM_NCCREATE) does here:
case WM_NCCREATE:
if (lParam)
{
CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam;
DEFWND_SetTextW( hwnd, cs->lpszName );
result = 1;
if(cs->style & (WS_HSCROLL | WS_VSCROLL))
{
SCROLLINFO si = {sizeof si, SIF_ALL, 0, 100, 0, 0, 0};
SetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
SetScrollInfo( hwnd, SB_VERT, &si, FALSE );
}
}
break;

WMI releases my implementation of IWbemQuerySink too many times (ExecQueryAsync): refcount turns negative

I've implemented the IWbemQuerySink in rust, in order to use ExecQueryAsync. Code is available in this pull request but I'll post the relevant bits here.
TLDR;
WMI is calling Release() 5 times on my QuerySink implementation, 1 which is understandable, and 4 which are not... There is only 1 call to AddRef by WMI, so the question is: where do this 4 other calls to Release comes from and is there a way to prevent them from happening?
With a lot of details
Here is a simplified implementation, it is working great:
use winapi::{
um::wbemcli::{
{IWbemClassObject,IWbemObjectSink, IWbemObjectSinkVtbl},
WBEM_S_NO_ERROR,
},
shared::{
ntdef::HRESULT,
wtypes::BSTR,
},
ctypes::{
c_long,
},
};
use com_impl::{ComImpl, VTable, Refcount};
use wio::com::ComPtr;
#[repr(C)]
#[derive(ComImpl)]
#[interfaces(IWbemObjectSink)]
pub struct QuerySink {
vtbl: VTable<IWbemObjectSinkVtbl>,
refcount: Refcount,
}
impl QuerySink {
pub fn new() -> ComPtr<IWbemObjectSink> {
let ptr = QuerySink::create_raw();
let ptr = ptr as *mut IWbemObjectSink;
unsafe { ComPtr::from_raw(ptr) }
}
}
// AddRef and Release methods are provided by com_impl
#[com_impl::com_impl]
unsafe impl IWbemObjectSink for QuerySink {
pub unsafe fn indicate(
&self,
_lObjectCount: c_long,
_apObjArray: *mut *mut IWbemClassObject
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
pub unsafe fn set_status(
&self,
_lFlags: c_long,
_hResult: HRESULT,
_strParam: BSTR,
_pObjParam: *mut IWbemClassObject
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
}
Let's try to use it in a simple call to ExecQueryAsync:
pub fn exec_async_query_native_wrapper(
&self,
query: impl AsRef<str>,
) -> Result<(), WMIError> {
let query_language = BStr::from_str("WQL")?;
let query = BStr::from_str(query.as_ref())?;
let p_sink: ComPtr<IWbemObjectSink> = QuerySink::new();
unsafe {
check_hres((*self.svc()).ExecQueryAsync(
query_language.as_bstr(),
query.as_bstr(),
WBEM_FLAG_BIDIRECTIONAL as i32,
ptr::null_mut(),
p_sink.as_raw(),
))?;
}
Ok(())
}
When executing it in a test, this code panics because the release function is called too many times, and the Refcount becomes negative:
attempt to subtract with overflow', C:\Users\apennamen\.cargo\registry\src\github.com-1ecc6299db9ec823\com-impl-0.1.1\src\lib.rs:139:9
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\std\src\panicking.rs:493
1: core::panicking::panic_fmt
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\core\src\panicking.rs:92
2: core::panicking::panic
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\core\src\panicking.rs:50
3: com_impl::Refcount::release
at C:\Users\apennamen\.cargo\registry\src\github.com-1ecc6299db9ec823\com-impl-0.1.1\src\lib.rs:139
4: wmi::query_sink::QuerySink::__com_impl__IUnknown__Release
at .\src\query_sink.rs:31
5: CoMarshalInterface
6: CoMarshalInterface
.....
After some tests, I found that the Release method is called 4 times more than it should be by WMI. So when adding 4 calls to AddRef, the code works just fine. This is of course not satisfying as maybe tomorrow it will be 5 calls, or 10 or 2...
I don't know how to handle this problem, hopefully someone else ran into it
This is a hacky working code:
pub fn exec_async_query_native_wrapper(
&self,
query: impl AsRef<str>,
) -> Result<(), WMIError> {
let query_language = BStr::from_str("WQL")?;
let query = BStr::from_str(query.as_ref())?;
let p_sink: ComPtr<IWbemObjectSink> = QuerySink::new();
unsafe {
// FIXME hack the RefCount
for _ in 0..4 { p_sink.AddRef(); }
check_hres((*self.svc()).ExecQueryAsync(
query_language.as_bstr(),
query.as_bstr(),
WBEM_FLAG_BIDIRECTIONAL as i32,
ptr::null_mut(),
p_sink.as_raw(),
))?;
}
Ok(())
}
}
Thank you for reading this far :)
EDIT :
Here is the expanded macro code:
#[cfg(feature = "async-query")]
pub(crate) mod query_sink {
use winapi::{
um::wbemcli::{
{IWbemClassObject, IWbemObjectSink, IWbemObjectSinkVtbl}, WBEM_S_NO_ERROR,
WBEM_STATUS_COMPLETE,
},
shared::{
ntdef::HRESULT,
wtypes::BSTR,
winerror::{E_POINTER, E_FAIL},
},
ctypes::{c_long},
};
use com_impl::{ComImpl, VTable, Refcount};
use log::{trace, warn};
use std::ptr::NonNull;
use wio::com::ComPtr;
use crate::result_enumerator::IWbemClassWrapper;
use crate::WMIError;
/// Implementation for [IWbemObjectSink](https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nn-wbemcli-iwbemobjectsink).
/// This [Sink](https://en.wikipedia.org/wiki/Sink_(computing))
/// receives asynchronously the result of the query,
/// through Indicate calls. When finished,the SetStatus method
/// is called.
/// # <https://learn.microsoft.com/fr-fr/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer-asynchronously>
#[repr(C)]
#[interfaces(IWbemObjectSink)]
pub struct QuerySink {
vtbl: VTable<IWbemObjectSinkVtbl>,
refcount: Refcount,
}
impl QuerySink {
fn create_raw() -> *mut Self {
Box::into_raw(Box::new(QuerySink {
vtbl: <Self as com_impl::BuildVTable<_>>::static_vtable(),
refcount: Default::default(),
}))
}
}
unsafe impl com_impl::BuildVTable<winapi::um::unknwnbase::IUnknownVtbl> for QuerySink {
const VTBL: winapi::um::unknwnbase::IUnknownVtbl = winapi::um::unknwnbase::IUnknownVtbl {
AddRef: Self::__com_impl__IUnknown__AddRef,
Release: Self::__com_impl__IUnknown__Release,
QueryInterface: Self::__com_impl__IUnknown__QueryInterface,
};
fn static_vtable() -> com_impl::VTable<winapi::um::unknwnbase::IUnknownVtbl> {
com_impl::VTable::new(&Self::VTBL)
}
}
#[allow(non_snake_case)]
impl QuerySink {
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__AddRef(
this: *mut winapi::um::unknwnbase::IUnknown,
) -> u32 {
let this = &*(this as *const Self);
this.refcount.add_ref()
}
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__Release(
this: *mut winapi::um::unknwnbase::IUnknown,
) -> u32 {
let ptr = this as *mut Self;
let count = (*ptr).refcount.release();
if count == 0 {
Box::from_raw(ptr);
}
count
}
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__QueryInterface(
this: *mut winapi::um::unknwnbase::IUnknown,
riid: *const winapi::shared::guiddef::IID,
ppv: *mut *mut winapi::ctypes::c_void,
) -> winapi::shared::winerror::HRESULT {
if ppv.is_null() {
return winapi::shared::winerror::E_POINTER;
}
if winapi::shared::guiddef::IsEqualIID(
&*riid,
&<winapi::um::unknwnbase::IUnknown as winapi::Interface>::uuidof(),
) || winapi::shared::guiddef::IsEqualIID(
&*riid,
&<IWbemObjectSink as winapi::Interface>::uuidof(),
) {
*ppv = this as *mut winapi::ctypes::c_void;
winapi::shared::winerror::S_OK
} else {
*ppv = std::ptr::null_mut();
winapi::shared::winerror::E_NOINTERFACE
}
}
}
impl QuerySink {
pub fn new() -> ComPtr<IWbemObjectSink> {
let ptr = QuerySink::create_raw();
let ptr = ptr as *mut IWbemObjectSink;
unsafe { ComPtr::from_raw(ptr) }
}
}
unsafe impl com_impl::BuildVTable<IWbemObjectSinkVtbl> for QuerySink {
const VTBL: IWbemObjectSinkVtbl = IWbemObjectSinkVtbl {
parent: <Self as com_impl::BuildVTable<_>>::VTBL,
Indicate: Self::__com_impl_stub__IWbemObjectSink__Indicate,
SetStatus: Self::__com_impl_stub__IWbemObjectSink__SetStatus,
};
fn static_vtable() -> com_impl::VTable<IWbemObjectSinkVtbl> {
com_impl::VTable::new(&Self::VTBL)
}
}
#[allow(non_snake_case)]
impl QuerySink {
#[inline(never)]
unsafe extern "system" fn __com_impl_stub__IWbemObjectSink__Indicate(
this: *mut IWbemObjectSink,
__com_arg_0: c_long,
__com_arg_1: *mut *mut IWbemClassObject,
) -> HRESULT {
let this = &*(this as *const Self);
Self::__com_impl_body__IWbemObjectSink__Indicate(this, __com_arg_0, __com_arg_1)
}
#[inline(never)]
unsafe extern "system" fn __com_impl_stub__IWbemObjectSink__SetStatus(
this: *mut IWbemObjectSink,
__com_arg_0: c_long,
__com_arg_1: HRESULT,
__com_arg_2: BSTR,
__com_arg_3: *mut IWbemClassObject,
) -> HRESULT {
let this = &*(this as *const Self);
Self::__com_impl_body__IWbemObjectSink__SetStatus(
this,
__com_arg_0,
__com_arg_1,
__com_arg_2,
__com_arg_3,
)
}
#[inline(always)]
unsafe extern "system" fn __com_impl_body__IWbemObjectSink__Indicate(
&self,
lObjectCount: c_long,
apObjArray: *mut *mut IWbemClassObject,
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
#[inline(always)]
unsafe extern "system" fn __com_impl_body__IWbemObjectSink__SetStatus(
&self,
lFlags: c_long,
_hResult: HRESULT,
_strParam: BSTR,
_pObjParam: *mut IWbemClassObject,
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
}
}

How do I implement a trait for an enum and its respective variants?

I'm trying to use enum variants to capture data which is heterogeneous in nature (has different collections of fields) but which is of the same "type" from a protocol perspective. However, I'm not sure how to implement subtype-specific methods and traits. Here is a minimal example of how I can create an enumeration of Data and I can use enum variant constructors to specify the types, but if I implement a trait on the variant, calling that function is not something I've figured out how to do.
use std::fmt;
enum Data {
N(NData),
S(SData),
}
struct NData {
numeric: u32,
}
impl fmt::Display for NData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.numeric)
}
}
struct SData {
stringy: Vec<String>,
}
fn main() {
let d_n: Data = Data::N(NData { numeric: 0x0 });
let n = NData { numeric: 0xff };
// Fails, fmt::Display not implemented for Data
println!("{}", d_n);
// Just fine!
println!("{}", n);
}
One possible solution could be to implement your trait for the variants as well as for the enum, which as you can see here only calls the specific implementations of the variants:
use std::fmt;
struct NData {
numeric: u32,
}
impl fmt::Display for NData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.numeric)
}
}
struct SData {
strings: Vec<String>,
}
impl fmt::Display for SData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.strings)
}
}
enum Data {
N(NData),
S(SData),
}
impl fmt::Display for Data {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Data::N(n_data) => n_data.fmt(f),
Data::S(s_data) => s_data.fmt(f),
}
}
}
fn main() {
let n = NData { numeric: 0xff };
let s = SData { strings: vec!["hello".to_string(), "world".to_string()] };
println!("{}", n);
println!("{}", s);
let d_n = Data::N(n);
let d_s = Data::S(s);
println!("{}", d_n);
println!("{}", d_s);
}
Which will produce the following output:
255
["hello", "world"]
255
["hello", "world"]

Resources