Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create PWSTR and PSTR from literals #581

Closed
rylev opened this issue Mar 9, 2021 · 15 comments · Fixed by #1891
Closed

Create PWSTR and PSTR from literals #581

rylev opened this issue Mar 9, 2021 · 15 comments · Fixed by #1891
Labels
enhancement New feature or request

Comments

@rylev
Copy link
Contributor

rylev commented Mar 9, 2021

Right now literals need to be copied into new buffers every time one is passed into a function that expects a PWSTR. Instead we should provide some type of macro convenience for creating PSTR and PWSTR from literals. These would encode the literals properly and add a trailing \0 at the end all at compile time. Bonus points if we can make macro that works for either kind of string pointer.

@rylev rylev added the enhancement New feature or request label Mar 9, 2021
@lespea
Copy link

lespea commented Mar 10, 2021

Could something like this work? https://github.com/Lokathor/utf16_lit

@rylev
Copy link
Contributor Author

rylev commented Mar 10, 2021

Yes, I also have my own crate that does this https://github.com/rylev/const-utf16 which introduced const compilation a bit before utf16_lit did. I think either one would be fine to use.

@kennykerr
Copy link
Collaborator

Do we need to use a macro? It would be nice if we could (also) write a const function to do this so you can write something like this:

let s = PSTR::from("hello");

It would somehow have to distinguish between a string literal and a regular string reference. I'm not sure if that's possible.

@agausmann
Copy link

Any function that takes a &str, &[u8], &[u16] etc with the goal of null-terminating it or transcoding it (ie. UTF-8 to UTF-16) would have to use another buffer to store the result, because the value can't be modified in-place. To get around that, the idea here is to make a new literal string that gets stored in the program's static storage at compile-time.

Functions cannot distinguish literals in their input (although you can get close with &'static str), and furthermore, they cannot create new literals (even if they're const). Literals can only be created by using literal syntax, so they have to be written in the code, either directly or by a macro.

For example, to convert a literal b"abcd" to one that is null-terminated, we can write a macro to rewrite the literal, syntactically, to b"abcd\0". That's the only way to do so at compile-time.

@rylev
Copy link
Contributor Author

rylev commented Mar 11, 2021

There is an issue with the fact that PWSTR and PSTR are only generated types and not owned by the windows crate so the windows crate can't expose any public API that produces either of these types because they would be different types from the ones the user themselves is generating. This is another case where we need to figure out #432 first.

@1Dragoon
Copy link

What's the current idiomatic way of creating a PWSTR from a string literal? I used this but it doesn't appear to work when I give it to a function that needs a pwstr for an input:

let text = String::from("my pwstr literal").encode_utf16().collect::<Vec<u16>>();
let mypwstr = PWSTR(text.as_mut_ptr());

@kennykerr
Copy link
Collaborator

The API is likely assuming a null terminator.

@agausmann
Copy link

agausmann commented Mar 31, 2021

Yep, I'd recommend using a crate like widestring. I use widestring::U16CString to create PWSTR values, it automatically converts strings to UTF-16 and null-terminates them.

@1Dragoon
Copy link

1Dragoon commented Apr 1, 2021

The null terminator is no doubt part of it but actually it looks like the trait constraints of the way some functions are now defined don't allow for null pointers when the API expects them in some cases, which is where I was actually stuck (the compiler gave an ambiguous message that made me think I was using the wrong type at first.) Namely this:

https://docs.microsoft.com/en-us/windows/win32/api/adshlp/nf-adshlp-adsopenobject

Where in the example they pass a null pointer to lpzusername and lpzpassword. But in the windows-rs crate it MUST be PWSTR per the type constraints:

https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/ActiveDirectory/fn.ADsOpenObject.html

So my previous way of simply passing a ptr::null_mut() doesn't work with the newest version of windows-rs when I want passive authentication. Is there another way of doing that?

Sorry may be off topic, I can create a separate issue if required.

@kennykerr
Copy link
Collaborator

Here's an example (the last parameter is effectively a null pointer value).

let event = CreateEventW(std::ptr::null_mut(), true, false, PWSTR::default());

@1Dragoon
Copy link

1Dragoon commented Apr 1, 2021

Ah I figured that was more analogous to an empty string than a null pointer, but at first glance it seems to work, thanks!

@wanderer06
Copy link

wanderer06 commented Apr 17, 2021

Might be late to the party, but I got around it using my own impl:

impl SystemServices::PWSTR {
    fn from(text: &'static str) -> Self {
        Self(text.encode_utf16().chain(::std::iter::once(0)).collect::<Vec<u16>>().as_mut_ptr())
    }
}

Then used it like so:

let class_name = SystemServices::PWSTR::from("Hello world?");

@Neopallium
Copy link

Might be late to the party, but I got around it using my own impl:

impl SystemServices::PWSTR {
    fn from(text: &'static str) -> Self {
        Self(text.encode_utf16().chain(::std::iter::once(0)).collect::<Vec<u16>>().as_mut_ptr())
    }
}

That creates a Vec<u16> and get a raw pointer .as_mut_ptr() to the vec's internal data. The Vec will be freed and that pointer will not be valid.

@kennykerr
Copy link
Collaborator

Playing with an implementation of this here: https://github.com/microsoft/windows-rs/compare/utf16?expand=1

@kennykerr
Copy link
Collaborator

#1891 introduced string literal macros that address this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants