4

I'm trying to use JNA to query the effective permissions for a file in Windows. Eventually, I plan on using the GetEffectiveRightsFromAcl function, but to do so, I need to provide a pointer to a populated TRUSTEE structure. The JNA Platform (platform.jar) doesn't appear define this struct, so I'm trying to define it myself instead. Here's what I have so far:

public static class TRUSTEE extends Structure {
    public TRUSTEE() {
        super();
    }
    public TRUSTEE(Pointer p) {
        super(p);
        read();
    }

    public Pointer pMultipleTrustee;
    public int MultipleTrusteeOperation;
    public int TrusteeForm;
    public int TrusteeType;
    public Pointer ptstrName;
}

I'm trying to populate the structure like this:

private TRUSTEE createTrusteeForCurrentUser() {
    TRUSTEE result = new TRUSTEE();
    result.TrusteeForm = TRUSTEE_FORM.TRUSTEE_IS_NAME;
    result.TrusteeType = TRUSTEE_TYPE.TRUSTEE_IS_USER;

    String strName = "CURRENT_USER";
    // How can I set result.ptstrName using strName?
}

This Google Groups thread recommends using String fields in structures when a char * is called for. However, I don't think this is appropriate in my situation, considering the ptstrName field is allowed to point to different types of things, depending on the value of TrusteeForm. So, I think I somehow need to convert from String to Pointer instead. I found the NativeString class in JNA, which would work, except it's a package-private class.

What's the recommended way to convert a Java String to a native format and obtain a Pointer to it? Am I even using the right data type for the TRUSTEE struct? I'm somewhat new to JNA, so please excuse me if I'm missing something obvious.

Update

I found a solution to my problem, but if anyone has a better solution I'd still like to hear it.

0

3 Answers 3

10

Assuming you want char * on the native side (you may need more memory allocated if the string contains non-ascii characters),

String myString = "CURRENT_USER";
Pointer m = new Memory(myString.length() + 1); // WARNING: assumes ascii-only string
m.setString(0, myString); 

You can then use m wherever you need to reference the "native" string.

For wide strings (wchar_t *),

String myString = "CURRENT_USER";
Pointer m = new Memory(Native.WCHAR_SIZE * (myString.length() + 1));
m.setWideString(0, myString);
Sign up to request clarification or add additional context in comments.

2 Comments

setString(offset, value) calls setString(offset, value, Native.getDefaultStringEncoding()). It seems unsafe to assume Native.getDefaultStringEncoding() always returns a format that uses only 1 byte per character, which is what you allocate?
Pointer m = new Memory(Native.WCHAR_SIZE * (myString.length() + 1); is missing a bracket, is this meant to be Pointer m = new Memory(Native.WCHAR_SIZE * (myString.length() + 1)); ?
3

I solved the problem by copying the source code for package-private NativeString class and creating a public copy in my project. I had to make one minor alteration due to the use of a package-private method in the constructor.

Update: As @fragorl notes in the comments, the implementation of NativeString shown below is by now quite out-of-date.


Usage:

private static TRUSTEE createTrusteeForCurrentUser() {
    TRUSTEE result = new TRUSTEE();
    result.TrusteeForm = TRUSTEE_FORM.TRUSTEE_IS_NAME;
    result.TrusteeType = TRUSTEE_TYPE.TRUSTEE_IS_USER;
    result.ptstrName = new NativeString("CURRENT_USER",true).getPointer();
    result.write();
    return result;
}

NativeString.java:

/** Provides a temporary allocation of an immutable C string 
 * (<code>const char*</code> or <code>const wchar_t*</code>) for use when 
 * converting a Java String into a native memory function argument.  
 *
 * @author  Todd Fast, [email protected]
 * @author [email protected]
 */
public class NativeString implements CharSequence, Comparable {

    private Pointer pointer;
    private boolean wide;

    /** Create a native string (NUL-terminated array of <code>char</code>).<p>
     * If the system property <code>jna.encoding</code> is set, its value will
     * be used to encode the native string.  If not set or if the encoding
     * is unavailable, the default platform encoding will be used. 
     */
    public NativeString(String string) {
        this(string, false);
    }

    /** Create a native string as a NUL-terminated array of <code>wchar_t</code>
     * (if <code>wide</code> is true) or <code>char</code>.<p>
     * If the system property <code>jna.encoding</code> is set, its value will
     * be used to encode the native <code>char</code>string.  
     * If not set or if the encoding is unavailable, the default platform 
     * encoding will be used. 
     * 
     * @param string value to write to native memory
     * @param wide whether to store the String as <code>wchar_t</code>
     */
    public NativeString(String string, boolean wide) {
        if (string == null) {
            throw new NullPointerException("String must not be null");
        }
        // Allocate the memory to hold the string.  Note, we have to
        // make this 1 element longer in order to accommodate the terminating 
        // NUL (which is generated in Pointer.setString()).
        this.wide = wide;
        if (wide) {
            int len = (string.length() + 1 ) * Native.WCHAR_SIZE;
            pointer = new Memory(len);
            pointer.setString(0, string, true);
        }
        else {
            byte[] data = Native.toByteArray(string);
            pointer = new Memory(data.length + 1);
            pointer.write(0, data, 0, data.length);
            pointer.setByte(data.length, (byte)0);
        }
    }

    public int hashCode() {
        return toString().hashCode();
    }

    public boolean equals(Object other) {

        if (other instanceof CharSequence) {
            return compareTo(other) == 0;
        }
        return false;
    }

    public String toString() {
        String s = wide ? "const wchar_t*" : "const char*";
        s += "(" + pointer.getString(0, wide) + ")";
        return s;
    }

    public Pointer getPointer() {
        return pointer;
    }

    public char charAt(int index) {
        return toString().charAt(index);
    }

    public int length() {
        return toString().length();
    }

    public CharSequence subSequence(int start, int end) {
        return CharBuffer.wrap(toString()).subSequence(start, end);
    }

    public int compareTo(Object other) {

        if (other == null)
            return 1;

        return toString().compareTo(other.toString());
    }
}

4 Comments

Thanks, this seems to be the "correct" way to do it. One question - why didn't you use the 1-arg NativeString constructor, instead of the 2-arg one?
@fragorl For my application I was using wide-character (Unicode) strings, so I needed to set the wide parameter to true. The 1-arg constructor sets it to false.
Ahh my bad, I was looking at the latest version of jna, where they changed the 1-arg constructor. It now reads: this(string, Native.getDefaultStringEncoding());. But you've got source code for an older version here - of course, your post is from 2012, woops ><
@fragorl - Good to know. I made a note in the answer.
-2

try using Pointer class in http://jna.java.net/javadoc/com/sun/jna/Pointer.html.

1 Comment

I know I need a Pointer, I guess the question is more how to convert a Java String into a native format and obtain a Pointer object to it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.