Windows-rs: Correct use of data types?

Created on 12 Feb 2021  路  15Comments  路  Source: microsoft/windows-rs

I'm trying to use ADSI, and I'm stumbling at the basics. Although I'm passing just a null pointer to ADsGetObject, at this point it shouldn't matter as the pointer gets overwritten. Nonetheless, it returns an error code:

    let mut p_ads: *mut c_void = ptr::null_mut();
    let pp_ads = &mut p_ads as *mut *mut c_void;

    unsafe {
        let mut ldap_root: Vec<u16> = "LDAP://rootDSE".encode_utf16().collect();
        ldap_root.push(0);
        let hr = ADsGetObject(
            ldap_root.as_mut_ptr() as _,
            & IADs::IID,
            pp_ads,
        );
        println!("{}", hr.is_err());
    }

Am I passing something incorrectly? Based on my understanding this should be 16-bit null terminated strings, GUID of the desired interface, and a pointer to the pointer of the struct. The powershell analog of this would be [adsi]'LDAP://rootDSE', which works on this machine, so I'm not sure why this wouldn't.

question

Most helpful comment

I would also suggest _not_ using raw pointers. Perhaps something like this:

fn main() -> Result<()> {
    initialize_mta()?;

    let path = HString::from("LDAP://RootDSE");
    let mut object: Option<IADs> = None;

    unsafe {
        ADsGetObject(path.as_wide().as_ptr(), &IADs::IID, object.set_abi()).ok()?;
    }

    Ok(())
}

I am also working on some return value transformations that will let you write something far simpler:

let object: IADs = ADsGetObject(path.as_wide().as_ptr()).ok()?;

All 15 comments

Figured it out, needed to coinitialize :)

I would also suggest _not_ using raw pointers. Perhaps something like this:

fn main() -> Result<()> {
    initialize_mta()?;

    let path = HString::from("LDAP://RootDSE");
    let mut object: Option<IADs> = None;

    unsafe {
        ADsGetObject(path.as_wide().as_ptr(), &IADs::IID, object.set_abi()).ok()?;
    }

    Ok(())
}

I am also working on some return value transformations that will let you write something far simpler:

let object: IADs = ADsGetObject(path.as_wide().as_ptr()).ok()?;

Oh that's neat. I don't suppose there's some syntactic sugar for extracting BSTR from the IADs methods is there? For example, to avoid this mess:

            let mut foo = &mut BSTR::default();
            let fa = (*ad_obj).get_ADsPath(foo);

I still haven't even figured out how to get a regular String from it yet.

BSTR integration is coming soon (hint: the BSTR struct will go away). https://github.com/microsoft/windows-rs/issues/484

For now, you can write something like this:

fn main() -> Result<()> {
    initialize_mta()?;

    let path = HString::from("LDAP://RootDSE");

    unsafe {
        let mut object: Option<IADs> = None;

        let object =
            ADsGetObject(path.as_wide().as_ptr(), &IADs::IID, object.set_abi()).and_some(object)?;

        let mut path = BString::new();
        object.get_ADsPath(path.set_abi() as _).ok()?;
        println!("{}", path);
    }

    Ok(())
}

You can also use to_string to get a String from the BString.

Hmm I can't get it to work, I tried this for example:

let path = HString::from(r"LDAP://CN=Smith\, John,OU=Users,DC=example,DC=com");

unsafe {
    let mut object: Option<IADsUser> = None;

    let object =
        ADsGetObject(path.as_wide().as_ptr(), &IADsUser::IID, object.set_abi()).and_some(object)?;

    let mut desc = BString::new();
    object.get_Description(desc.set_abi() as _).ok()?;
    println!("{}", desc.to_string());
}

I figured that would return John Smith's AD description as a string, but instead it just returns a long hex string, though this may be getting outside of the scope of the Windows-RS crate. In any case, thanks a lot for helping with this! I've been wanting to use rust more at work but haven't been able to because I do a lot with active directory, and the ldap3 crate unfortunately doesn't support integrated authentication so I can't use it.

