Grob - Growable buffer especially useful for Windows API calls

Huzzah! I finally had time to publish my first crate. grob - gro(wable) b(uffer).

Everyone who has spent enough time with the Windows API (@jbe) eventually has to work with a function that requires a growable buffer. GetAdaptersAddresses is a great example. When called the adapteraddresses and sizepointer parameters define a buffer where GetAdaptersAddresses will store data. If the buffer is too small sizepointer is set to the buffer size needed and an error is returned. The caller then has to allocate a larger buffer and try again. With this crate, calling a function like GetAdaptersAddresses is as simple as this...

use windows::Win32::NetworkManagement::IpHelper::GetAdaptersAddresses;
use windows::Win32::NetworkManagement::IpHelper::GET_ADAPTERS_ADDRESSES_FLAGS;
use windows::Win32::Networking::WinSock::AF_UNSPEC;

use grob::{winapi_large_binary, RvIsError};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    winapi_large_binary(
        |argument| {
            RvIsError::new(unsafe {
                GetAdaptersAddresses(
                    AF_UNSPEC.0 as u32,
                    GET_ADAPTERS_ADDRESSES_FLAGS(0),
                    None,
                    Some(argument.pointer()),
                    argument.size(),
                )
            })
        },
        |frozen_buffer| {
            if let Some(mut p) = frozen_buffer.pointer() {
                while p != std::ptr::null() {
                    println!("FriendlyName = {}", unsafe { (*p).FriendlyName.display() });
                    p = unsafe { (*p).Next };
                }
            }
            Ok(())
        },
    )?;
    Ok(())
}

All comments, both negative and positive, encouraged!

RE: How to allocate memory for an FFI call

I wonder if there's a way to get windows builds succeed on docs.rs? (build failure log)

Maybe you need the following in your Cargo.toml file:

[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"

See Metadata for custom builds on docs.rs.

I wonder what this second closure is used for. Can't you return a struct that supports dereference to allows obtaining the pointer through a method, and which will release the memory when dropped?

1 Like

Thank you for the feedback!

Yeah. I saw that. All Windows things are in two modules. In the worst case I can add conditionals to exclude those two for now.

Thanks. I'll give that a try.

To process the data.

There is a generic function + example that does that for you for API functions that return a path and another pair for API functions that return a string. The second closure is provided for you.

Absolutely! Whatever that closure returns is what gets returned from winapi_large_binary. But, I assumed the user would convert the raw Windows data to something Rusty then return that. The generic functions do not have a provision for returning the raw data. When the second closure returns the buffer is freed.

The inner layer is also fairly easy to use (it's just a bit verbose). At that layer the raw buffer is yours to do with as you please.

Something like this?

That worked! Thanks!

Now I need to write the actual documentation.

Now with Miri unit tests.

And an example that calls GetFileVersionInfoW for many files in the Windows directory.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.