Created
August 31, 2019 20:36
-
-
Save DrMetallius/084115493deb21148a6bef9326b47ea6 to your computer and use it in GitHub Desktop.
COM in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use winapi::shared::ntdef::LPCWSTR; | |
use winapi::um::winuser::{BS_DEFPUSHBUTTON, SW_SHOW, WS_CHILD, WS_VISIBLE, WS_TABSTOP, WS_OVERLAPPEDWINDOW, WNDCLASSW}; | |
use winapi::shared::minwindef::{HINSTANCE, UINT, WPARAM, LPARAM, LPVOID, DWORD}; | |
use winapi::shared::windef::{POINT, HBRUSH, HMENU, HWND}; | |
use winapi::um::winuser; | |
use winapi::um::winuser::{BS_TEXT, GetWindowLongW}; | |
use winapi::um::libloaderapi::GetModuleHandleW; | |
use winapi::shared::minwindef::LRESULT; | |
use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER; | |
use std::ffi::{OsStr, OsString}; | |
use std::os::windows::ffi::OsStrExt; | |
use super::text::WINDOW_NAME; | |
use winapi::um::winuser::GWL_HINSTANCE; | |
use winapi::um::combaseapi::{CoCreateInstanceEx, CoTaskMemFree}; | |
use std::ptr::{null, null_mut}; | |
use winapi::um::shobjidl::{FOS_FORCEFILESYSTEM, FOS_PICKFOLDERS, IFileDialog, IFileDialogEvents}; | |
use winapi::um::shobjidl_core::{IShellItem, SIGDN_FILESYSPATH}; | |
use winapi::um::combaseapi::CoCreateInstance; | |
use winapi::shared::guiddef::GUID; | |
use winapi::ctypes::c_void; | |
use winapi::shared::ntdef::HRESULT; | |
use winapi::Interface; | |
use winapi::um::unknwnbase::IUnknown; | |
use std::mem; | |
use winapi::um::winnt::PWSTR; | |
use std::os::windows::ffi::OsStringExt; | |
use std::slice; | |
use winapi::um::winnt::WCHAR; | |
use libc::wcslen; | |
use ui::text::MAIN_OPEN_BTN; | |
use std::ops::Deref; | |
macro_rules! com { | |
($call:ident($($args:tt)*)) => { | |
com!(@parse () $call () ($($args)*)); | |
}; | |
(@parse ($($out_vars:ident)*) $call:ident ($($parsed_args:tt)*) (out $arg:ident: $type:ty)) => { | |
com!(@parse ($($out_vars)* $arg) $call ($($parsed_args)* ComPtr::<$type>::as_out_param(&mut $arg) as *mut _,) ()); | |
}; | |
(@parse ($($out_vars:ident)*) $call:ident ($($parsed_args:tt)*) (out $arg:ident: $type:ty, $($rest:tt)*)) => { | |
com!(@parse ($($out_vars)* $arg) $call ($($parsed_args)* ComPtr::<$type>::as_out_param(&mut $arg) as *mut _,) ($($rest)*)); | |
}; | |
(@parse ($($out_vars:ident)*) $call:ident ($($parsed_args:tt)*) ($arg:expr)) => { | |
com!(@parse ($($out_vars)*) $call ($($parsed_args)* $arg,) ()); | |
}; | |
(@parse ($($out_vars:ident)*) $call:ident ($($parsed_args:tt)*) ($arg:expr, $($rest:tt)*)) => { | |
com!(@parse ($($out_vars)*) $call ($($parsed_args)* $arg,) ($($rest)*)); | |
}; | |
(@parse ($($out_vars:ident)*) $call:ident ($($parsed_args:tt)*) ()) => { | |
$( | |
let mut $out_vars = ComPtr::new(); | |
)* | |
let result = $call($($parsed_args)*); | |
if result != 0 { | |
return ::std::result::Result::Err(result); | |
} | |
}; | |
} | |
macro_rules! try_com { | |
($expr:expr) => (if $expr != 0 { | |
return ::std::result::Result::Err($expr) | |
} | |
) | |
} | |
macro_rules! uninitialized_ref { | |
($param:ident, $type:ty) => {let mut $param: &mut $type = mem::uninitialized();} | |
} | |
macro_rules! out_param { | |
($param:ident) => {&mut $param as *mut _ as *mut _} | |
} | |
pub fn run() { | |
unsafe { | |
let class_name = to_wstring("my_window"); | |
let wnd = WNDCLASSW { | |
style: 0, | |
lpfnWndProc: Some(window_proc), | |
cbClsExtra: 0, | |
cbWndExtra: 0, | |
hInstance: 0 as HINSTANCE, | |
hIcon: winuser::LoadIconW(0 as HINSTANCE, winuser::IDI_APPLICATION), | |
hCursor: winuser::LoadCursorW(0 as HINSTANCE, winuser::IDC_ARROW), | |
hbrBackground: 16 as HBRUSH, | |
lpszMenuName: 0 as LPCWSTR, | |
lpszClassName: class_name.as_ptr(), | |
}; | |
winuser::RegisterClassW(&wnd); | |
let h_wnd_window = winuser::CreateWindowExW(0, class_name.as_ptr(), | |
to_wstring(WINDOW_NAME).as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, | |
0, 0, 400, 400, 0 as HWND, 0 as HMENU, 0 as HINSTANCE, null_mut()); | |
let mut msg = winuser::MSG { | |
hwnd: 0 as HWND, | |
message: 0 as UINT, | |
wParam: 0 as WPARAM, | |
lParam: 0 as LPARAM, | |
time: 0 as DWORD, | |
pt: POINT { x: 0, y: 0 }, | |
}; | |
winuser::ShowWindow(h_wnd_window, SW_SHOW); | |
let hwndButton = winuser::CreateWindowExW( | |
BS_TEXT, | |
to_wstring("BUTTON").as_ptr(), // Predefined class; Unicode assumed | |
to_wstring(MAIN_OPEN_BTN).as_ptr(), // Button text | |
WS_TABSTOP | WS_VISIBLE | WS_CHILD, // Styles | |
50, // x position | |
50, // y position | |
100, // Button width | |
30, // Button height | |
h_wnd_window, // Parent window | |
null_mut() as _, // No menu. | |
GetWindowLongW(h_wnd_window, GWL_HINSTANCE) as HINSTANCE, | |
null_mut() as LPVOID); // Pointer not needed. | |
// Finally we run the standard application loop - | |
loop { | |
let pm = winuser::GetMessageW(&mut msg, 0 as HWND, 0, 0); | |
if pm == 0 { | |
break; | |
} | |
// println!("Msg on the main loop: {}", msg.message); | |
match msg.message { | |
winuser::WM_QUIT => { break; } | |
winuser::WM_COMMAND => { | |
// println!("Command"); | |
show_open_file_dialog(h_wnd_window); | |
continue; | |
} | |
_ => { | |
winuser::TranslateMessage(&mut msg); | |
winuser::DispatchMessageW(&mut msg); | |
} | |
}; | |
} | |
} | |
} | |
fn to_wstring(string: &str) -> Vec<u16> { | |
let mut data: Vec<u16> = OsStr::new(string).encode_wide().collect(); | |
data.push(0); | |
data | |
} | |
fn from_wstring(string: PWSTR) -> String { | |
let string_slice = unsafe { | |
let mut len = wcslen(string); | |
slice::from_raw_parts(string, len) | |
}; | |
OsString::from_wide(string_slice).into_string().unwrap() | |
} | |
pub unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT { | |
// println!("Msg on window proc: {}", msg); | |
if msg == winuser::WM_COMMAND { | |
println!("Command!"); | |
} | |
match msg { | |
winuser::WM_DESTROY => { | |
winuser::PostQuitMessage(0); | |
0 | |
} | |
winuser::WM_COMMAND => { | |
winuser::PostMessageW(h_wnd, msg, w_param, l_param); | |
0 | |
} | |
_ => winuser::DefWindowProcW(h_wnd, msg, w_param, l_param) | |
} | |
} | |
const GUID_FILE_DIALOG: GUID = GUID { | |
Data1: 0xdc1c5a9c, | |
Data2: 0xe88a, | |
Data3: 0x4dde, | |
Data4: [0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7], | |
}; | |
unsafe fn show_open_file_dialog(owner: HWND) -> Result<(), HRESULT> { | |
uninitialized_ref!(file_dialog, IFileDialog); | |
try_com!(CoCreateInstance(&GUID_FILE_DIALOG, | |
null_mut(), | |
CLSCTX_INPROC_SERVER, | |
&IFileDialog::uuidof() as *const _, | |
out_param!(file_dialog))); | |
let mut options = 0u32; | |
try_com!(file_dialog.GetOptions(out_param!(options))); | |
try_com!(file_dialog.SetOptions(options | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM)); | |
try_com!(file_dialog.Show(owner)); | |
let mut dialog_result = ComPtr::<IShellItem>::new(); | |
try_com!(file_dialog.GetResult(dialog_result.as_out_param())); | |
uninitialized_ref!(display_name, WCHAR); | |
try_com!(dialog_result.GetDisplayName(SIGDN_FILESYSPATH, out_param!(display_name))); | |
println!("Got display name: {}", from_wstring(display_name)); | |
CoTaskMemFree(display_name as *mut _ as *mut _); | |
dialog_result.Release(); | |
file_dialog.Release(); | |
Ok(()) | |
} | |
unsafe fn test() -> Result<(), HRESULT> { | |
trace_macros!(true); | |
com!(CoCreateInstance(&GUID_FILE_DIALOG, null_mut(), CLSCTX_INPROC_SERVER, &IFileDialog::uuidof() as *const _, out file_dialog: IFileDialog)); | |
trace_macros!(false); | |
file_dialog.Show(null_mut()); | |
Ok(()) | |
} | |
fn func(string: &str, string2: &str) { | |
println!("{}", string); | |
} | |
struct ComPtr<T: Interface> { | |
ptr: *mut T | |
} | |
impl<'a, T: Interface> ComPtr<T> { | |
fn new() -> ComPtr<T> { | |
ComPtr { | |
ptr: null_mut() | |
} | |
} | |
fn as_out_param(&mut self) -> *mut *mut T { | |
&mut self.ptr as *mut _ as *mut _ | |
} | |
} | |
impl<'a, T: Interface> Drop for ComPtr<T> { | |
fn drop(&mut self) { | |
unsafe { | |
if let Some(ptr) = (self.ptr as *const IUnknown).as_ref() { | |
ptr.Release(); | |
} | |
} | |
} | |
} | |
impl<T: Interface> Deref for ComPtr<T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
unsafe { | |
self.ptr.as_ref().unwrap() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment