在ChayimFriedman的回答基础上,这是我解决这个问题的方法:
use std::{alloc::Layout, error::Error, fmt::Debug};
use windows::Win32::Graphics::Gdi::{LOGPALETTE, PALETTEENTRY};
pub struct LogPalette {
data: *mut LOGPALETTE,
layout: Layout,
}
impl Debug for LogPalette {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogPalette")
.field("version", &self.get_version())
.field("entries", &self.get_entries())
.finish()
}
}
impl LogPalette {
pub fn new(
version: u16,
data: impl ExactSizeIterator<Item = PALETTEENTRY>,
) -> Result<LogPalette, Box<dyn Error>> {
let num_entries: u16 = data.len().try_into()?;
unsafe {
let layout = Layout::new::<LOGPALETTE>()
.extend(Layout::array::<PALETTEENTRY>(
num_entries.saturating_sub(1).into(),
)?)?
.0
.pad_to_align();
let logpalette = std::alloc::alloc(layout).cast::<LOGPALETTE>();
if logpalette.is_null() {
std::alloc::handle_alloc_error(layout);
}
std::ptr::addr_of_mut!((*logpalette).palVersion).write(version);
std::ptr::addr_of_mut!((*logpalette).palNumEntries).write(num_entries);
let entries = std::ptr::addr_of_mut!((*logpalette).palPalEntry).cast::<PALETTEENTRY>();
for (index, entry) in data.enumerate().take(num_entries.into()) {
entries.add(index).write(entry);
}
Ok(Self {
data: logpalette,
layout,
})
}
}
pub fn get_raw(&self) -> *const LOGPALETTE {
self.data
}
pub fn get_raw_mut(&mut self) -> *mut LOGPALETTE {
self.data
}
pub fn get_entries(&self) -> &[PALETTEENTRY] {
unsafe {
let size = self.get_num_entries();
let pal_ptr = std::ptr::addr_of!((*self.data).palPalEntry);
std::slice::from_raw_parts(pal_ptr.cast(), size.into())
}
}
pub fn get_entries_mut(&mut self) -> &mut [PALETTEENTRY] {
unsafe {
let size = self.get_num_entries();
let pal_ptr = std::ptr::addr_of_mut!((*self.data).palPalEntry);
std::slice::from_raw_parts_mut(pal_ptr.cast(), size.into())
}
}
pub fn get_version(&self) -> u16 {
unsafe { (*self.data).palVersion }
}
pub fn get_num_entries(&self) -> u16 {
unsafe { (*self.data).palNumEntries }
}
}
impl Drop for LogPalette {
fn drop(&mut self) {
unsafe {
std::alloc::dealloc(self.data.cast(), self.layout);
}
}
}
fn main() {
let entries = [
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 0,
peFlags: 0,
},
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 255,
peFlags: 0,
},
PALETTEENTRY {
peRed: 0,
peGreen: 0,
peBlue: 255,
peFlags: 0,
},
];
let mut palette = LogPalette::new(1, entries.into_iter()).unwrap();
println!("{:#?}", palette);
println!();
println!("Setting red value of entry 2 to 127 ...");
println!();
palette.get_entries_mut()[2].peRed = 127;
println!("{:#?}", palette);
}
LogPalette {
version: 1,
entries: [
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 0,
peFlags: 0,
},
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 255,
peFlags: 0,
},
PALETTEENTRY {
peRed: 0,
peGreen: 0,
peBlue: 255,
peFlags: 0,
},
],
}
Setting red value of entry 2 to 127 ...
LogPalette {
version: 1,
entries: [
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 0,
peFlags: 0,
},
PALETTEENTRY {
peRed: 255,
peGreen: 128,
peBlue: 255,
peFlags: 0,
},
PALETTEENTRY {
peRed: 127,
peGreen: 0,
peBlue: 255,
peFlags: 0,
},
],
}
windows
和windows-sys
包。 - Chayim Friedmanmem::transmute
。 - IInspectableLOGPALETTE
和PALETTEENTRY
。当然这并没有回答问题,只是一句注释。 - FinomnisLOGPALETTE
指针。但是,如果你想在 Rust 中创建它,将其传递给 C,然后在 C 中释放它,那么我认为你需要采用不太美观的指针方式。 - Finomnis