├── src ├── win32 │ ├── mod.rs │ ├── com_pointer.rs │ ├── gui.rs │ ├── ffi.rs │ └── client_site.rs └── lib.rs ├── Cargo.toml ├── License.txt └── examples └── synth.rs /src/win32/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code, non_snake_case)] 2 | mod client_site; 3 | mod com_pointer; 4 | mod gui; 5 | #[allow(dead_code, non_snake_case, non_upper_case_globals)] 6 | mod ffi; 7 | 8 | pub use win32::gui::new_plugin_gui; 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vst-gui" 3 | version = "0.2.0" 4 | authors = ["Alexander Agafonov "] 5 | description = "An extension to the 'rust-vst' crate to create VST plugin GUIs" 6 | license = "MIT" 7 | keywords = ["gui", "plugin", "vst", "vst2"] 8 | 9 | [dependencies] 10 | vst = "^0.2" 11 | 12 | [target.'cfg(windows)'.dependencies] 13 | memoffset = "0.6.1" 14 | 15 | [target.'cfg(windows)'.dependencies.winapi] 16 | version = "0.3.9" 17 | features = [ 18 | "combaseapi", 19 | "libloaderapi", 20 | "oaidl", 21 | "oleauto", 22 | "winbase", 23 | "winerror", 24 | "winuser" 25 | ] 26 | 27 | [[example]] 28 | name = "synth" 29 | crate-type = ["cdylib"] 30 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alexander Agafonov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(windows)] 2 | #[macro_use] 3 | extern crate memoffset; 4 | #[cfg(windows)] 5 | #[macro_use] 6 | extern crate winapi; 7 | extern crate vst; 8 | 9 | use std::error::Error; 10 | use std::os::raw::c_void; 11 | 12 | #[cfg(windows)] 13 | mod win32; 14 | 15 | mod lib { 16 | use std::error::Error; 17 | use std::os::raw::c_void; 18 | 19 | pub type JavascriptCallback = Box String>; 20 | 21 | pub trait PluginGui { 22 | fn size(&self) -> (i32, i32); 23 | fn position(&self) -> (i32, i32); 24 | fn close(&mut self); 25 | fn open(&mut self, parent_handle: *mut c_void) -> bool; 26 | fn is_open(&mut self) -> bool; 27 | fn execute(&self, javascript_code: &str) -> Result<(), Box>; 28 | } 29 | } 30 | 31 | pub struct PluginGui { 32 | gui: Box, 33 | } 34 | 35 | impl PluginGui { 36 | // Calls the Javascript 'eval' function with the specified argument. 37 | // This method always returns an error when the plugin window is closed. 38 | pub fn execute(&self, javascript_code: &str) -> Result<(), Box> { 39 | self.gui.execute(javascript_code) 40 | } 41 | } 42 | 43 | impl vst::editor::Editor for PluginGui { 44 | fn size(&self) -> (i32, i32) { 45 | self.gui.size() 46 | } 47 | 48 | fn position(&self) -> (i32, i32) { 49 | self.gui.position() 50 | } 51 | 52 | fn close(&mut self) { 53 | self.gui.close() 54 | } 55 | 56 | fn open(&mut self, parent_handle: *mut c_void) -> bool { 57 | self.gui.open(parent_handle) 58 | } 59 | 60 | fn is_open(&mut self) -> bool { 61 | self.gui.is_open() 62 | } 63 | } 64 | 65 | pub use lib::JavascriptCallback; 66 | 67 | pub fn new_plugin_gui( 68 | html_document: String, 69 | js_callback: JavascriptCallback, 70 | window_size: Option<(i32, i32)>) -> PluginGui 71 | { 72 | #[cfg(windows)] 73 | { 74 | PluginGui { 75 | gui: win32::new_plugin_gui(html_document, js_callback, window_size) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/win32/com_pointer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null_mut; 2 | 3 | use winapi::Interface; 4 | use winapi::shared::winerror::S_OK; 5 | use winapi::shared::minwindef::LPVOID; 6 | use winapi::um::unknwnbase::IUnknown; 7 | 8 | pub struct ComPointer { 9 | pointer: *mut T, 10 | } 11 | 12 | impl ComPointer { 13 | pub fn new() -> Self { 14 | ComPointer { 15 | pointer: null_mut(), 16 | } 17 | } 18 | 19 | pub fn from_raw(pointer: *mut T) -> Self { 20 | ComPointer { 21 | pointer: pointer, 22 | } 23 | } 24 | 25 | // Note: this method doesn't modify the reference counter 26 | pub fn as_ptr(&self) -> *mut T { 27 | self.pointer 28 | } 29 | 30 | // Note: this method doesn't modify the reference counter 31 | pub fn as_mut_ptr(&mut self) -> &mut *mut T { 32 | &mut self.pointer 33 | } 34 | 35 | pub fn get(&self) -> Option<&T> { 36 | if self.pointer != null_mut() { 37 | unsafe { 38 | Some(&*self.pointer) 39 | } 40 | } else { 41 | None 42 | } 43 | } 44 | 45 | pub fn query_interface(&self) -> ComPointer { 46 | let mut result = ComPointer::::new(); 47 | 48 | let success = if self.pointer != null_mut() { 49 | unsafe { 50 | let result_pointer = result.as_mut_ptr() 51 | as *mut *mut I 52 | as *mut LPVOID; 53 | 54 | (*(self.pointer as *mut IUnknown)).QueryInterface( 55 | &I::uuidof(), result_pointer) == S_OK 56 | } 57 | } else { 58 | false 59 | }; 60 | 61 | match success { 62 | true => result, 63 | false => ComPointer::::new(), 64 | } 65 | } 66 | } 67 | 68 | impl Drop for ComPointer { 69 | fn drop(&mut self) { 70 | if self.pointer != null_mut() { 71 | unsafe { 72 | (*(self.pointer as *mut IUnknown)).Release(); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/synth.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate vst; 3 | extern crate vst_gui; 4 | 5 | use std::f32::consts::PI; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | use vst::buffer::AudioBuffer; 9 | use vst::editor::Editor; 10 | use vst::plugin::{Category, Plugin, Info}; 11 | 12 | const HTML: &'static str = r#" 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 52 | 53 | "#; 54 | 55 | struct Oscillator { 56 | pub frequency: f32, 57 | pub waveform: f32, 58 | pub phase: f32, 59 | pub amplitude: f32, 60 | } 61 | 62 | fn create_javascript_callback( 63 | oscillator: Arc>) -> vst_gui::JavascriptCallback 64 | { 65 | Box::new(move |message: String| { 66 | let mut tokens = message.split_whitespace(); 67 | 68 | let command = tokens.next().unwrap_or(""); 69 | let argument = tokens.next().unwrap_or("").parse::(); 70 | 71 | let mut locked_oscillator = oscillator.lock().unwrap(); 72 | 73 | match command { 74 | "getWaveform" => { 75 | return locked_oscillator.waveform.to_string(); 76 | }, 77 | "getFrequency" => { 78 | return locked_oscillator.frequency.to_string(); 79 | }, 80 | "setWaveform" => { 81 | if argument.is_ok() { 82 | locked_oscillator.waveform = argument.unwrap(); 83 | } 84 | }, 85 | "setFrequency" => { 86 | if argument.is_ok() { 87 | locked_oscillator.frequency = argument.unwrap(); 88 | } 89 | }, 90 | _ => {} 91 | } 92 | 93 | String::new() 94 | }) 95 | } 96 | 97 | struct ExampleSynth { 98 | sample_rate: f32, 99 | // We access this object both from a UI thread and from an audio processing 100 | // thread. 101 | oscillator: Arc>, 102 | } 103 | 104 | impl Default for ExampleSynth { 105 | fn default() -> ExampleSynth { 106 | let oscillator = Arc::new(Mutex::new( 107 | Oscillator { 108 | frequency: 440.0, 109 | waveform: 0.0, 110 | phase: 0.0, 111 | amplitude: 0.1, 112 | } 113 | )); 114 | 115 | ExampleSynth { 116 | sample_rate: 44100.0, 117 | oscillator: oscillator.clone(), 118 | } 119 | } 120 | } 121 | 122 | impl Plugin for ExampleSynth { 123 | fn get_info(&self) -> Info { 124 | Info { 125 | name: "Example Synth".to_string(), 126 | vendor: "rust-vst-gui".to_string(), 127 | unique_id: 9614, 128 | category: Category::Synth, 129 | inputs: 2, 130 | outputs: 2, 131 | parameters: 0, 132 | initial_delay: 0, 133 | f64_precision: false, 134 | ..Info::default() 135 | } 136 | } 137 | 138 | fn set_sample_rate(&mut self, sample_rate: f32) { 139 | self.sample_rate = sample_rate as f32; 140 | } 141 | 142 | fn process(&mut self, buffer: &mut AudioBuffer) { 143 | let mut oscillator = self.oscillator.lock().unwrap(); 144 | 145 | let actual_phase = oscillator.phase; 146 | let actual_frequency = oscillator.frequency; 147 | 148 | let phase = |sample_index: usize| { 149 | actual_phase + 2.0 * PI * actual_frequency * 150 | (sample_index as f32) / self.sample_rate 151 | }; 152 | 153 | for (_, output) in buffer.zip() { 154 | for (index, sample) in output.iter_mut().enumerate() { 155 | let sine_wave = phase(index).sin(); 156 | let square_wave = phase(index).cos().signum(); 157 | 158 | *sample = oscillator.amplitude * ( 159 | sine_wave * (1.0 - oscillator.waveform) + 160 | square_wave * oscillator.waveform); 161 | } 162 | } 163 | 164 | oscillator.phase = phase(buffer.samples()) % (2.0 * PI); 165 | } 166 | 167 | fn get_editor(&mut self) -> Option> { 168 | let gui = vst_gui::new_plugin_gui( 169 | String::from(HTML), 170 | create_javascript_callback(self.oscillator.clone()), 171 | None); 172 | Some(Box::new(gui)) 173 | } 174 | } 175 | 176 | plugin_main!(ExampleSynth); 177 | -------------------------------------------------------------------------------- /src/win32/gui.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::ffi::OsStr; 3 | use std::mem::zeroed; 4 | use std::os::raw::c_void; 5 | use std::os::windows::ffi::OsStrExt; 6 | use std::ptr::{null, null_mut}; 7 | use std::rc::Rc; 8 | 9 | use winapi::Interface; 10 | use winapi::shared::guiddef::*; 11 | use winapi::shared::minwindef::*; 12 | use winapi::shared::windef::*; 13 | use winapi::shared::winerror::*; 14 | use winapi::shared::wtypes::*; 15 | use winapi::um::combaseapi::*; 16 | use winapi::um::libloaderapi::*; 17 | use winapi::um::oaidl::*; 18 | use winapi::um::oaidl::DISPID; // Required to eliminate ambiguity 19 | use winapi::um::objidlbase::*; 20 | use winapi::um::oleauto::*; 21 | use winapi::um::winnt::*; 22 | use winapi::um::winuser::*; 23 | 24 | use lib::{JavascriptCallback, PluginGui}; 25 | use win32::client_site::*; 26 | use win32::com_pointer::*; 27 | use win32::ffi::*; 28 | 29 | fn error(message: &str) -> Box { 30 | From::from(message) 31 | } 32 | 33 | struct Window { 34 | handle: HWND, 35 | } 36 | 37 | impl Window { 38 | // The "plugin_window" string in utf16. 39 | const CLASS_NAME: [u16; 14] = [ 40 | 0x0070, 0x006c, 0x0075, 0x0067, 0x0069, 0x006e, 0x005f, 0x0077, 41 | 0x0069, 0x006e, 0x0064, 0x006f, 0x0077, 0x0000]; 42 | 43 | pub fn new(parent: HWND, size: Option<(i32, i32)>) -> Window { 44 | Window::register_window_class(); 45 | 46 | let window_size = size.unwrap_or_else(|| Window::default_size()); 47 | let handle = unsafe { 48 | const STYLE: DWORD = WS_CHILD | WS_VISIBLE; 49 | const STYLE_EXTENDED: DWORD = 0; 50 | 51 | CreateWindowExW( 52 | STYLE_EXTENDED, 53 | Window::CLASS_NAME.as_ptr(), 54 | null(), /*window_name*/ 55 | STYLE, 56 | 0, /*x*/ 57 | 0, /*y*/ 58 | window_size.0, 59 | window_size.1, 60 | parent, 61 | null_mut(), /*menu*/ 62 | GetModuleHandleW(null()), 63 | null_mut()) 64 | }; 65 | 66 | Window { 67 | handle: handle, 68 | } 69 | } 70 | 71 | fn size(&self) -> (i32, i32) { 72 | let mut rectangle = 73 | RECT {left: 0, top: 0, right: 0, bottom: 0}; 74 | 75 | unsafe { 76 | GetWindowRect(self.handle, &mut rectangle); 77 | } 78 | 79 | let width = (rectangle.right - rectangle.left) as i32; 80 | let height = (rectangle.bottom - rectangle.top) as i32; 81 | 82 | (width, height) 83 | } 84 | 85 | fn default_size() -> (i32, i32) { 86 | unsafe { 87 | let width = GetSystemMetrics(SM_CXSCREEN) / 2; 88 | let height = GetSystemMetrics(SM_CYSCREEN) / 2; 89 | 90 | (width, height) 91 | } 92 | } 93 | 94 | fn register_window_class() { 95 | let class = WNDCLASSW { 96 | style: CS_DBLCLKS, 97 | lpfnWndProc: Some(Window::window_procedure), 98 | cbClsExtra: 0, 99 | cbWndExtra: 0, 100 | hInstance: unsafe { GetModuleHandleW(null()) }, 101 | hIcon: null_mut(), 102 | hCursor: unsafe { LoadCursorW(null_mut(), IDC_ARROW) }, 103 | hbrBackground: null_mut(), 104 | lpszMenuName: null(), 105 | lpszClassName: Window::CLASS_NAME.as_ptr() 106 | }; 107 | 108 | unsafe { 109 | RegisterClassW(&class); 110 | } 111 | } 112 | 113 | extern "system" fn window_procedure( 114 | handle: HWND, message: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT 115 | { 116 | match message { 117 | WM_GETDLGCODE => { 118 | return DLGC_WANTALLKEYS; 119 | }, 120 | _ => {} 121 | } 122 | unsafe { 123 | DefWindowProcW(handle, message, wparam, lparam) 124 | } 125 | } 126 | } 127 | 128 | struct WebBrowser { 129 | browser: ComPointer, 130 | } 131 | 132 | impl WebBrowser { 133 | fn new( 134 | window_handle: HWND, 135 | html_document: String, 136 | js_callback: Rc) -> 137 | Result> 138 | { 139 | unsafe { 140 | OleInitialize(null_mut()); 141 | } 142 | 143 | let browser = WebBrowser { 144 | browser: WebBrowser::new_browser_com_object()? 145 | }; 146 | 147 | browser.embed(window_handle, js_callback)?; 148 | 149 | // https://msdn.microsoft.com/library/aa752047 150 | browser.open_blank_page()?; 151 | browser.load_html_document(html_document)?; 152 | 153 | Ok(browser) 154 | } 155 | 156 | fn new_browser_com_object() -> 157 | Result, Box> 158 | { 159 | let mut web_browser = ComPointer::::new(); 160 | 161 | let result = unsafe { 162 | CoCreateInstance( 163 | &CLSID_WebBrowser, 164 | null_mut(), 165 | CLSCTX_INPROC, 166 | &IWebBrowser2::uuidof(), 167 | web_browser.as_mut_ptr() 168 | as *mut *mut IWebBrowser2 169 | as *mut LPVOID) 170 | }; 171 | 172 | if result == S_OK && web_browser.get().is_some() { 173 | Ok(web_browser) 174 | } else { 175 | Err(error("Couldn't get an instance of the 'IWebBrowser2' class")) 176 | } 177 | } 178 | 179 | fn browser(&self) -> &IWebBrowser2 { 180 | self.browser 181 | .get() 182 | .unwrap() 183 | } 184 | 185 | fn open_blank_page(&self) -> Result<(), Box> { 186 | let url_buffer: Vec = 187 | OsStr::new("about:blank").encode_wide().collect(); 188 | 189 | unsafe { 190 | let url = SysAllocStringLen( 191 | url_buffer.as_ptr(), 192 | url_buffer.len() as u32); 193 | 194 | if self.browser().Navigate( 195 | url, 196 | null_mut(), 197 | null_mut(), 198 | null_mut(), 199 | null_mut()) != S_OK 200 | { 201 | return Err(error("Couldn't open a blank page")) 202 | } 203 | 204 | SysFreeString(url); 205 | } 206 | 207 | Ok(()) 208 | } 209 | 210 | fn document_dispatch(&self) -> 211 | Result, Box> 212 | { 213 | let mut result = ComPointer::::new(); 214 | 215 | let success = unsafe { 216 | self.browser().get_Document(result.as_mut_ptr()) == S_OK && 217 | result.get().is_some() 218 | }; 219 | 220 | match success { 221 | true => Ok(result), 222 | false => Err( 223 | error( 224 | "The 'IWebBrowser2::get_Document' method returned an \ 225 | error")), 226 | } 227 | } 228 | 229 | fn window_dispatch(&self) -> Result, Box> { 230 | let document_dispatch = self.document_dispatch()?; 231 | 232 | let window_dispatch = document_dispatch 233 | .query_interface::() 234 | .get() 235 | .map(|document| { 236 | let mut window = ComPointer::::new(); 237 | 238 | unsafe { 239 | if document.get_parentWindow(window.as_mut_ptr()) == S_OK { 240 | window 241 | } else { 242 | ComPointer::::new() 243 | } 244 | } 245 | }) 246 | .map(|window| { 247 | window.query_interface::() 248 | }) 249 | .unwrap_or(ComPointer::::new()); 250 | 251 | if window_dispatch.get().is_some() { 252 | Ok(window_dispatch) 253 | } else { 254 | Err( 255 | error( 256 | "Couldn't get an instance of the 'IDispatch' class \ 257 | for the document window")) 258 | } 259 | } 260 | 261 | fn load_html_document( 262 | &self, html_document: String) -> Result<(), Box> 263 | { 264 | // TODO: do not assume the document is ready 265 | let document_dispatch = self.document_dispatch()?; 266 | 267 | let stream = ComPointer::::from_raw( 268 | unsafe { 269 | SHCreateMemStream( 270 | html_document.as_ptr(), 271 | html_document.len() as u32) 272 | }); 273 | 274 | stream 275 | .get() 276 | .ok_or(error("Couldn't get an instance of the 'IStream' class"))?; 277 | 278 | let success = document_dispatch 279 | .query_interface::() 280 | .get() 281 | .map(|persist_stream| { 282 | unsafe { 283 | persist_stream.InitNew() == S_OK && 284 | persist_stream.Load(stream.as_ptr()) == S_OK 285 | } 286 | }) 287 | .ok_or( 288 | error( 289 | "Couldn't get an instance of the 'IPersistStreamInit' \ 290 | class"))?; 291 | 292 | match success { 293 | true => Ok(()), 294 | false => Err(error("Couldn't load an HTML document")), 295 | } 296 | } 297 | 298 | fn embed( 299 | &self, 300 | window_handle: HWND, 301 | js_callback: Rc) -> Result<(), Box> 302 | { 303 | let ole_object = self.browser.query_interface::(); 304 | 305 | ole_object 306 | .get() 307 | .ok_or( 308 | error("Couldn't get an instance of the 'IOleObject' class"))?; 309 | 310 | let ole_in_place_object = 311 | ole_object.query_interface::(); 312 | 313 | ole_in_place_object 314 | .get() 315 | .ok_or( 316 | error( 317 | "Couldn't get an instance of the 'IOleInPlaceObject' \ 318 | class"))?; 319 | 320 | let client_site = new_client_site( 321 | window_handle, ole_in_place_object, js_callback); 322 | 323 | let success = { 324 | let mut rectangle = RECT {left: 0, top: 0, right: 0, bottom: 0}; 325 | 326 | unsafe { 327 | GetClientRect(window_handle, &mut rectangle); 328 | 329 | ole_object.get().unwrap().SetClientSite( 330 | client_site.as_ptr()) == S_OK && 331 | ole_object.get().unwrap().DoVerb( 332 | OLEIVERB_INPLACEACTIVATE, 333 | null_mut(), 334 | client_site.as_ptr(), 335 | 0, 336 | window_handle, 337 | &rectangle) == S_OK && 338 | self.browser().put_Width(rectangle.right) == S_OK && 339 | self.browser().put_Height(rectangle.bottom) == S_OK && 340 | self.browser().put_Visible(TRUE as VARIANT_BOOL) == S_OK 341 | } 342 | }; 343 | 344 | match success { 345 | true => Ok(()), 346 | false => Err(error("Couldn't reveal an HTML browser")), 347 | } 348 | } 349 | 350 | fn execute(&self, javascript_code: &str) -> Result<(), Box> { 351 | let window_dispatch = self.window_dispatch()?; 352 | 353 | let argument_value: Vec = OsStr::new(javascript_code) 354 | .encode_wide() 355 | .collect(); 356 | 357 | unsafe { 358 | let mut argument: VARIANT = zeroed(); 359 | 360 | VariantInit(&mut argument); 361 | 362 | argument.n1.n2_mut().vt = VT_BSTR as u16; 363 | *argument.n1.n2_mut().n3.bstrVal_mut() = SysAllocStringLen( 364 | argument_value.as_ptr(), 365 | argument_value.len() as u32); 366 | 367 | let mut parameters = DISPPARAMS { 368 | rgvarg: &mut argument, 369 | rgdispidNamedArgs: null_mut(), 370 | cArgs: 1, 371 | cNamedArgs: 0, 372 | }; 373 | 374 | // TODO: cache the 'window_dispatch' object and the method id 375 | 376 | let result = if window_dispatch 377 | .get() 378 | .unwrap() 379 | .Invoke( 380 | WebBrowser::window_eval_method_id(&window_dispatch)?, 381 | &IID_NULL, 382 | LOCALE_SYSTEM_DEFAULT, 383 | DISPATCH_METHOD, 384 | &mut parameters, 385 | null_mut(), 386 | null_mut(), 387 | null_mut()) == S_OK 388 | { 389 | Ok(()) 390 | } else { 391 | Err(error("Execution of the Javascript code failed")) 392 | }; 393 | 394 | VariantClear(&mut argument); 395 | result 396 | } 397 | } 398 | 399 | fn window_eval_method_id(window_dispatch: &ComPointer) -> 400 | Result> 401 | { 402 | assert!(window_dispatch.get().is_some()); 403 | 404 | // The "eval" string in utf16. 405 | let mut method_name: Vec = 406 | vec![0x0065, 0x0076, 0x0061, 0x006c, 0x0000]; 407 | let mut id: DISPID = 0; 408 | 409 | let result = unsafe { 410 | window_dispatch 411 | .get() 412 | .unwrap() 413 | .GetIDsOfNames( 414 | &IID_NULL, 415 | &mut method_name.as_mut_ptr(), 1, LOCALE_SYSTEM_DEFAULT, 416 | &mut id) 417 | }; 418 | 419 | if result == S_OK { 420 | Ok(id) 421 | } else { 422 | Err(error("Couldn't get an ID for the 'eval' method")) 423 | } 424 | } 425 | } 426 | 427 | struct Gui { 428 | html_document: String, 429 | js_callback: Rc, 430 | web_browser: Option, 431 | window: Option, 432 | window_size: Option<(i32, i32)>, 433 | } 434 | 435 | impl PluginGui for Gui { 436 | fn size(&self) -> (i32, i32) { 437 | match self.window { 438 | Some(ref window) => window.size(), 439 | None => (0, 0) 440 | } 441 | } 442 | 443 | fn position(&self) -> (i32, i32) { 444 | (0, 0) 445 | } 446 | 447 | fn close(&mut self) { 448 | self.web_browser = None; 449 | self.window = None; 450 | } 451 | 452 | fn open(&mut self, parent_handle: *mut c_void) -> bool { 453 | let window = Window::new(parent_handle as HWND, self.window_size); 454 | 455 | match WebBrowser::new( 456 | window.handle, 457 | self.html_document.clone(), 458 | self.js_callback.clone()) { 459 | Ok(browser) => { 460 | self.window = Some(window); 461 | self.web_browser = Some(browser); 462 | true 463 | }, 464 | Err(_) => false // TODO: Display errors 465 | } 466 | } 467 | 468 | fn is_open(&mut self) -> bool { 469 | self.window.is_some() 470 | } 471 | 472 | fn execute(&self, javascript_code: &str) -> Result<(), Box> { 473 | if let Some(ref web_browser) = self.web_browser { 474 | web_browser.execute(javascript_code) 475 | } else { 476 | Err(error("The plugin window is closed")) 477 | } 478 | } 479 | } 480 | 481 | pub fn new_plugin_gui( 482 | html_document: String, 483 | js_callback: JavascriptCallback, 484 | window_size: Option<(i32, i32)>) -> Box 485 | { 486 | Box::new( 487 | Gui { 488 | html_document: html_document, 489 | js_callback: Rc::new(js_callback), 490 | web_browser: None, 491 | window: None, 492 | window_size: window_size, 493 | }) 494 | } 495 | -------------------------------------------------------------------------------- /src/win32/ffi.rs: -------------------------------------------------------------------------------- 1 | // This module contains declarations which are missing from the 'winapi' crate. 2 | 3 | use std::os::raw::*; 4 | 5 | // Non-asterisk imports are required to eliminate ambiguity 6 | use winapi::shared::guiddef::*; 7 | use winapi::shared::minwindef::*; 8 | use winapi::shared::windef::*; 9 | use winapi::shared::windef::SIZE; 10 | use winapi::shared::wtypes::*; 11 | use winapi::shared::wtypesbase::*; 12 | use winapi::um::oaidl::*; 13 | use winapi::um::objidl::{IMoniker, IPersist, IPersistVtbl}; 14 | use winapi::um::objidlbase::*; 15 | use winapi::um::unknwnbase::*; 16 | use winapi::um::winnt::*; 17 | use winapi::um::winuser::*; 18 | 19 | #[link(name = "ole32")] 20 | extern "system" { 21 | pub fn OleInitialize(_: LPVOID) -> HRESULT; 22 | pub fn OleSetContainedObject( 23 | pUnknown: *mut IUnknown, fContained: BOOL) -> HRESULT; 24 | } 25 | 26 | #[link(name = "shlwapi")] 27 | extern "system" { 28 | pub fn SHCreateMemStream(pInit: *const BYTE, cbInit: UINT) -> *mut IStream; 29 | } 30 | 31 | // We don't use these types so we don't need exact declarations. 32 | pub type IDataObject = IUnknown; 33 | pub type IDropTarget = IUnknown; 34 | pub type IOleCommandTarget = IUnknown; 35 | pub type IOleContainer = IUnknown; 36 | pub type IOleInPlaceActiveObject = IUnknown; 37 | pub type IHTMLWindow2 = IUnknown; 38 | 39 | pub const OLEIVERB_INPLACEACTIVATE: LONG = -5; 40 | 41 | RIDL!{ 42 | #[uuid(0x00000112, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 43 | interface IOleObject(IOleObjectVtbl) : IUnknown(IUnknownVtbl) { 44 | fn SetClientSite(pClientSite: *mut IOleClientSite,) -> HRESULT, 45 | fn Unused_GetClientSite() -> HRESULT, 46 | fn Unused_SetHostNames() -> HRESULT, 47 | fn Unused_Close() -> HRESULT, 48 | fn Unused_SetMoniker() -> HRESULT, 49 | fn Unused_GetMoniker() -> HRESULT, 50 | fn Unused_InitFromData() -> HRESULT, 51 | fn Unused_GetClipboardData() -> HRESULT, 52 | fn DoVerb( 53 | iVerb: LONG, 54 | lpmsg: LPMSG, 55 | pActiveSite: *mut IOleClientSite, 56 | lindex: LONG, 57 | hwndParent: HWND, 58 | lprcPosRect: LPCRECT,) -> HRESULT, 59 | fn Unused_EnumVerbs() -> HRESULT, 60 | fn Unused_Update() -> HRESULT, 61 | fn Unused_IsUpToDate() -> HRESULT, 62 | fn Unused_GetUserClassID() -> HRESULT, 63 | fn Unused_GetUserType() -> HRESULT, 64 | fn Unused_SetExtent() -> HRESULT, 65 | fn Unused_GetExtent() -> HRESULT, 66 | fn Unused_Advise() -> HRESULT, 67 | fn Unused_Unadvise() -> HRESULT, 68 | fn Unused_EnumAdvise() -> HRESULT, 69 | fn Unused_GetMiscStatus() -> HRESULT, 70 | fn Unused_SetColorScheme() -> HRESULT, 71 | } 72 | } 73 | 74 | RIDL!{ 75 | #[uuid(0x00000118, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 76 | interface IOleClientSite(IOleClientSiteVtbl) : IUnknown(IUnknownVtbl) { 77 | fn SaveObject() -> HRESULT, 78 | fn GetMoniker( 79 | dwAssign: DWORD, 80 | dwWhichMoniker: DWORD, 81 | ppmk: *mut *mut IMoniker,) -> HRESULT, 82 | fn GetContainer(ppContainer: *mut *mut IOleContainer,) -> HRESULT, 83 | fn ShowObject() -> HRESULT, 84 | fn OnShowWindow(fShow: BOOL,) -> HRESULT, 85 | fn RequestNewObjectLayout() -> HRESULT, 86 | } 87 | } 88 | 89 | RIDL!{ 90 | #[uuid(0x00000114, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 91 | interface IOleWindow(IOleWindowVtbl) : IUnknown(IUnknownVtbl) { 92 | fn GetWindow(phwnd: *mut HWND,) -> HRESULT, 93 | fn ContextSensitiveHelp(fEnterMode: BOOL,) -> HRESULT, 94 | } 95 | } 96 | 97 | RIDL!{ 98 | #[uuid(0x00000115, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 99 | interface IOleInPlaceUIWindow(IOleInPlaceUIWindowVtbl) : 100 | IOleWindow(IOleWindowVtbl) 101 | { 102 | fn GetBorder(lprectBorder: LPRECT,) -> HRESULT, 103 | fn RequestBorderSpace( 104 | pborderwidths: LPCRECT,) -> HRESULT, 105 | fn SetBorderSpace( 106 | pborderwidths: LPCRECT,) -> HRESULT, 107 | fn SetActiveObject( 108 | pActiveObject: *mut IOleInPlaceActiveObject, 109 | pszObjName: LPCOLESTR,) -> HRESULT, 110 | } 111 | } 112 | 113 | RIDL!{ 114 | #[uuid(0x00000116, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 115 | interface IOleInPlaceFrame(IOleInPlaceFrameVtbl) : 116 | IOleInPlaceUIWindow(IOleInPlaceUIWindowVtbl) 117 | { 118 | fn InsertMenus( 119 | hmenuShared: HMENU, 120 | lpMenuWidths: LPVOID,) -> HRESULT, 121 | fn SetMenu( 122 | hmenuShared: HMENU, 123 | holemenu: HGLOBAL, 124 | hwndActiveObject: HWND,) -> HRESULT, 125 | fn RemoveMenus( 126 | hmenuShared: HMENU,) -> HRESULT, 127 | fn SetStatusText( 128 | pszStatusText: LPCOLESTR,) -> HRESULT, 129 | fn EnableModeless( 130 | fEnable: BOOL,) -> HRESULT, 131 | fn TranslateAccelerator( 132 | lpmsg: LPMSG, 133 | wID: WORD,) -> HRESULT, 134 | } 135 | } 136 | 137 | STRUCT!{ 138 | struct OLEINPLACEFRAMEINFO { 139 | cb: UINT, 140 | fMDIApp: BOOL, 141 | hwndFrame: HWND, 142 | haccel: HACCEL, 143 | cAccelEntries: UINT, 144 | } 145 | } 146 | 147 | RIDL!{ 148 | #[uuid(0x00000119, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 149 | interface IOleInPlaceSite(IOleInPlaceSiteVtbl) : IOleWindow(IOleWindowVtbl) { 150 | fn CanInPlaceActivate() -> HRESULT, 151 | fn OnInPlaceActivate() -> HRESULT, 152 | fn OnUIActivate() -> HRESULT, 153 | fn GetWindowContext( 154 | ppFrame: *mut *mut IOleInPlaceFrame, 155 | ppDoc: *mut *mut IOleInPlaceUIWindow, 156 | lprcPosRect: LPRECT, 157 | lprcClipRect: LPRECT, 158 | lpFrameInfo: *mut OLEINPLACEFRAMEINFO,) -> HRESULT, 159 | fn Scroll(scrollExtant: SIZE,) -> HRESULT, 160 | fn OnUIDeactivate(fUndoable: BOOL,) -> HRESULT, 161 | fn OnInPlaceDeactivate() -> HRESULT, 162 | fn DiscardUndoState() -> HRESULT, 163 | fn DeactivateAndUndo() -> HRESULT, 164 | fn OnPosRectChange(lprcPosRect: LPCRECT,) -> HRESULT, 165 | } 166 | } 167 | 168 | RIDL!{ 169 | #[uuid(0x00000113, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 170 | interface IOleInPlaceObject(IOleInPlaceObjectVtbl) : IOleWindow(IOleWindowVtbl) { 171 | fn InPlaceDeactivate() -> HRESULT, 172 | fn UIDeactivate() -> HRESULT, 173 | fn SetObjectRects( 174 | lprcPosRect: LPCRECT, 175 | lprcClipRect: LPCRECT,) -> HRESULT, 176 | fn ReactivateAndUndo() -> HRESULT, 177 | } 178 | } 179 | 180 | RIDL!{ 181 | #[uuid(0xeab22ac1, 0x30c1, 0x11cf, 0xa7, 0xeb, 0x00, 0x00, 0xc0, 0x5b, 0xae, 0x0b)] 182 | interface IWebBrowser(IWebBrowserVtbl) : IDispatch(IDispatchVtbl) { 183 | fn Unused_GoBack() -> HRESULT, 184 | fn Unused_GoForward() -> HRESULT, 185 | fn Unused_GoHome() -> HRESULT, 186 | fn Unused_GoSearch() -> HRESULT, 187 | fn Navigate( 188 | url: BSTR, 189 | flags: *mut VARIANT, 190 | targetFrameName: *mut VARIANT, 191 | postData: *mut VARIANT, 192 | headers: *mut VARIANT,) -> HRESULT, 193 | fn Unused_Refresh() -> HRESULT, 194 | fn Unused_Refresh2() -> HRESULT, 195 | fn Unused_Stop() -> HRESULT, 196 | fn Unused_get_Application() -> HRESULT, 197 | fn Unused_get_Parent() -> HRESULT, 198 | fn Unused_get_Container() -> HRESULT, 199 | fn get_Document(ppDisp: *mut *mut IDispatch,) -> HRESULT, 200 | fn Unused_get_TopLevelContainer() -> HRESULT, 201 | fn Unused_get_Type() -> HRESULT, 202 | fn Unused_get_Left() -> HRESULT, 203 | fn Unused_put_Left() -> HRESULT, 204 | fn Unused_get_Top() -> HRESULT, 205 | fn Unused_put_Top() -> HRESULT, 206 | fn Unused_get_Width() -> HRESULT, 207 | fn put_Width(width: c_long,) -> HRESULT, 208 | fn Unused_get_Height() -> HRESULT, 209 | fn put_Height(height: c_long,) -> HRESULT, 210 | fn Unused_get_LocationName() -> HRESULT, 211 | fn Unused_get_LocationURL() -> HRESULT, 212 | fn Unused_get_Busy() -> HRESULT, 213 | } 214 | } 215 | 216 | RIDL!{ 217 | #[uuid(0x0002df05, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] 218 | interface IWebBrowserApp(IWebBrowserAppVtbl) : IWebBrowser(IWebBrowserVtbl) { 219 | fn Unused_Quit() -> HRESULT, 220 | fn Unused_ClientToWindow() -> HRESULT, 221 | fn Unused_PutProperty() -> HRESULT, 222 | fn Unused_GetProperty() -> HRESULT, 223 | fn Unused_get_Name() -> HRESULT, 224 | fn Unused_get_HWND() -> HRESULT, 225 | fn Unused_get_FullName() -> HRESULT, 226 | fn Unused_get_Path() -> HRESULT, 227 | fn Unused_get_Visible() -> HRESULT, 228 | fn put_Visible(value: VARIANT_BOOL,) -> HRESULT, 229 | fn Unused_get_StatusBar() -> HRESULT, 230 | fn Unused_put_StatusBar() -> HRESULT, 231 | fn Unused_get_StatusText() -> HRESULT, 232 | fn Unused_put_StatusText() -> HRESULT, 233 | fn Unused_get_ToolBar() -> HRESULT, 234 | fn Unused_put_ToolBar() -> HRESULT, 235 | fn Unused_get_MenuBar() -> HRESULT, 236 | fn Unused_put_MenuBar() -> HRESULT, 237 | fn Unused_get_FullScreen() -> HRESULT, 238 | fn Unused_put_FullScreen() -> HRESULT, 239 | } 240 | } 241 | 242 | RIDL!{ 243 | #[uuid(0xd30c1661, 0xcdaf, 0x11d0, 0x8a, 0x3e, 0x00, 0xc0, 0x4f, 0xc9, 0xe2, 0x6e)] 244 | interface IWebBrowser2(IWebBrowser2Vtbl) : IWebBrowserApp(IWebBrowserAppVtbl) { 245 | fn Unused_Navigate2() -> HRESULT, 246 | fn Unused_QueryStatusWB() -> HRESULT, 247 | fn Unused_ExecWB() -> HRESULT, 248 | fn Unused_ShowBrowserBar() -> HRESULT, 249 | fn Unused_get_ReadyState() -> HRESULT, 250 | fn Unused_get_Offline() -> HRESULT, 251 | fn Unused_put_Offline() -> HRESULT, 252 | fn Unused_get_Silent() -> HRESULT, 253 | fn Unused_put_Silent() -> HRESULT, 254 | fn Unused_get_RegisterAsBrowser() -> HRESULT, 255 | fn Unused_put_RegisterAsBrowser() -> HRESULT, 256 | fn Unused_get_RegisterAsDropTarget() -> HRESULT, 257 | fn Unused_put_RegisterAsDropTarget() -> HRESULT, 258 | fn Unused_get_TheaterMode() -> HRESULT, 259 | fn Unused_put_TheaterMode() -> HRESULT, 260 | fn Unused_get_AddressBar() -> HRESULT, 261 | fn Unused_put_AddressBar() -> HRESULT, 262 | fn Unused_get_Resizable() -> HRESULT, 263 | fn Unused_put_Resizable() -> HRESULT, 264 | } 265 | } 266 | 267 | DEFINE_GUID!{ 268 | CLSID_WebBrowser, 269 | 0x8856f961, 0x340a, 0x11d0, 0xa9, 0x6b, 0x00, 0xc0, 0x4f, 0xd7, 0x05, 0xa2} 270 | 271 | RIDL!{ 272 | #[uuid(0x7fd52380, 0x4e07, 0x101b, 0xae, 0x2d, 0x08, 0x00, 0x2b, 0x2e, 0xc7, 0x13)] 273 | interface IPersistStreamInit(IPersistStreamInitVtbl) : IPersist(IPersistVtbl) { 274 | fn IsDirty() -> HRESULT, 275 | fn Load(pStm: *mut IStream,) -> HRESULT, 276 | fn Save(pStm: *mut IStream, fClearDirty: BOOL,) -> HRESULT, 277 | fn GetSizeMax(pcbSize: *mut ULARGE_INTEGER,) -> HRESULT, 278 | fn InitNew() -> HRESULT, 279 | } 280 | } 281 | 282 | STRUCT!{ 283 | struct DOCHOSTUIINFO { 284 | cbSize: c_ulong, 285 | dwFlags: DWORD, 286 | dwDoubleClick: DWORD, 287 | pchHostCss: *mut OLECHAR, 288 | pchHostNS: *mut OLECHAR, 289 | } 290 | } 291 | 292 | RIDL!{ 293 | #[uuid(0xbd3f23c0, 0xd43e, 0x11cf, 0x89, 0x3b, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x1a)] 294 | interface IDocHostUIHandler(IDocHostUIHandlerVtbl) : IUnknown(IUnknownVtbl) { 295 | fn ShowContextMenu( 296 | dwID: DWORD, 297 | ppt: *mut POINT, 298 | pcmdtReserved: *mut IUnknown, 299 | pdispReserved: *mut IDispatch,) -> HRESULT, 300 | fn GetHostInfo(pInfo: *mut DOCHOSTUIINFO,) -> HRESULT, 301 | fn ShowUI( 302 | dwID: DWORD, 303 | pActiveObject: *mut IOleInPlaceActiveObject, 304 | pCommandTarget: *mut IOleCommandTarget, 305 | pFrame: *mut IOleInPlaceFrame, 306 | pDoc: *mut IOleInPlaceUIWindow,) -> HRESULT, 307 | fn HideUI() -> HRESULT, 308 | fn UpdateUI() -> HRESULT, 309 | fn EnableModeless(fEnable: BOOL,) -> HRESULT, 310 | fn OnDocWindowActivate(fActivate: BOOL,) -> HRESULT, 311 | fn OnFrameWindowActivate(fActivate: BOOL,) -> HRESULT, 312 | fn ResizeBorder( 313 | prcBorder: LPCRECT, 314 | pUIWindow: *mut IOleInPlaceUIWindow, 315 | fRameWindow: BOOL,) -> HRESULT, 316 | fn TranslateAccelerator( 317 | lpMsg: LPMSG, 318 | pguidCmdGroup: *const GUID, 319 | nCmdID: DWORD,) -> HRESULT, 320 | fn GetOptionKeyPath( 321 | pchKey: *mut LPOLESTR, 322 | dw: DWORD,) -> HRESULT, 323 | fn GetDropTarget( 324 | pDropTarget: *mut IDropTarget, 325 | ppDropTarget: *mut *mut IDropTarget,) -> HRESULT, 326 | fn GetExternal( 327 | ppDispatch: *mut *mut IDispatch,) -> HRESULT, 328 | fn TranslateUrl( 329 | dwTranslate: DWORD, 330 | pchURLIn: LPWSTR, 331 | ppchURLOut: *mut LPWSTR,) -> HRESULT, 332 | fn FilterDataObject( 333 | pDO: *mut IDataObject, 334 | ppDORet: *mut *mut IDataObject,) -> HRESULT, 335 | } 336 | } 337 | 338 | RIDL!{ 339 | #[uuid(0x626fc520, 0xa41e, 0x11cf, 0xa7, 0x31, 0x00, 0xa0, 0xc9, 0x08, 0x26, 0x37)] 340 | interface IHTMLDocument(IHTMLDocumentVtbl) : IDispatch(IDispatchVtbl) { 341 | fn Unused_get_Script() -> HRESULT, 342 | } 343 | } 344 | 345 | RIDL!{ 346 | #[uuid(0x332c4425, 0x26cb, 0x11d0, 0xb4, 0x83, 0x00, 0xc0, 0x4f, 0xd9, 0x01, 0x19)] 347 | interface IHTMLDocument2(IHTMLDocument2Vtbl) : IHTMLDocument(IHTMLDocumentVtbl) { 348 | fn Unused_get_all() -> HRESULT, 349 | fn Unused_get_body() -> HRESULT, 350 | fn Unused_get_activeElement() -> HRESULT, 351 | fn Unused_get_images() -> HRESULT, 352 | fn Unused_get_applets() -> HRESULT, 353 | fn Unused_get_links() -> HRESULT, 354 | fn Unused_get_forms() -> HRESULT, 355 | fn Unused_get_anchors() -> HRESULT, 356 | fn Unused_put_title() -> HRESULT, 357 | fn Unused_get_title() -> HRESULT, 358 | fn Unused_get_scripts() -> HRESULT, 359 | fn Unused_put_designMode() -> HRESULT, 360 | fn Unused_get_designMode() -> HRESULT, 361 | fn Unused_get_selection() -> HRESULT, 362 | fn Unused_get_readyState() -> HRESULT, 363 | fn Unused_get_frames() -> HRESULT, 364 | fn Unused_get_embeds() -> HRESULT, 365 | fn Unused_get_plugins() -> HRESULT, 366 | fn Unused_put_alinkColor() -> HRESULT, 367 | fn Unused_get_alinkColor() -> HRESULT, 368 | fn Unused_put_bgColor() -> HRESULT, 369 | fn Unused_get_bgColor() -> HRESULT, 370 | fn Unused_put_fgColor() -> HRESULT, 371 | fn Unused_get_fgColor() -> HRESULT, 372 | fn Unused_put_linkColor() -> HRESULT, 373 | fn Unused_get_linkColor() -> HRESULT, 374 | fn Unused_put_vlinkColor() -> HRESULT, 375 | fn Unused_get_vlinkColor() -> HRESULT, 376 | fn Unused_get_referrer() -> HRESULT, 377 | fn Unused_get_location() -> HRESULT, 378 | fn Unused_get_lastModified() -> HRESULT, 379 | fn Unused_put_URL() -> HRESULT, 380 | fn Unused_get_URL() -> HRESULT, 381 | fn Unused_put_domain() -> HRESULT, 382 | fn Unused_get_domain() -> HRESULT, 383 | fn Unused_put_cookie() -> HRESULT, 384 | fn Unused_get_cookie() -> HRESULT, 385 | fn Unused_put_expando() -> HRESULT, 386 | fn Unused_get_expando() -> HRESULT, 387 | fn Unused_put_charset() -> HRESULT, 388 | fn Unused_get_charset() -> HRESULT, 389 | fn Unused_put_defaultCharset() -> HRESULT, 390 | fn Unused_get_defaultCharset() -> HRESULT, 391 | fn Unused_get_mimeType() -> HRESULT, 392 | fn Unused_get_fileSize() -> HRESULT, 393 | fn Unused_get_fileCreatedDate() -> HRESULT, 394 | fn Unused_get_fileModifiedDate() -> HRESULT, 395 | fn Unused_get_fileUpdatedDate() -> HRESULT, 396 | fn Unused_get_security() -> HRESULT, 397 | fn Unused_get_protocol() -> HRESULT, 398 | fn Unused_get_nameProp() -> HRESULT, 399 | fn Unused_write() -> HRESULT, 400 | fn Unused_writeln() -> HRESULT, 401 | fn Unused_open() -> HRESULT, 402 | fn Unused_close() -> HRESULT, 403 | fn Unused_clear() -> HRESULT, 404 | fn Unused_queryCommandSupported() -> HRESULT, 405 | fn Unused_queryCommandEnabled() -> HRESULT, 406 | fn Unused_queryCommandState() -> HRESULT, 407 | fn Unused_queryCommandIndeterm() -> HRESULT, 408 | fn Unused_queryCommandText() -> HRESULT, 409 | fn Unused_queryCommandValue() -> HRESULT, 410 | fn Unused_execCommand() -> HRESULT, 411 | fn Unused_execCommandShowHelp() -> HRESULT, 412 | fn Unused_createElement() -> HRESULT, 413 | fn Unused_put_onhelp() -> HRESULT, 414 | fn Unused_get_onhelp() -> HRESULT, 415 | fn Unused_put_onclick() -> HRESULT, 416 | fn Unused_get_onclick() -> HRESULT, 417 | fn Unused_put_ondblclick() -> HRESULT, 418 | fn Unused_get_ondblclick() -> HRESULT, 419 | fn Unused_put_onkeyup() -> HRESULT, 420 | fn Unused_get_onkeyup() -> HRESULT, 421 | fn Unused_put_onkeydown() -> HRESULT, 422 | fn Unused_get_onkeydown() -> HRESULT, 423 | fn Unused_put_onkeypress() -> HRESULT, 424 | fn Unused_get_onkeypress() -> HRESULT, 425 | fn Unused_put_onmouseup() -> HRESULT, 426 | fn Unused_get_onmouseup() -> HRESULT, 427 | fn Unused_put_onmousedown() -> HRESULT, 428 | fn Unused_get_onmousedown() -> HRESULT, 429 | fn Unused_put_onmousemove() -> HRESULT, 430 | fn Unused_get_onmousemove() -> HRESULT, 431 | fn Unused_put_onmouseout() -> HRESULT, 432 | fn Unused_get_onmouseout() -> HRESULT, 433 | fn Unused_put_onmouseover() -> HRESULT, 434 | fn Unused_get_onmouseover() -> HRESULT, 435 | fn Unused_put_onreadystatechange() -> HRESULT, 436 | fn Unused_get_onreadystatechange() -> HRESULT, 437 | fn Unused_put_onafterupdate() -> HRESULT, 438 | fn Unused_get_onafterupdate() -> HRESULT, 439 | fn Unused_put_onrowexit() -> HRESULT, 440 | fn Unused_get_onrowexit() -> HRESULT, 441 | fn Unused_put_onrowenter() -> HRESULT, 442 | fn Unused_get_onrowenter() -> HRESULT, 443 | fn Unused_put_ondragstart() -> HRESULT, 444 | fn Unused_get_ondragstart() -> HRESULT, 445 | fn Unused_put_onselectstart() -> HRESULT, 446 | fn Unused_get_onselectstart() -> HRESULT, 447 | fn Unused_elementFromPoint() -> HRESULT, 448 | fn get_parentWindow(p: *mut *mut IHTMLWindow2,) -> HRESULT, 449 | fn Unused_get_styleSheets() -> HRESULT, 450 | fn Unused_put_onbeforeupdate() -> HRESULT, 451 | fn Unused_get_onbeforeupdate() -> HRESULT, 452 | fn Unused_put_onerrorupdate() -> HRESULT, 453 | fn Unused_get_onerrorupdate() -> HRESULT, 454 | fn Unused_toString() -> HRESULT, 455 | fn Unused_createStyleSheet() -> HRESULT, 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/win32/client_site.rs: -------------------------------------------------------------------------------- 1 | use std::boxed::Box; 2 | use std::ptr::null_mut; 3 | use std::rc::Rc; 4 | 5 | // Non-asterisk imports are required to eliminate ambiguity 6 | use winapi::Interface; 7 | use winapi::ctypes::*; 8 | use winapi::shared::guiddef::*; 9 | use winapi::shared::minwindef::*; 10 | use winapi::shared::minwindef::ULONG; 11 | use winapi::shared::ntdef::LPWSTR; 12 | use winapi::shared::windef::*; 13 | use winapi::shared::windef::SIZE; 14 | use winapi::shared::winerror::*; 15 | use winapi::shared::wtypesbase::*; 16 | use winapi::shared::wtypes::VT_BSTR; 17 | use winapi::um::oaidl::*; 18 | use winapi::um::oaidl::DISPID; 19 | use winapi::um::objidl::IMoniker; 20 | use winapi::um::oleauto::*; 21 | use winapi::um::unknwnbase::*; 22 | use winapi::um::winbase::*; 23 | use winapi::um::winnt::LCID; 24 | use winapi::um::winuser::*; 25 | 26 | use lib::JavascriptCallback; 27 | use win32::com_pointer::ComPointer; 28 | use win32::ffi::*; 29 | 30 | #[repr(C, packed)] 31 | struct ClientSite { 32 | ole_client_site: IOleClientSite, 33 | ole_in_place_site: IOleInPlaceSite, 34 | doc_host_ui_handler: IDocHostUIHandler, 35 | dispatch: IDispatch, 36 | ole_in_place_frame: ComPointer, 37 | ole_in_place_object: ComPointer, 38 | reference_counter: ULONG, 39 | window: HWND, 40 | callback: Rc, 41 | } 42 | 43 | impl ClientSite { 44 | fn from_ole_in_place_site( 45 | instance: *mut IOleInPlaceSite) -> *mut ClientSite 46 | { 47 | ClientSite::from_member_and_offset( 48 | instance as *mut u8, offset_of!(ClientSite, ole_in_place_site)) 49 | } 50 | 51 | fn from_doc_host_ui_handler( 52 | instance: *mut IDocHostUIHandler) -> *mut ClientSite 53 | { 54 | ClientSite::from_member_and_offset( 55 | instance as *mut u8, offset_of!(ClientSite, doc_host_ui_handler)) 56 | } 57 | 58 | fn from_dispatch( 59 | instance: *mut IDispatch) -> *mut ClientSite 60 | { 61 | ClientSite::from_member_and_offset( 62 | instance as *mut u8, offset_of!(ClientSite, dispatch)) 63 | } 64 | 65 | fn from_member_and_offset( 66 | member: *mut u8, offset: usize) -> *mut ClientSite 67 | { 68 | unsafe { 69 | member.offset(-(offset as isize)) as *mut ClientSite 70 | } 71 | } 72 | } 73 | 74 | const OLE_CLIENT_SITE_VTABLE: IOleClientSiteVtbl = IOleClientSiteVtbl { 75 | parent: IUnknownVtbl { 76 | AddRef: IOleClientSite_AddRef, 77 | Release: IOleClientSite_Release, 78 | QueryInterface: IOleClientSite_QueryInterface, 79 | }, 80 | SaveObject: IOleClientSite_SaveObject, 81 | GetMoniker: IOleClientSite_GetMoniker, 82 | GetContainer: IOleClientSite_GetContainer, 83 | ShowObject: IOleClientSite_ShowObject, 84 | OnShowWindow: IOleClientSite_OnShowWindow, 85 | RequestNewObjectLayout: IOleClientSite_RequestNewObjectLayout, 86 | }; 87 | 88 | const OLE_IN_PLACE_SITE_VTABLE: IOleInPlaceSiteVtbl = IOleInPlaceSiteVtbl { 89 | parent: IOleWindowVtbl { 90 | parent: IUnknownVtbl { 91 | AddRef: IOleInPlaceSite_AddRef, 92 | Release: IOleInPlaceSite_Release, 93 | QueryInterface: IOleInPlaceSite_QueryInterface, 94 | }, 95 | GetWindow: IOleInPlaceSite_GetWindow, 96 | ContextSensitiveHelp: IOleInPlaceSite_ContextSensitiveHelp, 97 | }, 98 | CanInPlaceActivate: IOleInPlaceSite_CanInPlaceActivate, 99 | OnInPlaceActivate: IOleInPlaceSite_OnInPlaceActivate, 100 | OnUIActivate: IOleInPlaceSite_OnUIActivate, 101 | GetWindowContext: IOleInPlaceSite_GetWindowContext, 102 | Scroll: IOleInPlaceSite_Scroll, 103 | OnUIDeactivate: IOleInPlaceSite_OnUIDeactivate, 104 | OnInPlaceDeactivate: IOleInPlaceSite_OnInPlaceDeactivate, 105 | DiscardUndoState: IOleInPlaceSite_DiscardUndoState, 106 | DeactivateAndUndo: IOleInPlaceSite_DeactivateAndUndo, 107 | OnPosRectChange: IOleInPlaceSite_OnPosRectChange, 108 | }; 109 | 110 | const DOC_HOST_UI_HANDLER_VTABLE: IDocHostUIHandlerVtbl = 111 | IDocHostUIHandlerVtbl { 112 | parent: IUnknownVtbl { 113 | AddRef: IDocHostUIHandler_AddRef, 114 | Release: IDocHostUIHandler_Release, 115 | QueryInterface: IDocHostUIHandler_QueryInterface, 116 | }, 117 | ShowContextMenu: IDocHostUIHandler_ShowContextMenu, 118 | GetHostInfo: IDocHostUIHandler_GetHostInfo, 119 | ShowUI: IDocHostUIHandler_ShowUI, 120 | HideUI: IDocHostUIHandler_HideUI, 121 | UpdateUI: IDocHostUIHandler_UpdateUI, 122 | EnableModeless: IDocHostUIHandler_EnableModeless, 123 | OnDocWindowActivate: IDocHostUIHandler_OnDocWindowActivate, 124 | OnFrameWindowActivate: IDocHostUIHandler_OnFrameWindowActivate, 125 | ResizeBorder: IDocHostUIHandler_ResizeBorder, 126 | TranslateAccelerator: IDocHostUIHandler_TranslateAccelerator, 127 | GetOptionKeyPath: IDocHostUIHandler_GetOptionKeyPath, 128 | GetDropTarget: IDocHostUIHandler_GetDropTarget, 129 | GetExternal: IDocHostUIHandler_GetExternal, 130 | TranslateUrl: IDocHostUIHandler_TranslateUrl, 131 | FilterDataObject: IDocHostUIHandler_FilterDataObject, 132 | }; 133 | 134 | const DISPATCH_VTABLE: IDispatchVtbl = IDispatchVtbl { 135 | parent: IUnknownVtbl { 136 | AddRef: IDispatch_AddRef, 137 | Release: IDispatch_Release, 138 | QueryInterface: IDispatch_QueryInterface, 139 | }, 140 | GetTypeInfoCount: IDispatch_GetTypeInfoCount, 141 | GetTypeInfo: IDispatch_GetTypeInfo, 142 | GetIDsOfNames: IDispatch_GetIDsOfNames, 143 | Invoke: IDispatch_Invoke, 144 | }; 145 | 146 | pub fn new_client_site( 147 | window: HWND, 148 | ole_in_place_object: ComPointer, 149 | callback: Rc) -> ComPointer 150 | { 151 | let client_site = Box::new( 152 | ClientSite { 153 | ole_client_site: IOleClientSite { 154 | lpVtbl: &OLE_CLIENT_SITE_VTABLE 155 | }, 156 | ole_in_place_site: IOleInPlaceSite { 157 | lpVtbl: &OLE_IN_PLACE_SITE_VTABLE 158 | }, 159 | doc_host_ui_handler: IDocHostUIHandler { 160 | lpVtbl: &DOC_HOST_UI_HANDLER_VTABLE 161 | }, 162 | dispatch: IDispatch { 163 | lpVtbl: &DISPATCH_VTABLE, 164 | }, 165 | ole_in_place_frame: new_in_place_frame(window), 166 | ole_in_place_object: ole_in_place_object, 167 | reference_counter: 1, 168 | window: window, 169 | callback: callback, 170 | }); 171 | 172 | ComPointer::from_raw(Box::into_raw(client_site) as *mut IOleClientSite) 173 | } 174 | 175 | unsafe extern "system" fn IOleClientSite_AddRef( 176 | instance: *mut IUnknown) -> ULONG 177 | { 178 | let client_site = instance as *mut ClientSite; 179 | 180 | (*client_site).reference_counter += 1; 181 | (*client_site).reference_counter 182 | } 183 | 184 | unsafe extern "system" fn IOleClientSite_Release( 185 | instance: *mut IUnknown) -> ULONG 186 | { 187 | let client_site = instance as *mut ClientSite; 188 | 189 | let result = { 190 | (*client_site).reference_counter -= 1; 191 | (*client_site).reference_counter 192 | }; 193 | 194 | assert!(result != ULONG::max_value()); 195 | 196 | if result == 0 { 197 | Box::from_raw(client_site); 198 | } 199 | 200 | result 201 | } 202 | 203 | unsafe extern "system" fn IOleClientSite_QueryInterface( 204 | instance: *mut IUnknown, 205 | riid: REFIID, 206 | ppvObject: *mut *mut c_void) -> HRESULT 207 | { 208 | let client_site = instance as *mut ClientSite; 209 | 210 | *ppvObject = if IsEqualGUID(&*riid, &IUnknown::uuidof()) { 211 | client_site as *mut c_void 212 | } else if IsEqualGUID(&*riid, &IOleClientSite::uuidof()) { 213 | client_site as *mut c_void 214 | } else if IsEqualGUID(&*riid, &IOleInPlaceSite::uuidof()) { 215 | &mut (*client_site).ole_in_place_site 216 | as *mut IOleInPlaceSite 217 | as *mut c_void 218 | } else if IsEqualGUID(&*riid, &IOleWindow::uuidof()) { 219 | &mut (*client_site).ole_in_place_site 220 | as *mut IOleInPlaceSite 221 | as *mut c_void 222 | } else if IsEqualGUID(&*riid, &IDocHostUIHandler::uuidof()) { 223 | &mut (*client_site).doc_host_ui_handler 224 | as *mut IDocHostUIHandler 225 | as *mut c_void 226 | } else if IsEqualGUID(&*riid, &IDispatch::uuidof()) { 227 | &mut (*client_site).dispatch 228 | as *mut IDispatch 229 | as *mut c_void 230 | } else { 231 | null_mut() 232 | }; 233 | 234 | if *ppvObject != null_mut() { 235 | (*instance).AddRef(); 236 | S_OK 237 | } else { 238 | E_NOINTERFACE 239 | } 240 | } 241 | 242 | unsafe extern "system" fn IOleClientSite_SaveObject( 243 | _instance: *mut IOleClientSite) -> HRESULT 244 | { 245 | S_OK 246 | } 247 | 248 | unsafe extern "system" fn IOleClientSite_GetMoniker( 249 | _instance: *mut IOleClientSite, 250 | _dwAssign: DWORD, 251 | _dwWhichMoniker: DWORD, 252 | ppmk: *mut *mut IMoniker,) -> HRESULT 253 | { 254 | *ppmk = null_mut(); 255 | E_NOTIMPL 256 | } 257 | 258 | unsafe extern "system" fn IOleClientSite_GetContainer( 259 | _instance: *mut IOleClientSite, 260 | ppContainer: *mut *mut IOleContainer) -> HRESULT 261 | { 262 | *ppContainer = null_mut(); 263 | E_NOINTERFACE 264 | } 265 | 266 | unsafe extern "system" fn IOleClientSite_ShowObject( 267 | _instance: *mut IOleClientSite) -> HRESULT 268 | { 269 | S_OK 270 | } 271 | 272 | unsafe extern "system" fn IOleClientSite_OnShowWindow( 273 | _instance: *mut IOleClientSite, _fShow: BOOL) -> HRESULT 274 | { 275 | S_OK 276 | } 277 | 278 | unsafe extern "system" fn IOleClientSite_RequestNewObjectLayout( 279 | _instance: *mut IOleClientSite) -> HRESULT 280 | { 281 | E_NOTIMPL 282 | } 283 | 284 | unsafe extern "system" fn IOleInPlaceSite_AddRef( 285 | instance: *mut IUnknown) -> ULONG 286 | { 287 | let client_site = ClientSite::from_ole_in_place_site( 288 | instance as *mut IOleInPlaceSite); 289 | 290 | (*client_site).ole_client_site.AddRef() 291 | } 292 | 293 | unsafe extern "system" fn IOleInPlaceSite_Release( 294 | instance: *mut IUnknown) -> ULONG 295 | { 296 | let client_site = ClientSite::from_ole_in_place_site( 297 | instance as *mut IOleInPlaceSite); 298 | 299 | (*client_site).ole_client_site.Release() 300 | } 301 | 302 | unsafe extern "system" fn IOleInPlaceSite_QueryInterface( 303 | instance: *mut IUnknown, 304 | riid: REFIID, 305 | ppvObject: *mut *mut c_void) -> HRESULT 306 | { 307 | let client_site = ClientSite::from_ole_in_place_site( 308 | instance as *mut IOleInPlaceSite); 309 | 310 | (*client_site).ole_client_site.QueryInterface(riid, ppvObject) 311 | } 312 | 313 | unsafe extern "system" fn IOleInPlaceSite_GetWindow( 314 | instance: *mut IOleWindow, 315 | phwnd: *mut HWND) -> HRESULT 316 | { 317 | let client_site = ClientSite::from_ole_in_place_site( 318 | instance as *mut IOleInPlaceSite); 319 | 320 | *phwnd = (*client_site).window; 321 | S_OK 322 | } 323 | 324 | unsafe extern "system" fn IOleInPlaceSite_ContextSensitiveHelp( 325 | _instance: *mut IOleWindow, 326 | _fEnterMode: BOOL) -> HRESULT 327 | { 328 | S_OK 329 | } 330 | 331 | unsafe extern "system" fn IOleInPlaceSite_CanInPlaceActivate( 332 | _instance: *mut IOleInPlaceSite) -> HRESULT 333 | { 334 | S_OK 335 | } 336 | 337 | unsafe extern "system" fn IOleInPlaceSite_OnInPlaceActivate( 338 | _instance: *mut IOleInPlaceSite) -> HRESULT 339 | { 340 | S_OK 341 | } 342 | 343 | unsafe extern "system" fn IOleInPlaceSite_OnUIActivate( 344 | _instance: *mut IOleInPlaceSite) -> HRESULT 345 | { 346 | S_OK 347 | } 348 | 349 | unsafe extern "system" fn IOleInPlaceSite_GetWindowContext( 350 | instance: *mut IOleInPlaceSite, 351 | ppFrame: *mut *mut IOleInPlaceFrame, 352 | ppDoc: *mut *mut IOleInPlaceUIWindow, 353 | _lprcPosRect: LPRECT, 354 | _lprcClipRect: LPRECT, 355 | lpFrameInfo: *mut OLEINPLACEFRAMEINFO) -> HRESULT 356 | { 357 | let client_site = ClientSite::from_ole_in_place_site(instance); 358 | 359 | *ppFrame = (*client_site).ole_in_place_frame.as_ptr(); 360 | (**ppFrame).AddRef(); 361 | 362 | *ppDoc = null_mut(); 363 | 364 | // TODO: set rectangles 365 | 366 | (*lpFrameInfo).fMDIApp = FALSE; 367 | (*lpFrameInfo).hwndFrame = (*client_site).window; 368 | (*lpFrameInfo).haccel = null_mut(); 369 | (*lpFrameInfo).cAccelEntries = 0; 370 | 371 | S_OK 372 | } 373 | 374 | unsafe extern "system" fn IOleInPlaceSite_Scroll( 375 | _instance: *mut IOleInPlaceSite, 376 | _scrollExtant: SIZE) -> HRESULT 377 | { 378 | S_OK 379 | } 380 | 381 | unsafe extern "system" fn IOleInPlaceSite_OnUIDeactivate( 382 | _instance: *mut IOleInPlaceSite, 383 | _fUndoable: BOOL) -> HRESULT 384 | { 385 | S_OK 386 | } 387 | 388 | unsafe extern "system" fn IOleInPlaceSite_OnInPlaceDeactivate( 389 | _instance: *mut IOleInPlaceSite) -> HRESULT 390 | { 391 | S_OK 392 | } 393 | 394 | unsafe extern "system" fn IOleInPlaceSite_DiscardUndoState( 395 | _instance: *mut IOleInPlaceSite) -> HRESULT 396 | { 397 | S_OK 398 | } 399 | 400 | unsafe extern "system" fn IOleInPlaceSite_DeactivateAndUndo( 401 | _instance: *mut IOleInPlaceSite) -> HRESULT 402 | { 403 | S_OK 404 | } 405 | 406 | unsafe extern "system" fn IOleInPlaceSite_OnPosRectChange( 407 | instance: *mut IOleInPlaceSite, 408 | lprcPosRect: LPCRECT) -> HRESULT 409 | { 410 | let client_site = ClientSite::from_ole_in_place_site(instance); 411 | 412 | (*client_site) 413 | .ole_in_place_object 414 | .get() 415 | .map(|ole_in_place_object| { 416 | ole_in_place_object.SetObjectRects( 417 | lprcPosRect, 418 | lprcPosRect); 419 | }); 420 | 421 | S_OK 422 | } 423 | 424 | unsafe extern "system" fn IDocHostUIHandler_AddRef( 425 | instance: *mut IUnknown) -> ULONG 426 | { 427 | let client_site = ClientSite::from_doc_host_ui_handler( 428 | instance as *mut IDocHostUIHandler); 429 | 430 | (*client_site).ole_client_site.AddRef() 431 | } 432 | 433 | unsafe extern "system" fn IDocHostUIHandler_Release( 434 | instance: *mut IUnknown) -> ULONG 435 | { 436 | let client_site = ClientSite::from_doc_host_ui_handler( 437 | instance as *mut IDocHostUIHandler); 438 | 439 | (*client_site).ole_client_site.Release() 440 | } 441 | 442 | unsafe extern "system" fn IDocHostUIHandler_QueryInterface( 443 | instance: *mut IUnknown, 444 | riid: REFIID, 445 | ppvObject: *mut *mut c_void) -> HRESULT 446 | { 447 | let client_site = ClientSite::from_doc_host_ui_handler( 448 | instance as *mut IDocHostUIHandler); 449 | 450 | (*client_site).ole_client_site.QueryInterface(riid, ppvObject) 451 | } 452 | 453 | unsafe extern "system" fn IDocHostUIHandler_ShowContextMenu( 454 | _instance: *mut IDocHostUIHandler, 455 | dwID: DWORD, 456 | _ppt: *mut POINT, 457 | _pcmdtReserved: *mut IUnknown, 458 | _pdispReserved: *mut IDispatch) -> HRESULT 459 | { 460 | const CONTEXT_MENU_CONTROL: DWORD = 0x2; 461 | const CONTEXT_MENU_TEXTSELECT: DWORD = 0x4; 462 | const CONTEXT_MENU_VSCROLL: DWORD = 0x9; 463 | const CONTEXT_MENU_HSCROLL: DWORD = 0x10; 464 | 465 | match dwID { 466 | CONTEXT_MENU_CONTROL | 467 | CONTEXT_MENU_TEXTSELECT | 468 | CONTEXT_MENU_VSCROLL | 469 | CONTEXT_MENU_HSCROLL => S_FALSE, 470 | _ => S_OK 471 | } 472 | } 473 | 474 | unsafe extern "system" fn IDocHostUIHandler_GetHostInfo( 475 | _instance: *mut IDocHostUIHandler, 476 | pInfo: *mut DOCHOSTUIINFO) -> HRESULT 477 | { 478 | const DOCHOSTUIFLAG_NO3DBORDER: DWORD = 0x00000004; 479 | const DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION: DWORD = 0x00010000; 480 | const DOCHOSTUIFLAG_THEME: DWORD = 0x00040000; 481 | const DOCHOSTUIFLAG_DPI_AWARE: DWORD = 0x40000000; 482 | 483 | (*pInfo).dwFlags = 484 | DOCHOSTUIFLAG_NO3DBORDER | 485 | DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | 486 | DOCHOSTUIFLAG_THEME | 487 | DOCHOSTUIFLAG_DPI_AWARE; 488 | (*pInfo).dwDoubleClick = 0; 489 | 490 | S_OK 491 | } 492 | 493 | unsafe extern "system" fn IDocHostUIHandler_ShowUI( 494 | _instance: *mut IDocHostUIHandler, 495 | _dwID: DWORD, 496 | _pActiveObject: *mut IOleInPlaceActiveObject, 497 | _pCommandTarget: *mut IOleCommandTarget, 498 | _pFrame: *mut IOleInPlaceFrame, 499 | _pDoc: *mut IOleInPlaceUIWindow) -> HRESULT 500 | { 501 | S_OK 502 | } 503 | 504 | unsafe extern "system" fn IDocHostUIHandler_HideUI( 505 | _instance: *mut IDocHostUIHandler) -> HRESULT 506 | { 507 | S_OK 508 | } 509 | 510 | unsafe extern "system" fn IDocHostUIHandler_UpdateUI( 511 | _instance: *mut IDocHostUIHandler) -> HRESULT 512 | { 513 | S_OK 514 | } 515 | 516 | unsafe extern "system" fn IDocHostUIHandler_EnableModeless( 517 | _instance: *mut IDocHostUIHandler, 518 | _fEnable: BOOL) -> HRESULT 519 | { 520 | S_OK 521 | } 522 | 523 | unsafe extern "system" fn IDocHostUIHandler_OnDocWindowActivate( 524 | _instance: *mut IDocHostUIHandler, 525 | _fActivate: BOOL) -> HRESULT 526 | { 527 | S_OK 528 | } 529 | 530 | unsafe extern "system" fn IDocHostUIHandler_OnFrameWindowActivate( 531 | _instance: *mut IDocHostUIHandler, 532 | _fActivate: BOOL) -> HRESULT 533 | { 534 | S_OK 535 | } 536 | 537 | unsafe extern "system" fn IDocHostUIHandler_ResizeBorder( 538 | _instance: *mut IDocHostUIHandler, 539 | _prcBorder: LPCRECT, 540 | _pUIWindow: *mut IOleInPlaceUIWindow, 541 | _fRameWindow: BOOL) -> HRESULT 542 | { 543 | S_OK 544 | } 545 | 546 | unsafe extern "system" fn IDocHostUIHandler_TranslateAccelerator( 547 | _instance: *mut IDocHostUIHandler, 548 | _lpMsg: LPMSG, 549 | _pguidCmdGroup: *const GUID, 550 | _nCmdID: DWORD) -> HRESULT 551 | { 552 | S_FALSE 553 | } 554 | 555 | unsafe extern "system" fn IDocHostUIHandler_GetOptionKeyPath( 556 | _instance: *mut IDocHostUIHandler, 557 | pchKey: *mut LPOLESTR, 558 | _dw: DWORD) -> HRESULT 559 | { 560 | *pchKey = null_mut(); 561 | S_FALSE 562 | } 563 | 564 | unsafe extern "system" fn IDocHostUIHandler_GetDropTarget( 565 | _instance: *mut IDocHostUIHandler, 566 | _pDropTarget: *mut IDropTarget, 567 | _ppDropTarget: *mut *mut IDropTarget) -> HRESULT 568 | { 569 | E_NOTIMPL 570 | } 571 | 572 | unsafe extern "system" fn IDocHostUIHandler_GetExternal( 573 | instance: *mut IDocHostUIHandler, 574 | ppDispatch: *mut *mut IDispatch) -> HRESULT 575 | { 576 | let client_site = ClientSite::from_doc_host_ui_handler(instance); 577 | 578 | (*client_site).ole_client_site.QueryInterface( 579 | &IDispatch::uuidof(), ppDispatch as *mut *mut c_void); 580 | S_OK 581 | } 582 | 583 | unsafe extern "system" fn IDocHostUIHandler_TranslateUrl( 584 | _instance: *mut IDocHostUIHandler, 585 | _dwTranslate: DWORD, 586 | _pchURLIn: LPWSTR, 587 | ppchURLOut: *mut LPWSTR) -> HRESULT 588 | { 589 | *ppchURLOut = null_mut(); 590 | S_FALSE 591 | } 592 | 593 | unsafe extern "system" fn IDocHostUIHandler_FilterDataObject( 594 | _instance: *mut IDocHostUIHandler, 595 | _pDO: *mut IDataObject, 596 | ppDORet: *mut *mut IDataObject) -> HRESULT 597 | { 598 | *ppDORet = null_mut(); 599 | S_FALSE 600 | } 601 | 602 | unsafe extern "system" fn IDispatch_AddRef( 603 | instance: *mut IUnknown) -> ULONG 604 | { 605 | let client_site = ClientSite::from_dispatch( 606 | instance as *mut IDispatch); 607 | 608 | (*client_site).ole_client_site.AddRef() 609 | } 610 | 611 | unsafe extern "system" fn IDispatch_Release( 612 | instance: *mut IUnknown) -> ULONG 613 | { 614 | let client_site = ClientSite::from_dispatch( 615 | instance as *mut IDispatch); 616 | 617 | (*client_site).ole_client_site.Release() 618 | } 619 | 620 | unsafe extern "system" fn IDispatch_QueryInterface( 621 | instance: *mut IUnknown, 622 | riid: REFIID, 623 | ppvObject: *mut *mut c_void) -> HRESULT 624 | { 625 | let client_site = ClientSite::from_dispatch( 626 | instance as *mut IDispatch); 627 | 628 | (*client_site).ole_client_site.QueryInterface(riid, ppvObject) 629 | } 630 | 631 | unsafe extern "system" fn IDispatch_GetTypeInfoCount( 632 | _instance: *mut IDispatch, 633 | pctinfo: *mut UINT) -> HRESULT 634 | { 635 | *pctinfo = 0; 636 | S_OK 637 | } 638 | 639 | unsafe extern "system" fn IDispatch_GetTypeInfo( 640 | _instance: *mut IDispatch, 641 | _iTInfo: UINT, 642 | _lcid: LCID, 643 | ppTInfo: *mut *mut ITypeInfo) -> HRESULT 644 | { 645 | *ppTInfo = null_mut(); 646 | S_FALSE 647 | } 648 | 649 | unsafe extern "system" fn IDispatch_GetIDsOfNames( 650 | _instance: *mut IDispatch, 651 | _riid: REFIID, 652 | rgszNames: *mut LPOLESTR, 653 | cNames: UINT, 654 | _lcid: LCID, 655 | rgDispId: *mut DISPID) -> HRESULT 656 | { 657 | for index in 0..cNames { 658 | *rgDispId.offset(index as isize) = DISPID_UNKNOWN; 659 | } 660 | 661 | // The "invoke" string encoded in utf16. 662 | const METHOD_NAME: [u16; 7] = [105, 110, 118, 111, 107, 101, 0]; 663 | 664 | if cNames != 1 || lstrcmpW(*rgszNames, METHOD_NAME.as_ptr()) != 0 { 665 | return DISP_E_UNKNOWNNAME; 666 | } 667 | 668 | *rgDispId = 1; 669 | S_OK 670 | } 671 | 672 | unsafe extern "system" fn IDispatch_Invoke( 673 | instance: *mut IDispatch, 674 | dispIdMember: DISPID, 675 | _riid: REFIID, 676 | _lcid: LCID, 677 | wFlags: WORD, 678 | pDispParams: *mut DISPPARAMS, 679 | pVarResult: *mut VARIANT, 680 | _pExcepInfo: *mut EXCEPINFO, 681 | puArgErr: *mut UINT) -> HRESULT 682 | { 683 | let client_site = ClientSite::from_dispatch(instance); 684 | 685 | if dispIdMember != 1 { 686 | return DISP_E_MEMBERNOTFOUND; 687 | } 688 | 689 | if wFlags != DISPATCH_METHOD && 690 | wFlags != DISPATCH_METHOD | DISPATCH_PROPERTYGET 691 | { 692 | return DISP_E_BADPARAMCOUNT; 693 | } 694 | 695 | if (*pDispParams).cNamedArgs != 0 { 696 | return DISP_E_NONAMEDARGS; 697 | } 698 | 699 | if (*pDispParams).cArgs != 1 { 700 | return DISP_E_BADPARAMCOUNT; 701 | } 702 | 703 | if (*(*pDispParams).rgvarg).n1.n2().vt as u32 != VT_BSTR { 704 | *puArgErr = 0; 705 | return DISP_E_TYPEMISMATCH; 706 | } 707 | 708 | // This is ridiculous 709 | let argument = *(*(*pDispParams).rgvarg).n1.n2().n3.bstrVal(); 710 | let argument_length = SysStringLen(argument); 711 | 712 | use std::ffi::{OsStr, OsString}; 713 | use std::os::windows::ffi::{OsStrExt, OsStringExt}; 714 | use std::slice; 715 | 716 | let argument_utf8 = 717 | OsString::from_wide( 718 | slice::from_raw_parts( 719 | argument, 720 | argument_length as usize)) 721 | .into_string(); 722 | 723 | if argument_utf8.is_err() { 724 | return DISP_E_OVERFLOW; 725 | } 726 | 727 | let result = ((*client_site).callback)(argument_utf8.unwrap()); 728 | 729 | if pVarResult != null_mut() { 730 | VariantInit(pVarResult); 731 | 732 | let result_utf16: Vec = 733 | OsStr::new(&result).encode_wide().collect(); 734 | 735 | (*pVarResult).n1.n2_mut().vt = VT_BSTR as u16; 736 | *(*pVarResult).n1.n2_mut().n3.bstrVal_mut() = SysAllocStringLen( 737 | result_utf16.as_ptr(), 738 | result_utf16.len() as u32); 739 | } 740 | 741 | S_OK 742 | } 743 | 744 | #[repr(C, packed)] 745 | struct InPlaceFrame { 746 | ole_in_place_frame: IOleInPlaceFrame, 747 | reference_counter: ULONG, 748 | window: HWND, 749 | } 750 | 751 | const OLE_IN_PLACE_FRAME_VTABLE: IOleInPlaceFrameVtbl = IOleInPlaceFrameVtbl { 752 | parent: IOleInPlaceUIWindowVtbl { 753 | parent: IOleWindowVtbl { 754 | parent: IUnknownVtbl { 755 | AddRef: IOleInPlaceFrame_AddRef, 756 | Release: IOleInPlaceFrame_Release, 757 | QueryInterface: IOleInPlaceFrame_QueryInterface, 758 | }, 759 | GetWindow: IOleInPlaceFrame_GetWindow, 760 | ContextSensitiveHelp: IOleInPlaceFrame_ContextSensitiveHelp, 761 | }, 762 | GetBorder: IOleInPlaceFrame_GetBorder, 763 | RequestBorderSpace: IOleInPlaceFrame_RequestBorderSpace, 764 | SetBorderSpace: IOleInPlaceFrame_SetBorderSpace, 765 | SetActiveObject: IOleInPlaceFrame_SetActiveObject, 766 | }, 767 | InsertMenus: IOleInPlaceFrame_InsertMenus, 768 | SetMenu: IOleInPlaceFrame_SetMenu, 769 | RemoveMenus: IOleInPlaceFrame_RemoveMenus, 770 | SetStatusText: IOleInPlaceFrame_SetStatusText, 771 | EnableModeless: IOleInPlaceFrame_EnableModeless, 772 | TranslateAccelerator: IOleInPlaceFrame_TranslateAccelerator, 773 | }; 774 | 775 | fn new_in_place_frame(window: HWND) -> ComPointer { 776 | let in_place_frame = Box::new( 777 | InPlaceFrame { 778 | ole_in_place_frame: IOleInPlaceFrame { 779 | lpVtbl: &OLE_IN_PLACE_FRAME_VTABLE 780 | }, 781 | reference_counter: 1, 782 | window: window, 783 | }); 784 | 785 | ComPointer::from_raw( 786 | Box::into_raw(in_place_frame) as *mut IOleInPlaceFrame) 787 | } 788 | 789 | unsafe extern "system" fn IOleInPlaceFrame_AddRef( 790 | instance: *mut IUnknown) -> ULONG 791 | { 792 | let in_place_frame = instance as *mut InPlaceFrame; 793 | 794 | (*in_place_frame).reference_counter += 1; 795 | (*in_place_frame).reference_counter 796 | } 797 | 798 | unsafe extern "system" fn IOleInPlaceFrame_Release( 799 | instance: *mut IUnknown) -> ULONG 800 | { 801 | let in_place_frame = instance as *mut InPlaceFrame; 802 | 803 | let result = { 804 | (*in_place_frame).reference_counter -= 1; 805 | (*in_place_frame).reference_counter 806 | }; 807 | 808 | assert!(result != ULONG::max_value()); 809 | 810 | if result == 0 { 811 | Box::from_raw(in_place_frame); 812 | } 813 | 814 | result 815 | } 816 | 817 | unsafe extern "system" fn IOleInPlaceFrame_QueryInterface( 818 | instance: *mut IUnknown, 819 | riid: REFIID, 820 | ppvObject: *mut *mut c_void) -> HRESULT 821 | { 822 | *ppvObject = 823 | if IsEqualGUID(&*riid, &IUnknown::uuidof()) || 824 | IsEqualGUID(&*riid, &IOleWindow::uuidof()) || 825 | IsEqualGUID(&*riid, &IOleInPlaceUIWindow::uuidof()) || 826 | IsEqualGUID(&*riid, &IOleInPlaceFrame::uuidof()) 827 | { 828 | instance as *mut c_void 829 | } else { 830 | null_mut() 831 | }; 832 | 833 | if *ppvObject != null_mut() { 834 | (*instance).AddRef(); 835 | S_OK 836 | } else { 837 | E_NOINTERFACE 838 | } 839 | } 840 | 841 | unsafe extern "system" fn IOleInPlaceFrame_GetWindow( 842 | instance: *mut IOleWindow, 843 | phwnd: *mut HWND) -> HRESULT 844 | { 845 | let in_place_frame = instance as *mut InPlaceFrame; 846 | 847 | *phwnd = (*in_place_frame).window; 848 | S_OK 849 | } 850 | 851 | unsafe extern "system" fn IOleInPlaceFrame_ContextSensitiveHelp( 852 | _instance: *mut IOleWindow, 853 | _fEnterMode: BOOL) -> HRESULT 854 | { 855 | S_OK 856 | } 857 | 858 | unsafe extern "system" fn IOleInPlaceFrame_GetBorder( 859 | _instance: *mut IOleInPlaceUIWindow, 860 | _lprectBorder: LPRECT) -> HRESULT 861 | { 862 | INPLACE_E_NOTOOLSPACE 863 | } 864 | 865 | unsafe extern "system" fn IOleInPlaceFrame_RequestBorderSpace( 866 | _instance: *mut IOleInPlaceUIWindow, 867 | _pborderwidths: LPCRECT) -> HRESULT 868 | { 869 | INPLACE_E_NOTOOLSPACE 870 | } 871 | 872 | unsafe extern "system" fn IOleInPlaceFrame_SetBorderSpace( 873 | _instance: *mut IOleInPlaceUIWindow, 874 | _pborderwidths: LPCRECT) -> HRESULT 875 | { 876 | S_OK 877 | } 878 | 879 | unsafe extern "system" fn IOleInPlaceFrame_SetActiveObject( 880 | _instance: *mut IOleInPlaceUIWindow, 881 | _pActiveObject: *mut IOleInPlaceActiveObject, 882 | _pszObjName: LPCOLESTR) -> HRESULT 883 | { 884 | S_OK 885 | } 886 | 887 | unsafe extern "system" fn IOleInPlaceFrame_InsertMenus( 888 | _instance: *mut IOleInPlaceFrame, 889 | _hmenuShared: HMENU, 890 | _lpMenuWidths: LPVOID) -> HRESULT 891 | { 892 | S_OK 893 | } 894 | 895 | unsafe extern "system" fn IOleInPlaceFrame_SetMenu( 896 | _instance: *mut IOleInPlaceFrame, 897 | _hmenuShared: HMENU, 898 | _holemenu: HGLOBAL, 899 | _hwndActiveObject: HWND) -> HRESULT 900 | { 901 | S_OK 902 | } 903 | 904 | unsafe extern "system" fn IOleInPlaceFrame_RemoveMenus( 905 | _instance: *mut IOleInPlaceFrame, 906 | _hmenuShared: HMENU) -> HRESULT 907 | { 908 | S_OK 909 | } 910 | 911 | unsafe extern "system" fn IOleInPlaceFrame_SetStatusText( 912 | _instance: *mut IOleInPlaceFrame, 913 | _pszStatusText: LPCOLESTR) -> HRESULT 914 | { 915 | S_OK 916 | } 917 | 918 | unsafe extern "system" fn IOleInPlaceFrame_EnableModeless( 919 | _instance: *mut IOleInPlaceFrame, 920 | _fEnable: BOOL) -> HRESULT 921 | { 922 | S_OK 923 | } 924 | 925 | unsafe extern "system" fn IOleInPlaceFrame_TranslateAccelerator( 926 | _instance: *mut IOleInPlaceFrame, 927 | _lpmsg: LPMSG, 928 | _wID: WORD) -> HRESULT 929 | { 930 | S_OK 931 | } 932 | --------------------------------------------------------------------------------