Call to Advapi32Util.getCurrentUserGroups() takes about 5 seconds to complete...

60 views
Skip to first unread message

Nick R

unread,
Aug 25, 2021, 8:35:20 PM8/25/21
to Java Native Access
Hi!

I have to do maintenance on an application that uses JNA and I noticed that a call to
Advapi32Util.getCurrentUserGroups() is abnormally long, it take about 5 seconds to complete.

I doubt it is supposed to take this long so what am I doing wrong?

I thought that maybe that 5 seconds would only happen once when things are getting initialized or something similar but it seems to consistently take that much time.

How can I fix this, assuming this is fixable?

The application uses an older version of JNA but I tried with the latest and I have the same problem.

Thank you!

Nick

Matthias Bläsing

unread,
Aug 26, 2021, 12:57:55 PM8/26/21
to jna-...@googlegroups.com
Hi,
I would check with a profiler where the time is spend. If it is in the native methods (have a look at what getCurrentUserGroups does under the hood), you will need to turn to your windows admins.

Without having seen your setup, I guess you are on a domain setup with a WAN link somewhere and looking the implementation of said method, a lot of lookups are done there. With a big number of groups and if only a fraction of these calls go over the network, there is your answer.

Maybe that helps as a pointer.

Greetings

Matthias

Daniel B. Widdis

unread,
Aug 26, 2021, 5:03:54 PM8/26/21
to Java Native Access
I had implemented similar/identical code to Advapi32Util.getCurrentUserGroups() in my own project and found that while it was acceptably slow on my home PC, it was painfully slow on my work PC with a larger address book, active directory, etc.  

I didn't go much deeper into troubleshooting, but the simple fact is that the underlying single slow native call in that util is this one:

Advapi32.INSTANCE.GetTokenInformation(hToken, WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, null, 0, tokenInformationLength)

I eventually decided I didn't want all the groups anyway (at least most of the time), I just wanted the primary group:

Advapi32Util.getTokenPrimaryGroup(hToken);

It's much faster. 


The current Advapi32Util#getTokenGroups method can incur significant latency, 20-45 seconds on my corporate machine. Only asking for the primary group is on the order of 20-45 milliseconds.

I don't think it's fixable, really.  I think asking for all the groups on a large AAD-served mail system is going to be slow, period.


--
You received this message because you are subscribed to the Google Groups "Java Native Access" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jna-users/8e610a60086d800f8a302b9f96f27e1be48d3c71.camel%40doppel-helix.eu.


--
Dan Widdis

Nick R

unread,
Aug 26, 2021, 9:23:22 PM8/26/21
to Java Native Access
Hi!

You both made me realize that the problem is because my VPN link was up and that it is apparently querying my employer's AD servers. Things go faster when my VPN access is not up...
My Internet access is also quite slow...

Thing is the only reason why I am querying this is to see if the user is a local administrator before executing a program which needs administrator access.

I am essentially using something like this bit of code


or pretty close to this...

Is there another way to test if the user is an administrator which would not involve using Advapi32Util.getCurrentUserGroups()?

I saw a bit of code which used com.sun packages but IIRC code in com.sun must never be used as it can disappear/be modified at any time... Chances are it might no longer exist in recent Java versions anyway...

Thank you and have a nice day!

Nick

Daniel Widdis

unread,
Aug 27, 2021, 12:38:18 AM8/27/21
to jna-...@googlegroups.com

You might consider IsUserAnAdmin() in Shell32, although that suggests calling CheckTokenMembership() instead. 

 

Here’s how I did it, essentially checking the current process for the TOKENELEVATION token.  However, I think this only checks if the current process is running elevated, not if the user has the ability to elevate – does that fit your use case?

 

public boolean isElevated() {

    HANDLEByReference hToken = new HANDLEByReference();

    boolean success = Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), WinNT.TOKEN_QUERY,

            hToken);

    if (!success) {

        LOG.error("OpenProcessToken failed. Error: {}", Native.getLastError());

        return false;

    }

    try {

        TOKEN_ELEVATION elevation = new TOKEN_ELEVATION();

        if (Advapi32.INSTANCE.GetTokenInformation(hToken.getValue(), TOKENELEVATION, elevation, elevation.size(),

                new IntByReference())) {

            return elevation.TokenIsElevated > 0;

        }

    } finally {

        Kernel32.INSTANCE.CloseHandle(hToken.getValue());

    }

    return false;

}

 

Where

private static final int TOKENELEVATION = 0x14;

 

and

 

@FieldOrder({ "TokenIsElevated" })

class TOKEN_ELEVATION extends Structure {

    public int TokenIsElevated;

Nick R

unread,
Aug 27, 2021, 10:39:05 AM8/27/21
to Java Native Access
Hi!

I test if the user is an admin to know if he is able to install a program before attempting to call the install program...

Is it enough to test if the process is elevated or not, I am not sure...

Thank you and have a nice day!

Nick

Tres Finocchiaro

unread,
Aug 27, 2021, 12:16:19 PM8/27/21
to jna-...@googlegroups.com
Unrelated, really, but to test if elevated, there's an old trick that's worked quite well... 

Just use a Runtime.exe() call to "net", "session" and snag the return code.  Reference: https://stackoverflow.com/a/11995662/3196753


Daniel B. Widdis

unread,
Aug 27, 2021, 1:04:40 PM8/27/21
to Java Native Access
I really like the solution with super-large ERROR printed with ## characters.

Speaking of SO, this answer has a few different options.  The first one matches the Advapi32Util one at the top of this thread.  How to check whether a Windows user has admin privileges in C? - Stack Overflow

The second one uses NetUserGetInfo.  The USER_INFO1 structure has a usri1_priv field.

I think I see the inspiration for the code I posted earlier in one of the other answers.




--
Dan Widdis

Tres Finocchiaro

unread,
Aug 27, 2021, 1:31:23 PM8/27/21
to jna-...@googlegroups.com
I really like the solution with super-large ERROR printed with ## characters.

Sorry, slightly off-topic to the OP's question, but regarding the ASCII art in one of the S.O. "net", "session" examples, I've usually switched the command prompt to bright red and done something similarly obnoxious:  When techs are busy servicing machines, it's often the ugly errors that catch the eye and avoid visiting the machine again. 😅

Reply all
Reply to author
Forward
0 new messages