How to link to direct2d methods with rust ffi? - winapi

So I tried to follow the win32 guide from rust with my own bindings.
I have no problem using the functions like CreateWindowExW and RegisterClassEx. There are no problems with my custom types and function signatures.
I have a problem with DIrect2D's methods.
I can use functions like D2D1CreateFactory, like this:
#[link(name = "d2d1")]
extern "system" {
pub fn D2D1CreateFactory(factoryType: D2D1_FACTORY_TYPE, riid: REFIID, pFactoryOptions: *const D2D1_FACTORY_OPTIONS, ppIFactory: *mut *mut VOID) -> HRESULT;
}
But when I try to use the methods like ID2D1Factory::CreateHwndRenderTarget and ID2D1RenderTarget::CreateSolidColorBrush:
#[link(name = "d2d1")]
extern "system" {
pub fn CreateHwndRenderTarget(renderTargetProperties: *const D2D1_RENDER_TARGET_PROPERTIES, hwndRenderTargetProperties: *const D2D1_HWND_RENDER_TARGET_PROPERTIES, hwndRenderTarget: *mut ID2D1HwndRenderTarget) -> HRESULT;
pub fn CreateSolidColorBrush(color: *const D2D1_COLOR_F, solidColorBrush: *mut ID2D1SolidColorBrush) -> HRESULT;
}
I get the following error:
rust.c1xdqhqliin6vee.rcgu.o : error LNK2019: unresolved external symbol __imp_CreateHwndRenderTarget referenced in function _ZN4rust23CreateGraphicsResources17hfd7581378c7f6240E
rust.c1xdqhqliin6vee.rcgu.o : error LNK2019: unresolved external symbol __imp_CreateSolidColorBrush referenced in function _ZN4rust23CreateGraphicsResources17hfd7581378c7f6240E
C:\Users\x\Desktop\rust\target\debug\deps\rust.exe : fatal error LNK1120: 2 unresolved externals
I know somehow windows-rs and winapi-rs solves it. But windows-rs's code is too complicated for me to undestand it, and winapi-rs's macros are a nightmare.
Is there a simple way to link to these methods, or do I need to set up a system with structs and traits like the two libraries above?
But even if I do set up a system like those, I still do not understand how would that be different from what I am doing. Methods in the end are just functions right? And these Direct2d methods are not even like traditional methods. From what I have seen, the interface(ID2D1Factory) is just used as a namespace to group up these functions. There are no shared variables and state between them.
How can I solve this and use the methods? What are the two libraries doing differently than me?

Related

How to cast LoadLibraryA in the Rust windows crate into LPTHREAD_START_ROUTINE?

I am trying to convert the DLL Injection code in this article from C++ to Rust, using the windows crate.
I had little trouble up until the CreateRemoteThread function.
HANDLE hThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, loc, 0, 0);
In my Rust code, I cannot cast LoadLibraryA function to LP_THREAD_START_ROUTINE like in the C++ code, due to the mismatch in LoadLibraryA function signature to LP_THREAD_START_ROUTINE type.
let remote_thread = CreateRemoteThread(
injected_process,
None,
0,
LoadLibraryA as LPTHREAD_START_ROUTINE,
Some(allocate_mem),
0,
None,
).unwrap_or_else(|error| {
panic!("CreateRemoteThread error \n {:?}", error);
});
non-primitive cast: `unsafe fn(_) -> Result<HINSTANCE, windows::core::Error> {LoadLibraryA::<_>}` as `Option<unsafe extern "system" fn(*mut c_void) -> u32>`
an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
I have tried to create a wrapper function around LoadLibraryA but that crashed the program when compiled and run.
unsafe extern "system" fn load_library_wrapper (allocate_mem: *mut c_void) -> u32 {
LoadLibraryA(allocate_mem);
0
}

undefined reference to 'core:fmt::write' when linking a standalone executable

