I'm using the windows-rs, the official crate for Windows API. Usually I have no problem with data object initialization, I can use several way to do it, for example:
let bitmap = BITMAP::default(); //initialize a BITMAP object
or if it's a struct I could just fill in some values like this:
let prop1 = D2D1_RENDER_TARGET_PROPERTIES {
dpiX: Default::default(),
dpiY: Default::default(),
..Default::default()
};
or there are functions to use to do the initialization, like in the follow code, I initialized 3 objects: factory, stream and encoder, all without any issue.
let factory: IWICImagingFactory = CoCreateInstance(&CLSID_WICImagingFactory,None,CLSCTX_INPROC_SERVER)?;
let encoder = factory.CreateEncoder(&GUID_ContainerFormatJpeg, &GUID_VendorMicrosoft)?;
let stream: IWICStream = factory.CreateStream()?;
stream.InitializeFromMemory(&output)?;
encoder.Initialize(&stream, WICBitmapEncoderNoCache)?;
But when it comes to this object: IWICBitmapFrameEncode, I have no clue. First of all, I have to initialize it, otherwise it can't be used, I can't just declare it like in c++ :
IWICBitmapFrameEncode *pFrameEncode = NULL; //this is enough for c++ but not for rust!
So here is what I tried:
let frame = IWICBitmapFrameEncode::default(); //default not found.
let frame: IWICBitmapFrameEncode = Default::default(); //default not found.
let frame = IWICBitmapFrameEncode::Initialize(&self, None); //Initialize method does exist,
//but it takes 2 parameters, the first one is &self which I cannot provide.
So I tried:
let frame: IWICBitmapFrameEncode;
frame.Initialize(None); // This way it only takes one parameter (which can be set to None) but the complier still complains: used binding `frame` isn't initialized.
I'm pretty sure this IWICBitmapFrameEncode it not a return value from some other function, So I can't initialize it that way either. So I'm stuck.
the definition of IWICBitmapFrameEncode :
pub struct IWICBitmapFrameEncode(pub ::windows::core::IUnknown);
impl IWICBitmapFrameEncode {
#[doc = "*Required features: `\"Win32_System_Com_StructuredStorage\"`*"]
#[cfg(feature = "Win32_System_Com_StructuredStorage")]
pub unsafe fn Initialize<P0>(&self, piencoderoptions: P0) -> ::windows::core::Result<()>
also: https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapframeencode
In c++, it's just two lines of code, All I'm trying to do is to do the same in rust. How can I transalte the following code in c++ to rust:
IWICBitmapFrameEncode *pFrameEncode = NULL; //initialization done!
hr = pEncoder->CreateNewFrame(&pFrameEncode, NULL); //now it can be used to receive value!
Note : Before I can use CreateNewFrame to receive value, I have to initialize it first. I'm stuck with the initialize part.
If you are interested in the whole context of what I'm trying to do. I'm trying to encode bitmap data to JPG data (with Direct2d interface)is all:
let factory: IWICImagingFactory = CoCreateInstance(&CLSID_WICImagingFactory,None,CLSCTX_INPROC_SERVER)?;
let encoder = factory.CreateEncoder(&GUID_ContainerFormatJpeg, &GUID_VendorMicrosoft)?;
let stream: IWICStream = factory.CreateStream()?;
stream.InitializeFromMemory(&output)?;
encoder.Initialize(&stream, WICBitmapEncoderNoCache)?;
let frame: IWICBitmapFrameEncode = IWICBitmapFrameEncode::Initialize(&self as _, None); //not working
// frame.Initialize(None); //not working
let frameopt = Some(frame);
encoder.CreateNewFrame(&frameopt as *const Option<IWICBitmapFrameEncode> as _, &mut None as _);
the "not working" part is the only problem for me.
My final code with IInspectable's help,it does pass the compiler, but there is a memory violation exception at frame.SetPixelFormat I trying to find out why. Looks like IInspectable's answer did help but I need time to fully verify it. For now I will accept his answer.
fn clipboard_bmp_to_jpg() -> std::result::Result<(), Box<dyn std::error::Error>> {
unsafe {
if (!OpenClipboard(None)).into() {
println!("open error");
}
let bitmap = BITMAP::default();
let hdl = GetClipboardData(2)?;
let h_bmp = HBITMAP(hdl.0);
let mut rst = GetObjectW(h_bmp, std::mem::size_of::<BITMAP>() as _, Some(&bitmap as *const BITMAP as _));
println!("{},{}", rst, bitmap.bmWidth);
println!("{},{}", bitmap.bmBitsPixel, bitmap.bmWidthBytes);
let num_bytes = (bitmap.bmWidthBytes*bitmap.bmHeight) as usize;
let mut pixels: Vec<u8> = Vec::with_capacity(num_bytes);
pixels.set_len(num_bytes);
let mut output: Vec<u8> = Vec::with_capacity(num_bytes);
output.set_len(num_bytes);
rst = GetBitmapBits(h_bmp, num_bytes as _, pixels.as_mut_ptr() as _);
println!("{},{}", rst, num_bytes);
let factory: IWICImagingFactory = CoCreateInstance(&CLSID_WICImagingFactory,None,CLSCTX_INPROC_SERVER)?;
let encoder = factory.CreateEncoder(&GUID_ContainerFormatJpeg, &GUID_VendorMicrosoft)?;
let stream: IWICStream = factory.CreateStream()?;
stream.InitializeFromMemory(&output)?;
encoder.Initialize(&stream, WICBitmapEncoderNoCache)?;
let mut frame = None;
let mut props = None;
let rst2 = encoder.CreateNewFrame(&mut frame, &mut props);
let frame = frame.ok_or(Error::from(E_FAIL))?;
//let props = props.ok_or(Error::from(E_FAIL))?;
//let frame: IWICBitmapFrameEncode = None.unwrap();
//let frameopt = Some(frame);
//let rst2 = encoder.CreateNewFrame(&frameopt as *const Option<IWICBitmapFrameEncode> as _, &mut None as _);
if rst2.is_err() {
println!("error CreateNewFrame");
}
let rst3 = frame.Initialize(None);
if rst3.is_err() {
println!("error Initialize");
}
frame.SetSize(bitmap.bmWidth as _, bitmap.bmHeight as _)?;
frame.SetPixelFormat(&GUID_WICPixelFormatDontCare as *const GUID as _)?; //memory violation exception here
let source = IWICBitmapSource::from_raw(pixels.as_mut_ptr() as _);
let rect = WICRect {
X: 0,
Y: 0,
Width: bitmap.bmWidth,
Height: bitmap.bmHeight,
};
let rst4 = frame.WriteSource(&source, &rect as _);
if rst4.is_err() {
println!("error WriteSource");
}
frame.Commit()?;
let rst5 = encoder.Commit();
if rst5.is_err() {
println!("error WriteSource");
}
}
Ok(())
}
COM programming in Rust using the windows
crate is both very convenient, as well as extremely hard to discover. COM interface pointers are modeled as #[repr(transparent)]
tuple structs, holding a single NonNull<c_void>
value. When used as [out]
parameters, they are wrapped behind an Option
, with the effect of having the same layout as a raw pointer, but with the inability to dereference a null pointer.
All of that is pretty solid design, at the expense of making it very challenging to discover how to play along said design's rules. When calling a COM interface method that returns an interface pointer, you'd typically create a mut
variable with value None
, pass it by &mut
, handle errors, and unwrap
(or handle the None
-case). Type inference will generally be able to deduce the T
in Option<T>
, so you don't have to spell it out.
Assuming that you have a function foo
that returns a Result<T>
you can do the following:
fn foo() -> Result<()> {
// ...
unsafe { encoder.Initialize(&stream, WICBitmapEncoderNoCache) }?;
let (frame, props) = {
// Initialize variables to `None` (i.e. null pointers)
// `T` in `Option<T>` is inferred from the subsequent call
let mut frame = None;
let mut props = None;
// Call COM method and propagate errors
unsafe { encoder.CreateNewFrame(&mut frame, &mut props) }?;
// "unwrap" the Option's and convert "null" pointers to errors
let frame = frame.ok_or(Error::from(E_FAIL))?;
let props = props.ok_or(Error::from(E_FAIL))?;
// All set, so we can move the interface pointers out
(frame, props)
};
// Use `frame` and `props` as needed
// ...
Ok(())
}
This compiles using windows
v0.44.0. If this fails for a later version, let me know.