Win32 Python: getting user’s display name using ctypes

This post explains how you can obtain the user’s display name (a.k.a. “real name” or “full name”) in Windows, using Python’s ctypes module. However, it also serves as a mini tutorial/demonstration of ctypes.

First, a bit of background. I researched this while working on a patch for Jokosher. When you create a new project in Jokosher, it will prompt you with a dialog asking for the name of the project and so on. One of the fields in this dialog is the Author field, which by default should be filled with the logged-in user’s real name. While there are several ways to get the user’s login ID (a.k.a. “username”), there is no easy way to get their real name (display name) in Windows.

This is where ctypes and GetUserNameEx come in. ctypes is a Python library that lets you call C functions. GetUserNameEx is the C function in Win32 API that we want to call.

For the impatient, here is the full code. Continue reading if you want to know how it works and maybe learn a bit about ctypes. Otherwise, copy away. Note, however, that it does not have any error checking whatsoever.

import ctypes

def get_display_name():
	GetUserNameEx = ctypes.windll.secur32.GetUserNameExW
	NameDisplay = 3

	size = ctypes.pointer(ctypes.c_ulong(0))
	GetUserNameEx(NameDisplay, None, size)

	nameBuffer = ctypes.create_unicode_buffer(size.contents.value)
	GetUserNameEx(NameDisplay, nameBuffer, size)
	return nameBuffer.value

Explanation

If you look at the GetUserNameEx documentation, the Requirements section says “DLL: Secur32.dll”. This lets us know how to import the function using ctypes. In the Unicode and ANSI names row, you’ll notice that the Unicode version of the function is GetUserNameExW; this is in line with most of the Win32 API having A (ANSI) and W (wide character) versions.

>>> GetUserNameEx = ctypes.windll.secur32.GetUserNameExW

Now look at the declaration for GetUserNameEx. The function takes an EXTENDED_NAME_FORMAT, an LPTSTR (alias for TCHAR *, which is itself an alias for wchar_t * in Unicode environments), and a PULONG (alias for unsigned long *).

BOOLEAN WINAPI GetUserNameEx(
  __in     EXTENDED_NAME_FORMAT NameFormat,
  __out    LPTSTR lpNameBuffer,
  __inout  PULONG lpnSize
);

Let’s find out which EXTENDED_NAME_FORMAT we need.

typedef enum  {
  NameUnknown            = 0,
  NameFullyQualifiedDN   = 1,
  NameSamCompatible      = 2,
  NameDisplay            = 3,
  NameUniqueId           = 6,
  NameCanonical          = 7,
  NameUserPrincipal      = 8,
  NameCanonicalEx        = 9,
  NameServicePrincipal   = 10,
  NameDnsDomain          = 12
} EXTENDED_NAME_FORMAT, *PEXTENDED_NAME_FORMAT;

We want NameDisplay, which—according to the documentation—is “a ‘friendly’ display name (for example, Jeff Smith)”.

>>> NameDisplay = 3

Now it gets complicated. We need a name buffer of a certain size (in characters), but we don’t know yet the required size for this buffer. The ugly solution would be to create a buffer of some arbitrarily large size (32,767 characters is the maximum that GetUserNameEx accepts). We’re going to do it the elegant way, by calling GetUserNameEx twice; first to find out the required buffer size, second to fill the actual buffer.

>>> size = ctypes.pointer(ctypes.c_ulong(0))
>>> GetUserNameEx(NameDisplay, None, size)
0
>>> size.contents.value
18L

The first line creates the aforementioned PULONG, i.e. pointer to an unsigned long; in this case, it points to the value 0. The combination of the null (None) buffer and the 0 size indicates that we just want to grab the required buffer size. After the function call, the size variable is filled with this number (18 in my case: 17 for my name, 1 for the terminating NULL character).

Next, we create the name buffer with the required size and call GetUserNameEx with the correct arguments.

>>> nameBuffer = ctypes.create_unicode_buffer(size.contents.value)
>>> GetUserNameEx(NameDisplay, nameBuffer, size)
1
>>> nameBuffer.value
u'Johannes Sasongko'

And we’re done!

Advertisements

6 thoughts on “Win32 Python: getting user’s display name using ctypes

  1. Helen Dangote

    This is a nice tip. Although I have never had any need to get the user display name, I will bookmark this page in case the need arises 🙂

    Thanks for the code as well.

    Reply
  2. bill

    If the function succeeds, the return value is a nonzero value.

    If the function fails, the return value is zero. To get extended error information, call GetLastError. Possible values include the following.

    你的函数返回值是 0 ,调用失败!

    Reply
  3. Paul

    From the Microsoft documentation on GetUserNameEx():

    “If the user account is not in a domain, only NameSamCompatible is supported.”

    Instead of

    NameDisplay = 3

    I had to use

    NameDisplay = 2

    Reply
    1. Johannes Sasongko Post author

      Good catch. Odd, though, because I’m using a local account and 3 works fine. Could it be dependent on the Windows version? (I’m on Windows 7.)

      Reply
  4. Pingback: Win32 Python: Getting all window titles « Johannes Sasongko's blog

Note: By commenting, you grant me permission to freely republish your comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s