I'm trying to implement core::fmt::Write for a struct I have written to manage a text buffer in an embedded rust project. I am using #[no_std] and my implementation is listed below
use core::fmt;
impl fmt::Write for Cell {
fn write_str(&mut self, s: &str) -> fmt::Result {
for ch in s.chars() {
self.put_char(ch)
}
Ok(())
}
}
the put_char function works properly when used alone. I attempt to use this elsewhere as follows
use core::fmt::{Write, Result};
...
let mut buff = io::Cell::new();
write!(&mut buff, "abc");
however, when I try to compile this, I get the following error during linking
ld: build/main.o: in function `core::fmt::Write::write_fmt':
/rustc/d32c320d7eee56706486fef6be778495303afe9e/library/core/src/fmt/mod.rs:182: undefined reference to `core::fmt::write'
ld: /rustc/d32c320d7eee56706486fef6be778495303afe9e/library/core/src/fmt/mod.rs:182: undefined reference to `core::fmt::write'
Why am I receiving this. It appears to reference an issue within the builtin libraries. What am I doing wrong that creates this issue
Do I need to explicitly link against the core libraries? I thought this wasn't necessary

Link Error during Rust compilation (Cargo)

I'm calling some WinAPI functions with the Rust FFI (in this case MessageBoxA).
My code was working until I did a little variable change and the compilation gave me an error as:
= note: Non-UTF-8 output: WinAPI-dd8845a539e186b8.4ojwfrbxjnkzuhga.rcgu.o : er
ror LNK2019: symbole externe non r\xe9solu MessageBoxA r\xe9f\xe9renc\xe9 dans l
a fonction _ZN6WinAPI4main17hdf93991da0bc3966E\r\nd:\\core\\Confidential\\Forens
ic\\Rust\\WinAPI\\target\\debug\\deps\\WinAPI-dd8845a539e186b8.exe : fatal error
LNK1120: 1 externes non r\xe9solus\r\n
The last line is in french and it means LNK1120: 1 unresolved external.
I kind of understand that is an encoding error but I have no ideas how to solve it.
So I canceled the little changes I did in my code but it keep displaying that weird message (The error message is actually bigger but not comprehensible).
It's actually a cargo project, the code if you want to check it:
#[cfg(windows)]
#[link(name = "user32", kind = "dylib")]
extern crate libc;
mod ffi{
use libc::{c_uint,uintptr_t,c_void};
type HANDLE = *mut c_void;
pub type UINT = c_uint;
pub type UINT_PTR = uintptr_t;
pub type HWND = HANDLE;
pub type LPCTSTR = *const i8;
pub const MB_OK: u32 = 0x0;
pub const MB_OKCANCEL: u32 = 0x00000001;
pub const MB_ICONWARNING: u32 = 0x00000030;
pub const MB_ICONINFORMATION: u32 = 0x00000040;
pub const MB_ICONQUESTION: u32 = 0x00000020;
}
extern "system"{
fn MessageBoxA(hWnd: ffi::HWND, lpText: ffi::LPCTSTR, lpCaption: ffi::LPCTSTR, uType: u32) -> u32;
}
use ffi::LPCTSTR;
use ffi::MB_OK;
use ffi::MB_ICONINFORMATION;
fn main() -> std::io::Result<()>{
unsafe{
let buffer: &[u8] = &[97,99,107,101,0]; // "acke" as a null terminated str
let lpData: LPCTSTR = core::str::from_utf8_unchecked(buffer).as_ptr() as *const i8;
let lpCaption: LPCTSTR = "Information".as_ptr() as *const i8;
MessageBoxA(
std::ptr::null_mut(),
lpData,
lpCaption,
MB_OK | MB_ICONINFORMATION,
);
};
return Ok(());
}
#[cfg(not(windows))]
fn main() -> std::io::Result<()>{
println!("That program only runs on Windows 10 architectures.");
return Ok(());
}
Important : The error doesn't occure when I put the call to MessageBoxA in comment.
I changed my Visual Studio default language to English because of encoding problems with link.exe.
Then I got an error about unresolved external: MessageBoxA#16. To solve that, I moved the line extern "system"{ /* Functions prototypes */ } directly after #[link(name="user32")] declaration.
You also need Windows 10 SDK installed.
Thanks to rustup.exe for giving really good installation indications!

How to use SetWindowsHookEx in Rust?

I am trying to figure out how to set global Windows hooks inside of Rust. I can find multiple examples for other languages, but there doesn't seem to be anything for Rust.
What I managed to get so far:
extern crate user32;
extern crate winapi;
const WH_KEYBOARD_LL: i32 = 13;
fn main() {
let hook_id = user32::SetWindowsHookExA(
WH_KEYBOARD_LL,
Some(hook_callback),
// No idea what goes here ,
0,
);
}
fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
// ...
}
The compiler complains that it needs a "system" fn for the callback function, but is getting a Rust fn, which makes sense, but I still don't know how to make that work.
From what I gathered from the documentation, the third parameter hMod should point to the same module that has the callback function, and the examples in other languages uses some function that gets the current module handle, but I don't know how to do that in Rust.
The compiler complains that it needs a "system" fn for the callback function, but is getting a Rust fn, which makes sense, but I still don't know how to make that work.
The compiler actually gives you exactly what you need ... if you continue reading the error you'll see:
expected type `unsafe extern "system" fn(i32, u64, i64) -> i64`
found type `fn(i32, u64, i64) -> i64 {hook_callback}`
Adding that, gives:
extern "system" fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
0
}
From what I gathered from the documentation, the third parameter hMod should point to the same module that has the callback function, and the examples in other languages uses some function that gets the current module handle, but I don't know how to do that in Rust.
Again, reading further into the WinAPI documentation shows that NULL should be the value of this parameter if the thread ID (the last argument) specifies that its within the same process. Since you've passed zero - which the documentation states is associated with all threads in the current process ... that's what it should be... NULL. So now we get:
let hook_id =
user32::SetWindowsHookExA(WH_KEYBOARD_LL, Some(hook_callback), std::ptr::null_mut(), 0);
This compiles.
Accounting for the other errors around unsafe that you'll get ... this gives you (full working code):
extern crate user32;
extern crate winapi;
const WH_KEYBOARD_LL: i32 = 13;
fn main() {
unsafe {
let hook_id =
user32::SetWindowsHookExA(WH_KEYBOARD_LL, Some(hook_callback), std::ptr::null_mut(), 0);
// Don't forget to release the hook eventually
user32::UnhookWindowsHookEx(hook_id);
}
}
extern "system" fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
0
}

Why does Rust export its whole standard library when building a DLL?

I'm currently trying to write a dynamic library with Rust which will be loaded from a existing program. I need to export a few functions with specific names and calling conventions. Everything works, but as soon as I use anything from the standard library:
The DLL size balloons to over 3MiB (Not exactly pretty, but I could live with that)
The whole standard library gets exported from the DLL. Here is a lists with all exports: http://pastebin.com/LsG1u96C (5100 functions)
Am I missing some compiler switch?
I compile the following code with rustc without any options:
#![crate_type = "dylib"]
#![feature(std_misc)]
use std::ffi::CString;
#[link(name = "user32")]
#[allow(non_snake_case)]
extern "stdcall" {
fn MessageBoxA(hWnd: u32, lpText: *const i8, lpCaption: *const i8, uType: u32) -> u32;
}
#[no_mangle]
#[allow(non_snake_case)]
pub unsafe extern "stdcall" fn _AddLuaState(lua_state_ptr: u32)
{
let info_str = format!("Lua State Created: {}!", lua_state_ptr);
let info_cstring = CString::new(info_str).unwrap();
let caption = CString::new("Hello from my Rust Library!").unwrap();
MessageBoxA(0, info_cstring.as_ptr(), caption.as_ptr(), 0);
}
_AddLuaState#4 is the only function that should be exported.
This is on a Windows 8.1 machine with rustc 1.0.0-nightly (522d09dfe 2015-02-19) (x86)
Update: It looks like when compiling a dynamically linked file with rustc -C prefer-dynamic, the DLL size shrinks to 60kiB and there are only 3 extra exports (http://pastebin.com/G0AYZrpF) which all look quite reasonable. But I'd still prefer a statically linked library.
Recently the new crate type "cdylib" has been added that likely better fits your use-case. Replace the first line of your source file with:
#![crate_type = "cdylib"]
When using the Cargo package manager instead of directly calling rustc update Cargo.toml to contain the following lines:
[lib]
crate-type = ["cdylib"]
For more details have a look at Rust pull request #33553.
In my test it decreased the size of the following simple "Hello World" DLL from 650k (dylib) down to 8k (cdylib). Also the number of exported symbols is decreased massively.
#[no_mangle]
pub extern fn hello_rust() -> *const u8 {
"Hello, world!\0".as_ptr()
}

Resources