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

How to use LPPROC_THREAD_ATTRIBUTE_LIST? #771

Closed
ibigbug opened this issue May 6, 2021 · 5 comments
Closed

How to use LPPROC_THREAD_ATTRIBUTE_LIST? #771

ibigbug opened this issue May 6, 2021 · 5 comments
Labels
question Further information is requested

Comments

@ibigbug
Copy link

ibigbug commented May 6, 2021

Basically I want to translate this from C# into Rust: https://github.com/microsoft/terminal/blob/2559ed6efa05cb8f999fbf0b8d890ec81161220a/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Processes/ProcessFactory.cs#L44

I was able to do this to get the lpSize:

InitializeProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST: NULL, 1, 0, &mut lpSize);

However I need to manually alloc the memory for LPPROC_THREAD_ATTRIBUTE_LIST with a fixed size and pass the memory to InitializeProcThreadAttributeList again. this is what I'm doing:

  let mut start_info: STARTUPINFOEXW = { mem::zeroed() };
  start_info.StartupInfo.cb = mem::size_of::<STARTUPINFOEXW>() as u32;
  let mut lpAttributeList: Box<[u8]> = vec![0; lpSize].into_boxed_slice();
  start_info.lpAttributeList = &mut *lpAttributeList;   // this line doesn't work

However it doesn't allow me to do the above:

error[E0308]: mismatched types
  --> terminal\src\windows\process.rs:57:34
   |
57 |     start_info.lpAttributeList = &mut *lpAttributeList;
   |                                  ^^^^^^^^^^^^^^^^^^^^^ expected struct `terminal::process::bindings::Windows::Win32::SystemServices::LPPROC_THREAD_ATTRIBUTE_LIST`, found `&mut [u8]`

I feel this is kind of more like a Rust question, thought would be great to get some help here :)

thanks.

@MarijnS95
Copy link
Contributor

MarijnS95 commented May 7, 2021

You are right in that this is most likely a general Rust issue of the form "how do I assign a pointer of type x to some field y", or in this case how to get such a pointer in the first place.

As such always paste the compiler error so that everyone - even those with no direct knowledge of this API nor what it generates to - can have a shot at answering, or find a similar solution to their own usecase.

If you check the definition of LPPROC_THREAD_ATTRIBUTE_LIST it is a simple newtype over c_void. Meaning you can construct a vec![0u8; lpSize] and store that in STARTUPINFOEXW using LPPROC_THREAD_ATTRIBUTE_LIST(lpAttributeList.as_mut_ptr().cast::<_>()).

@kennykerr kennykerr added the question Further information is requested label May 7, 2021
@ibigbug
Copy link
Author

ibigbug commented May 7, 2021

Thanks @MarijnS95 !

this solved all my questions regarding how to convert those pointers into the structs.

I also noticed that there could be several different way to initialize an "empty" struct, hope this is more related to the usage of this library

usually I can do:

  • LPPROC_THREAD_ATTRIBUTE_LIST: NULL
  • LPPROC_THREAD_ATTRIBUTE_LIST: default()
  • manual allocate memory like above

And I understand the default() comes from #[derive(Default)]

do you have a link or doc telling about the difference and when to use which for best practice? (please let me know if it's too off topic I can create another issue if it's better)

As such always paste the compiler error so that everyone - even those with no direct knowledge of this API nor what it generates to - can have a shot at answering, or find a similar solution to their own usecase.

this makes a lot sense. I was assuming that the error message for this particular use case can't be different, and didn't realize it will help other people for searching purpose maybe. I've added into the original post.

@MarijnS95
Copy link
Contributor

For one LPPROC_THREAD_ATTRIBUTE_LIST is not a struct but a newtype to *mut c_void. There's an associated NULL constant generated for easy access, but it has a Default implementation too.

default() may or may not zero-initialize, depending on the implementation. #[derive(Default)] calls the Default implementation for all fields, but it can also be given a manual implementation. In case of Windows bindings that should pretty much always be equivalent to zero-initialization, too.

As an improvement, can use a struct constructor:

let start_info = STARTUPINFOEXW {
    StartupInfo: STARTUPINFOW {
        cb: mem::size_of::<STARTUPINFOEXW>() as u32,
        ..Default::default()
    },
    lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST(lpAttributeList.as_mut_ptr().cast::<_>()),
};

this makes a lot sense. I was assuming that the error message for this particular use case can't be different, and didn't realize it will help other people for searching purpose maybe. I've added into the original post.

Helping others finding a similar answer to their own solution isn't nearly as relevant as the error is for finding what the problem is in the first place. If you write // this line doesn't work no-one knows what is wrong exactly unless they replicate the code in a test project or look at the implementation - it is unlikely that everyone knows the exact API/structs from the top of their head. At that point it's just guesswork and looking at the documentation to spot the type difference.

@ibigbug
Copy link
Author

ibigbug commented May 7, 2021

thanks again for the knowledge sharing and making this community so friendly!

@michaelvanstraten
Copy link

This might be interesting for anybody wanting to use this feature in combination with the std::process::Command type: rust-lang/rust#123604.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants