Comments (8)
Making the handles non-Copy
wouldn't fix this issue either. An user could close a handle and still keep it indefinitely, then call a function with this handle later – pontentially causing the behavior you described. That's a rather hard problem to solve.
One solution I can think of is to make the function calls operate by reference (instead of by value), and "closing" functions would then set the handle to null
. Subsequent calls would then return an ERROR_INVALID_HANDLE
(if anything), which could be properly caught as ordinary errors.
What do you think?
from winsafe.
What would be most intuitive to me is that the handles are non-Copy
, non-Clone
, do not have an explicit close function, and automatically close on drop. Is there a reason that wouldn’t work?
The iterator used for enumerating values would have to borrow the handle, I assume. That seems sensible to me, to ensure the handle outlives the iterator.
from winsafe.
This would be the perfect solution – in fact, my very first implementation tried to follow this concept. The problem is that sometimes you should not close the handles.
From the top of my head:
-
basically all
HWND
functions, likeCreateWindowEx
andGetParent
; -
the messy GDI objects, whose handles are sometimes returned again by
SelectObject
; -
the
HIMAGELIST
, which is created and, if passed to a ListView withLVS_SHAREIMAGELISTS
style, should not be deleted (the ListView takes ownership of it).
After facing many weird situations, I eventually gave up the idea.
However...
Rethinking this solution, I could simply segregate the handles types. Some of them would be returned wrapped in to a smart pointer implementing Deref
, and they would implement Drop
to call the close functions, thus removing the manual close altogether. This would work like a glove for cases like HKEY
and HFILE
.
The problem with this approach is that I would have two "categories" of handles, which would be potentially confusing.
Another idea is having high-level structs, like File
does over HFILE
, to properly work on the resource cleanup. In this specific case, a high-level Registry struct.
from winsafe.
Hm, yes, there is some complexity there. Some handles are pretty simple, but others not so much.
For the HGDIOBJ
case, I thought of two ideas.
Option 1
SelectObject
takes an HGDIOBJ
by value and std::mem::forget
s it. It then takes the return value of the raw function call, i.e. the previously selected value, wraps it in a new handle wrapper, and returns it. This is pretty easy to understand, but it is somewhat limited: you can’t keep an HGDIOBJ
in a read-only place (e.g. a struct
to which you have only a shared reference) and still be able to select it into a DC, and it also means you can’t select the same object into two DCs at the same time.
Option 2
Instead of SelectObject
, you could do a scope-bound thing. So the DC
type would have something like a with_selected
method, which would be used something like this:
let my_brush = …;
let my_dc = …;
// At this time, the default brush is selected.
my_dc.with_selected(&my_brush, || {
// Inside this lambda, my_brush is selected.
// It is taken by shared reference, so it can still be reused elsewhere, but it cannot be destroyed.
});
// When the lambda completes, the original default brush is re-selected to the DC before `with_selected` returns.
// `my_brush` has now been unlocked and is safe to destroy.
I’ve seen this kind of pattern elsewhere, though I’m not sure how ergonomic it would be in this case. It also forces an extra SelectObject
call to restore the original brush; I’m not sure whether that will have a performance impact or not, and I’m not that familiar with GDI so I’m not actually even sure whether you might need to do that anyway before destroying the DC.
from winsafe.
Option 1 has the limitations you point out.
Option 2 has the problem to be completely alien to the Win32 API – if creating a whole new layer is the answer, then I'd rather implement a whole new struct over HGDIOBJ
, leaving it alone.
The idea of setting the handle to null
in "closing" functions still seems to be the most attractive to me, so far. It would prevent accidental handle reuse, which is the original problem you pointed out, and you are 100% right about this.
from winsafe.
That’s fair. Although, for a HANDLE
, what null value would you use? NULL
or INVALID_HANDLE_VALUE
?
from winsafe.
Actually, would that even help? You’d still have to make the handles non-Copy
, non-Clone
, otherwise one handle would be nulled out, but all the others wouldn’t. And even then, AFAICT it wouldn’t stop someone from deleting a GDI object that was currently selected, would it? Is it actually possible to do this safely, or do the handles themselves need to be unsafe and then the higher-level wrappers be safe?
from winsafe.
I believe INVALID_HANDLE_VALUE
would be clearer for the given intent.
Yes, the handles would still have to be non-Copy
and non-Clone
.
It has been a long time since I wrote my last GDI program, but as far as I remember, deleting a GDI object which is in use simply will revert the resource to its stock one, not causing any other harms. I'll have to test this. The high-level wrapper will certainly prevent this, though.
from winsafe.
Related Issues (20)
- 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
- Public access ot LocalFreeGuard::new or LocalFree HOT 2
- [BUG?QUESTION?]SendInput example not working HOT 1
- [Question] How can I set the pszText into NMLVDISPINFO correctly when handling lvn_get_disp_info event? HOT 19
- [Question] Using MutFn as WinApi callback
- WString pointers cause issues on stack for certain API calls HOT 5
- Build fails for the i686-pc-windows-(msvc/gnu) targets HOT 8
- winsvc.h HOT 2
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.