My example works for me, but I'm just getting back the AD path so it's pretty simple. It does however validate that the Rust bindings are working. Beyond that yes, it's more about Active Directory than about Rust.

Oh actually it looks like the machine I was testing it on today still had an older version of the crate in the cargo.toml (I was wondering why only coinitialize worked and not initialize_mta on this computer) and it works now. Looks like I have my weekend planned :) One more thing, I noticed that IADsUser::Get() requires VARIANT, is that something on the radar for implementation, or is there any kind of workaround for now?

Great, glad you're unblocked!

Yes, VARIANT support is coming. Much like BSTR, there will likely be a helper struct named Variant that provides friendlier access to the underlying union.

Is there an issue tracker for Variant support? I couldn't find one. If not, mind if I create one?

Please go ahead - thanks!

Is there a more correct way to create BSTR objects? This kind of feels like using a nuclear bomb as a scalpel:

let desc = "John Smith's Description";
let bstr: BSTR = std::mem::transmute(BString::from(desc));
object.put_Description(bstr).ok()?;
object.SetInfo().ok()?;

Soon the BSTR struct will be no longer and instead remapped directly to windows::BString.

Sorry to keep bugging you I'm new to software development in general :) I think I may not be properly hinting to the method call that I'm passing an array, or that I'm receiving one either. What is the correct way to handle this?

        let path = HString::from("LDAP://CN=joe.doe,CN=Users,DC=example,DC=com");

        let mut ido: Option<IDirectoryObject> = None;
        let ido =
            ADsGetObject(
                path.as_wide().as_ptr(), 
                &IDirectoryObject::IID, 
                ido.set_abi()
            ).and_some(ido)?;

        let gn = HString::from("name");
        let sn = HString::from("sn");
        let ot = HString::from("description");

        let mut attrnames = [gn.as_wide(), sn.as_wide(), ot.as_wide()];
        let numret = ptr::null_mut();
        let mut attrinfo: *mut ADS_ATTR_INFO = ptr::null_mut();

        let hr = ido.GetObjectAttributes(
            attrnames.as_mut_ptr() as _,
            3,
            &mut attrinfo,
            numret
        );
        println!("{:?}", hr.ok() );

Figured out what was wrong:

HString cannot be used inside of an array, you have to use the &str::encode_utf16().collect() method to get wide strings or else you end up with some memory corruption for reasons I don't fully understand. @kennykerr this could be a bug, I'm not sure.

And I wasn't using the dword pointer correctly.

Fixed code:

        let path = HString::from("LDAP://CN=joe.doe,CN=Users,DC=example,DC=com");
        let mut ido: Option<IDirectoryObject> = None;
        let ido =
            ADsGetObject(
                path.as_wide().as_ptr(), 
                &IDirectoryObject::IID, 
                ido.set_abi()
            ).and_some(ido)?;

        let gn: Vec<u16> = "givenName\0".encode_utf16().collect();
        let sn: Vec<u16> = "sn\0".encode_utf16().collect();
        let ot: Vec<u16> = "otherTelephone\0".encode_utf16().collect();

        let mut attrnames = vec![gn.clone(), sn.clone(), ot.clone()];
        let mut numret = 0;
        let mut attrinfo: *mut ADS_ATTR_INFO = ptr::null_mut();

        let hr = ido.GetObjectAttributes(
            attrnames.as_mut_ptr() as _,
            attrnames.len() as _,
            &mut attrinfo,
            &mut numret
        );

Looks like everything's been resolved. If you run into anything else, please create a new issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ZCWorks picture ZCWorks  路  3Comments

06393993 picture 06393993  路  3Comments

DJankauskas picture DJankauskas  路  3Comments

bdbai picture bdbai  路  3Comments

rylev picture rylev  路  4Comments