Skip to content

Instantly share code, notes, and snippets.

@DanielKeep
Created September 18, 2014 14:57
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save DanielKeep/5b4f613b22add300ee52 to your computer and use it in GitHub Desktop.
Save DanielKeep/5b4f613b22add300ee52 to your computer and use it in GitHub Desktop.
Using COM from Rust: showing the Run dialog
/*!
Shows the "Run" dialog under Windows using COM automation.
*/
#![allow(dead_code)]
#![feature(macro_rules)]
#![license = "MIT"]
extern crate libc;
macro_rules! DEFINE_GUID(
($name:ident, $l:expr, $w1:expr, $w2:expr, $($bs:expr),+) => {
pub static $name: ::types::GUID = ::types::GUID {
data1: $l,
data2: $w1,
data3: $w2,
data4: [$($bs),+]
};
};
)
fn main() {
/*
Until `rustc` stops using the LLVM segmented stacks support on Windows, you have to call this to un-bork OS-private data.
In this particular case, it causes CoCreateInstance to fail with ERROR_NOACCESS.
*/
//fix_corrupt_tlb();
win32::show_run_file_dialog();
}
fn fix_corrupt_tlb() {
unsafe { ::std::rt::stack::record_sp_limit(0); }
}
mod win32 {
use libc::c_void;
use types::{DWORD, HRESULT, REFCLSID, REFIID};
use types::{IUnknown};
pub fn show_run_file_dialog() {
use std::mem::transmute;
use std::ptr::mut_null;
use types::{IID_IShellDispatch, IShellDispatch};
match unsafe { CoInitializeEx(mut_null(), COINIT_APARTMENTTHREADED) } {
S_OK => (),
S_FALSE => (),
result => fail!("call to CoInitializeEx failed: {}", result)
}
/*
You know what would make this easier? Being able to do this:
match CoCreateInstance(..., &mut let obj) { ... }
// obj is in scope here
Not *widely* useful, especially not in idiomatic Rust, but still nice for dealing with these sorts of APIs.
*/
let mut obj: *mut IShellDispatch = mut_null();
match unsafe { CoCreateInstance(&CLSID_ShApp, mut_null(), CLSCTX_INPROC_SERVER, &IID_IShellDispatch, transmute(&mut obj)) } {
S_OK => (),
REGDB_E_CLASSNOTREG => fail!("CoCreateInstance failed: class not registered"),
E_NOINTERFACE => fail!("CoCreateInstance failed: class does not implement interface"),
0x800703e6 => fail!("CoCreateInstance failed: ERROR_NOACCESS; see https://github.com/rust-lang/rust/issues/13259"),
result => fail!("CoCreateInstance failed: error {:#08x}", result)
}
assert!(obj != mut_null());
match unsafe { ((*(*obj).__vtable).FileRun)(transmute(obj)) } {
S_OK => (),
result => fail!("IShellDispatch.FileRun failed: error {:#08x}", result)
}
unsafe {
((*(*obj).__vtable).__base.__base.Release)(transmute(obj));
}
unsafe {
CoUninitialize();
}
}
#[link(name = "ole32")]
extern "stdcall" {
fn CoCreateInstance(rclsid: REFCLSID, pUnkOuter: *mut IUnknown, dwClsContext: DWORD, riid: REFIID, ppv: *mut *mut c_void) -> HRESULT;
fn CoInitializeEx(pvReserved: *mut c_void, dwCoInit: DWORD) -> HRESULT;
fn CoUninitialize();
}
static CLSCTX_INPROC_SERVER: DWORD = 0x1;
static CLSCTX_INPROC_HANDLER: DWORD = 0x2;
static CLSCTX_LOCAL_SERVER: DWORD = 0x4;
static CLSCTX_INPROC_SERVER16: DWORD = 0x8;
static CLSCTX_REMOTE_SERVER: DWORD = 0x10;
static CLSCTX_INPROC_HANDLER16: DWORD = 0x20;
static CLSCTX_RESERVED1: DWORD = 0x40;
static CLSCTX_RESERVED2: DWORD = 0x80;
static CLSCTX_RESERVED3: DWORD = 0x100;
static CLSCTX_RESERVED4: DWORD = 0x200;
static CLSCTX_NO_CODE_DOWNLOAD: DWORD = 0x400;
static CLSCTX_RESERVED5: DWORD = 0x800;
static CLSCTX_NO_CUSTOM_MARSHAL: DWORD = 0x1000;
static CLSCTX_ENABLE_CODE_DOWNLOAD: DWORD = 0x2000;
static CLSCTX_NO_FAILURE_LOG: DWORD = 0x4000;
static CLSCTX_DISABLE_AAA: DWORD = 0x8000;
static CLSCTX_ENABLE_AAA: DWORD = 0x10000;
static CLSCTX_FROM_DEFAULT_CONTEXT: DWORD = 0x20000;
static CLSCTX_ACTIVATE_32_BIT_SERVER: DWORD = 0x40000;
static CLSCTX_ACTIVATE_64_BIT_SERVER: DWORD = 0x80000;
static CLSCTX_ENABLE_CLOAKING: DWORD = 0x100000;
static CLSCTX_APPCONTAINER: DWORD = 0x400000;
static CLSCTX_ACTIVATE_AAA_AS_IU: DWORD = 0x800000;
static CLSCTX_PS_DLL: DWORD = 0x80000000;
static COINIT_APARTMENTTHREADED: DWORD = 0x2;
static COINIT_MULTITHREADED: DWORD = 0x0;
static COINIT_DISABLE_OLE1DDE: DWORD = 0x4;
static COINIT_SPEED_OVER_MEMORY: DWORD = 0x8;
static S_OK: HRESULT = 0x00000000;
static S_FALSE: HRESULT = 0x00000001;
static CLASS_E_NOAGGREGATION: HRESULT = 0x80040110;
static E_NOINTERFACE: HRESULT = 0x80004002;
static REGDB_E_CLASSNOTREG: HRESULT = 0x80040154;
static REGDB_E_IIDNOTREG: HRESULT = 0x80040155;
static RPC_E_CHANGED_MODE: HRESULT = 0x80010106;
// {13709620-C279-11CE-A49E-444553540000}
DEFINE_GUID!(CLSID_ShApp, 0x13709620, 0xC279, 0x11CE, 0xA4,0x9E, 0x44,0x45,0x53,0x54,0x00,0x00)
}
#[allow(non_snake_case)]
mod types {
use libc::c_void;
#[repr(C)]
pub struct GUID {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8, ..8],
}
pub type BOOL = u32;
pub type DWORD = u32;
pub type HRESULT = u32;
pub type LONG = i32;
pub type ULONG = u32;
pub type WORD = u16;
pub type LCID = DWORD;
pub type CLSID = GUID;
pub type FMTID = GUID;
pub type IID = GUID;
pub type REFGUID = *const GUID;
pub type REFCLSID = *const CLSID;
pub type REFIID = *const IID;
pub type REFFMTID = *const FMTID;
pub type DISPID = LONG;
pub type MEMBERID = DISPID;
pub type OLECHAR = u16;
pub type BSTR = *mut OLECHAR;
pub type LPBSTR = *mut BSTR;
#[repr(C)]
pub struct DISPPARAMS {
rgvarg: *mut VARIANTARG,
rgdispidNamedArgs: *mut DISPID,
cArgs: u32,
cNamedArgs: u32,
}
pub struct VARIANT;
type VARIANTARG = VARIANT;
pub struct EXCEPINFO;
pub type ComPtr = *mut c_void;
#[repr(C)]
pub struct IUnknown {
pub __vtable: *mut IUnknown_vtable,
}
//pub static IID_IUnknown: &'static str = "00000000-0000-0000-C000-000000000046";
DEFINE_GUID!(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)
#[repr(C)]
pub struct IUnknown_vtable {
pub QueryInterface: extern "stdcall" fn(ComPtr, REFIID, *mut ComPtr) -> HRESULT,
pub AddRef: extern "stdcall" fn(ComPtr) -> ULONG,
pub Release: extern "stdcall" fn(ComPtr) -> ULONG,
}
#[repr(C)]
pub struct IClassFactory {
pub __vtable: *mut IClassFactory_vtable,
}
//pub static IID_IClassFactory: &'static str = "00000001-0000-0000-C000-000000000046";
DEFINE_GUID!(IID_IClassFactory, 0x00000001, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46)
#[repr(C)]
pub struct IClassFactory_vtable {
pub __base: IUnknown_vtable,
pub CreateInstance: extern "stdcall" fn(ComPtr, *mut IUnknown, REFIID, *mut ComPtr) -> HRESULT,
pub LockServer: extern "stdcall" fn(ComPtr, BOOL) -> HRESULT,
}
#[repr(C)]
pub struct IDispatch {
pub __vtable: *mut IDispatch_vtable,
}
//pub static IID_IDispatch: &'static str = "00020400-0000-0000-C000000000000046";
DEFINE_GUID!(IID_IDispatch, 0x00020400, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46)
#[repr(C)]
pub struct IDispatch_vtable {
pub __base: IUnknown_vtable,
pub GetTypeInfoCount: extern "stdcall" fn(ComPtr, *mut u32) -> HRESULT,
pub GetTypeInfo: extern "stdcall" fn(ComPtr, u32, LCID, *mut *mut ITypeInfo) -> HRESULT,
pub GetIDsOfNames: extern "stdcall" fn(ComPtr, REFIID, *mut BSTR, u32, LCID, *mut DISPID) -> HRESULT,
pub Invoke: extern "stdcall" fn(ComPtr, DISPID, REFIID, LCID, WORD, *mut DISPPARAMS, *mut VARIANT, *mut EXCEPINFO, *mut u32) -> HRESULT,
}
#[repr(C)]
pub struct ITypeInfo {
pub __vtable: *mut ITypeInfo_vtable,
}
//pub static IID_ITypeInfo: &'static str = "00020401-0000-0000-C000-000000000046";
DEFINE_GUID!(IID_ITypeInfo, 0x00020401, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46)
#[repr(C)]
//#[idl="oaidl.idl"]
pub struct ITypeInfo_vtable {
pub base: IUnknown_vtable,
// TODO
}
#[repr(C)]
pub struct IShellDispatch {
pub __vtable: *mut IShellDispatch_vtable,
}
//pub static IID_IShellDispatch: &'static str = "d8f015c0-c278-11ce-a49e-444553540000";
DEFINE_GUID!(IID_IShellDispatch, 0xd8f015c0, 0xc278, 0x11ce, 0xa4,0x9e, 0x44,0x45,0x53,0x54,0x00,0x00)
#[repr(C)]
//#[idl="shldisp.idl"]
pub struct IShellDispatch_vtable {
pub __base: IDispatch_vtable,
pub Application: extern "stdcall" fn(ComPtr, *mut *mut IDispatch) -> HRESULT,
pub Parent: extern "stdcall" fn(ComPtr, *mut *mut IDispatch) -> HRESULT,
pub NameSpace: *mut c_void, // extern "stdcall" fn(ComPtr, VARIANT, *mut *mut Folder) -> HRESULT,
pub BrowseForFolder: *mut c_void, // extern "stdcall" fn(ComPtr, u32, BSTR, u32, *mut *mut Folder) -> HRESULT,
pub Windows: extern "stdcall" fn(ComPtr, *mut *mut IDispatch) -> HRESULT,
pub Open: *mut c_void, // extern "stdcall" fn(ComPtr, VARIANT) -> HRESULT,
pub Explore: *mut c_void, // extern "stdcall" fn(ComPtr, VARIANT) -> HRESULT,
pub MinimizeAll: extern "stdcall" fn(ComPtr) -> HRESULT,
pub UndoMinimizeALL: extern "stdcall" fn(ComPtr) -> HRESULT,
pub FileRun: extern "stdcall" fn(ComPtr) -> HRESULT,
pub CascadeWindows: extern "stdcall" fn(ComPtr) -> HRESULT,
pub TileVertically: extern "stdcall" fn(ComPtr) -> HRESULT,
pub TileHorizontally: extern "stdcall" fn(ComPtr) -> HRESULT,
pub ShutdownWindows: extern "stdcall" fn(ComPtr) -> HRESULT,
pub Suspend: extern "stdcall" fn(ComPtr) -> HRESULT,
pub EjectPC: extern "stdcall" fn(ComPtr) -> HRESULT,
pub SetTime: extern "stdcall" fn(ComPtr) -> HRESULT,
pub TrayProperties: extern "stdcall" fn(ComPtr) -> HRESULT,
pub Help: extern "stdcall" fn(ComPtr) -> HRESULT,
pub FindFiles: extern "stdcall" fn(ComPtr) -> HRESULT,
pub FindComputer: extern "stdcall" fn(ComPtr) -> HRESULT,
pub RefreshMenu: extern "stdcall" fn(ComPtr) -> HRESULT,
pub ControlPanelItem: extern "stdcall" fn(ComPtr, BSTR) -> HRESULT,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment