I am using windows crate to retrieve the HWND of other programs running right now in the computer, they are not handles to windows of my own process but other ones with EnumThreadWindows. I get some handles to the windows and now I would like to create a thread for each of those windows and move the HWND there.
The problem is that HWND contains c_void so i'm getting: the trait std::marker::Send is not implemented for *mut std::ffi::c_void
Reading in this forum and in some other places (and from my knowledge), HWND is not actually a pointer but a handle. And from what i read is marked as not send to prevent using the window handle in other threads that did not create the window. In my case this restriction does not apply since my program is not even the HWND owner and i want to work with those.
Which would be the best/proper way to move each HWND to each of the new created threads?
I think Rust does not consider "raw" pointers to be "safe" for moving between threads.
Whether a HWND actually is a pointer or more like a "handle" (ID) doesn't really matter here. For as long as it is defined as *mut std::ffi::c_void, it will be treated as a pointer by Rust!
So, you probably have to resort to "unsafe" code.
If you wrap the pointer (HWND) in a struct, then you can make it Send as follows:
This is where FFI gets a bit tricky, in general Rust can't make assumptions about whether some random pointer is safe to use on a different thread than it was created, or across threads simultaneously, so you need to reference documentation, which is often frustratingly silent on this.
Sometimes it's not even consistently the case, for example File handles are not considered safe to use by Windows unless you use OVERLAPPED (though I believe that's data integrity not what Rust considers unsafe) - for these you need separate types and careful API design to correctly represent the safety contract.
For this case, though, Window handles themselves are safe to use in all the contexts I'm aware of, Windows either blocks or queues calls, so go right ahead and slap a Send and Sync on that.
The traits still apply, of course; it's entirely possible for an OS to give you a handle to a resource created by another process that cannot be moved across threads (eg it's an index into a per thread table on the kernel side). It just happens that they generally don't, and indeed generally the actual resource table is machine wide (with per process permission tracking)
So then in order to slap send and sync should i do as dEajL3kA1 sugested and create a wrapper arround the std::ffi::c_void?
Or should i just edit the crate type? Which is the rusty way / considered better / good practice?
The question is which invariants apply to the pointer in question. Is it safe to transfer it between threads? If so, you can use the wrapper approach. If not, the unsafe impl will result in UB anyways.
Do you need the handler in the threads or only beforehand? If not, you can create a custom struct that contains all relevant information minus the pointer and you can transfer that struct safely between the threads (wrapped in an Arc or whatever).
If you cannot safely transfer the pointer across thread boundaries, but need it in the threads, you should acquire it within the respective threads and use another means of synchronization, e.g. via a synchronized list of IDs or so to coordinate the threads.
You can't edit the std crate really (the source the editor links you to is for your reference, it's already compiled), and it's a pretty bad idea to patch the code of other crates unless you really need it (but sometimes you do, so there are mechanisms to do so... if you're not a published crate)
But more relevantly here, making c_voidSend wouldn't even help: you want a pointer to c_void and it's the pointer that isn't Send, the target doesn't matter. You definitely can't edit the pointer type!
But even after all that, creating a wrapping type is also a more Rust-ey option; you want to expose a public interface that is impossible to use incorrectly where possible, and wrapping a type that is easy to use incorrectly is the most common way to do so.