Comments (19)
Because of Rust's strict lifetime checks, you must store the strings in an external place whose lifetime extends beyond the event closure. The ovbious place is the window object itself. From the docs example, we could have:
#[derive(Clone)]
pub struct WndMain {
lv: gui::ListView,
strs: Vec<WString>,
}
This would allow us to have something like this:
let self2 = self.clone();
lv.on().lvn_get_disp_info(|di| {
let item_idx = di.item.iItem as usize;
di.item.set_pszText(self2.ee.get_mut(item_idx));
Ok(())
});
Just keep in mind that LVN_GETDISPINFO
is tricky notification, because it's wildly unsafe. It may be hard to get it right.
from winsafe.
Umm..
I need to share the Vec between each event handler, but if I use Rc or Arc, the lifetime will not meet.
from winsafe.
but if I use Rc or Arc, the lifetime will not meet
If will if you clone it into the closure.
from winsafe.
from winsafe.
This question is tricker than I thought. The root cause is that, in C++, you pass a pointer to a string allocated somewhere else, and Rust's borrow checker goes crazy with such things.
The best I could come up with is to use an unsafe
block to bypass the borrow checker:
let self2 = self.clone();
self.lv.on().lvn_get_disp_info(move |di| {
let item_idx = di.item.iItem as usize;
let mut strs = self2.strs.borrow_mut();
let str_mut = strs.get_mut(item_idx).unwrap();
let str_ptr = str_mut as *mut w::WString;
di.item.set_pszText(Some(unsafe { &mut *str_ptr }));
Ok(())
});
I can't test this right now, there's a risk of invalid memory access somehow (just like in C++). Please let me know if this approach works for you.
from winsafe.
It does work but umm...
If I enable LTO in the build, the code won't work.
from winsafe.
Can you post your code here, so I can try to reproduce this?
from winsafe.
main.rs
use std::{cell::RefCell, rc::Rc};
use winsafe::{
co,
gui::{self, Horz, Vert},
msg,
prelude::*,
AnyResult, WString,
};
#[derive(Clone)]
struct DemoWindow {
window: gui::WindowMain,
list: gui::ListView,
insert_button: gui::Button,
items: Rc<RefCell<Vec<WString>>>,
}
impl DemoWindow {
fn new() -> Self {
let window = gui::WindowMain::new(gui::WindowMainOpts {
title: "Demo Window".to_owned(),
size: (1024, 768),
..Default::default()
});
let list = gui::ListView::new(
&window,
gui::ListViewOpts {
position: (0, 68),
size: (1024, 700),
list_view_style: co::LVS::REPORT
| co::LVS::NOSORTHEADER
| co::LVS::SHOWSELALWAYS
| co::LVS::SHAREIMAGELISTS
| co::LVS::OWNERDATA,
columns: vec![("Column 1".to_owned(), 600)],
resize_behavior: (Horz::Resize, Vert::Resize),
..Default::default()
},
);
let insert_button = gui::Button::new(
&window,
gui::ButtonOpts {
text: "Insert new item".to_owned(),
position: (0, 0),
width: 100,
..Default::default()
},
);
let demo_window = DemoWindow {
window,
list,
insert_button,
items: Rc::new(RefCell::new(Vec::new())),
};
demo_window.handle_events();
demo_window
}
fn handle_events(&self) {
let self_cloned = self.clone();
self.insert_button.on().bn_clicked(move || {
let count = self_cloned.list.items().count();
self_cloned
.items
.borrow_mut()
.push(WString::from_str(count.to_string()));
self_cloned
.list
.hwnd()
.SendMessage(msg::lvm::SetItemCount {
count: count + 1,
behavior: co::LVSICF::NoValue,
})?;
Ok(())
});
let self_cloned = self.clone();
self.list.on().lvn_get_disp_info(move |info| {
let index = info.item.iItem as usize;
let mut strs = self_cloned.items.borrow_mut();
let str_mut = strs.get_mut(index).unwrap();
let ptr = str_mut as *mut WString;
info.item.set_pszText(Some(unsafe { &mut *ptr }));
Ok(())
});
}
fn run(&self) -> AnyResult<i32> {
self.window.run_main(None)
}
}
fn main() {
let app = DemoWindow::new();
let _ = app.run();
}
Cargo.toml
[package]
name = "virtual-list-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
winsafe = { git = "https://github.com/rodrigocfd/winsafe", features = [ "gui" ] }
[profile.release]
opt-level = 3
lto = true # <- comment out this line otherwise nothing will be shown in the list view
from winsafe.
According to this article, we must copy the characters to the buffer pointed by LVITEM
, instead of setting the pointer. Until now this was not possible, because the pointer was protected.
So I implemented a new method, raw_pszText
, which returns the raw pointer. With it, we can write the following:
let self_cloned = self.clone();
self.list.on().lvn_get_disp_info(move |info| {
if info.item.mask.has(co::LVIF::TEXT) { // is this a text request?
let index = info.item.iItem as usize;
let strs = self_cloned.items.borrow();
let str_ref = strs.get(index).unwrap(); // string for the requested item
let (ptr, cch) = info.item.raw_pszText(); // retrieve raw pointer
let out_slice = unsafe { std::slice::from_raw_parts_mut(ptr, cch as _) };
out_slice.iter_mut()
.zip(str_ref.as_slice())
.for_each(|(dest, src)| *dest = *src); // copy from our string to their buffer
}
Ok(())
});
Let me know if this works for you.
Note that you don't necessarily need to keep a Vec<WString>
. If more practical, you can keep a Vec<String>
instead, and perform the conversion String
-> WString
when processing lvn_get_disp_info
.
from winsafe.
Yes, it works, thank you!
from winsafe.
I reference comctl32
implements from Wine and some blahblah source codes from some blahblah Microsoft partners, both of them will allocate buffer to pszText
with cchTextMax * sizeof(WCHAR)
length and then free it after use, so it's safe to copy string to pszText
.
It's so weird the MSDN document is misleading...
But if WString
is much larger than cchTextMax
, it may overflow...
from winsafe.
I made a small improvement for the LVM_SETITEMCOUNT
message, which now can be sent straight from the control itself.
Instead of:
self_cloned
.list
.hwnd()
.SendMessage(msg::lvm::SetItemCount {
count: count + 1,
behavior: co::LVSICF::NoValue,
})?;
you can do just:
self_cloned.list.items()
.set_item_count(count + 1, None);
from winsafe.
Neat!
from winsafe.
But if
WString
is much larger thancchTextMax
, it may overflow...
In my example it won't, because I'm copying with zip
.
from winsafe.
Won't there be something other than \0
in the last element of out_slice
if str_ref
is too long?
from winsafe.
Minor nitpick: the method is called set_count
, not set_item_count
.
Anyway,
Won't there be something other than
\0
in the last element ofout_slice
ifstr_ref
is too long?
yes, indeed. I've ran into this in the past, so I forgot I already implemented a safe way to do this, the WString::copy_to_slice
method, which takes care of the terminating null.
So, instead of
out_slice.iter_mut()
.zip(str_ref.as_slice())
.for_each(|(dest, src)| *dest = *src); // copy from our string to their buffer
just use
str_ref.copy_to_slice(out_slice); // copy from our string to their buffer
from winsafe.
okay...
from winsafe.
Umm..Will there be a safer method to set text?
from winsafe.
Umm..Will there be a safer method to set text?
Even if I write a wrapper to set the text, we don't own that pointer (Windows does), so it could still trigger UB, so it would have to be unsafe as well.
The only way I see to provide a safe wrapper would be to add another level of wrappers over all events. That would be very time-consuming and potentially slower, so I don't really think it's a good idea for now.
from winsafe.
Related Issues (20)
- winsvc.h HOT 2
- validate_retrieved_reg_val always fails
- [Question] - Calling windows APIs twice best practice HOT 3
- RegQueryValueEx returning TRANSACTION_REQUEST_NOT_VALID for valid data HOT 6
- nm_custom_draw should provide a mutable reference? HOT 6
- How do you envision WM_USER being used? HOT 1
- `WC::NoValue`? HOT 1
- I don't think `MultiByteToWideChar` and `WideCharToMultiByte` need to add 1 to `num_bytes` HOT 4
- resizable_layout not work as expect HOT 8
- When the new_dlg function is used to create a window, the wm_create event does not work HOT 6
- Async IO should be marked as "unsafe" HOT 1
- CreateToolhelp32Snapshot error when compiling for x86
- Panic in wstring/ QueryFullProcessImageName HOT 3
- How to set up a transparent window? HOT 1
- please font settings HOT 7
- Add missing API to open app in foreground? HOT 3
- Possible ShellExecuteEx implementation HOT 8
- Linker errors in Windows 7
- Add support for raw-window-handle HOT 3
- Can't run library tests on 0.0.21 HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from winsafe.