Dynamic-size arrays with ANYSIZE_ARRAY in Windows API

By | November 10, 2024

You can often find structure in the code that looks like this TOKEN_PRIVILEGES structure.

typedef struct _TOKEN_PRIVILEGES {
    DWORD PrivilegeCount;
    LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

The ANYSIZE_ARRAY macro is used in the definition of the TOKEN_PRIVILEGES structure to allow for a flexible array member. This is a common technique in C and C++ to define structures that can have a variable-length array as their last member.

In C and C++, you sometimes need to define a structure that can hold a variable number of elements in an array. However, the language does not directly support variable-length arrays within structures. To work around this, you can use a placeholder value like 1 (which is what ANYSIZE_ARRAY is defined as) to define the array, and then allocate the actual memory dynamically.

For structure example above

typedef struct _TOKEN_PRIVILEGES {
    DWORD PrivilegeCount;
    LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

Privileges is defined with ANYSIZE_ARRAY, which is 1. This means the structure definition includes space for at least one LUID_AND_ATTRIBUTES element.

When you need to create an instance of TOKEN_PRIVILEGES with more than one privilege, you allocate enough memory to hold the base structure plus the additional elements in the way like

    DWORD privilegeCount = 3; // some number of privileges
    size_t size = sizeof(TOKEN_PRIVILEGES) + (privilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES);
    PTOKEN_PRIVILEGES tokenPrivileges = (PTOKEN_PRIVILEGES)new(size);
    tokenPrivileges->PrivilegeCount = privilegeCount;
    

You can then access the elements of the Privileges array as if it were a regular array using point arithmetic:

for (DWORD i = 0; i < tokenPrivileges->PrivilegeCount; ++i) {
    // Access each privilege
    tokenPrivileges->Privileges[i].Luid = ...;
    tokenPrivileges->Privileges[i].Attributes = ...;
}

As you can see the first element (with index 0 that was initially declared in structure) is also available.

Why do we need to use ANYSIZE_ARRAY:

  • compatibility, using ANYSIZE_ARRAY ensures compatibility with older compilers and codebases that might not support flexible array members directly.
  • clarity, it makes it clear that the array is intended to be of variable length, even though the actual size is determined at runtime.

It should be also noticed that in standard C, flexible array members (introduced in C99) can be declared with an empty size (type array[]). However, C++ does not support C99 flexible arrays natively. Instead, C++ programmers often use dynamic memory management techniques (like std::vector or new[]), when ANYSIZE_ARRAY is a Windows-specific workaround rather than a portable solution.

I’d also recommend this article https://web.archive.org/web/20120209061713/http://blogs.msdn.com/b/oldnewthing/archive/2004/08/26/220873.aspx by Raymond Chan and his book https://www.google.ro/books/edition/The_Old_New_Thing/wYrCitbs5PQC?hl=en&gbpv=1&dq=old+new+things&printsec=frontcover for a better understanding of the motivation behind the scenes

Leave a Reply