The only supportable caller contract is to pass writable storage large enough for four UINT32 values, treat every field as valid only after TBS_SUCCESS, and accept the current layout only when the returned structVersion is the schema version the caller understands.
Initialization and version fields
Info is declared [out], and the function documentation says a TPM_DEVICE_INFO structure “is returned.” It does not document any caller initialization requirement for structVersion or another byte of the buffer.^1^
The structure reference’s statement that structVersion is a TBS context version that “must be set to TPM_VERSION_20” is inconsistent with:
- the getter’s output-only annotation;
- the field name and SDK comment;
- the separate
tpmVersionmember; - the protocol structure, where
StructVersionis a 4-byte schema version set to1.^2^ ^3^
The defensible interpretation is:
-
TPM_DEVICE_INFO.structVersion: returned device-information structure/schema version; currently observed/documented elsewhere as1. -
TPM_DEVICE_INFO.tpmVersion: returned TPM generation. The structure reference documentsTPM_VERSION_12orTPM_VERSION_20.^2^ -
TBS_CONTEXT_PARAMS[2].version: caller-supplied context-input version forTbsi_Context_Create; this is where settingTPM_VERSION_20is actually documented.^4^
No published Win32 contract explicitly promises that the implementation never reads the initial bytes. Zero-initializing remains prudent, but it must not be represented as a required version negotiation mechanism.
Layout and forward compatibility
The published C declaration contains four consecutive UINT32 members, and the getter requires space for four 32-bit values—16 bytes for the currently published schema.^1^ ^2^
That declaration is architecture-neutral because its members have fixed widths and contain no pointers. However, the available documentation does not publish:
- a packing directive or formal cross-version ABI table;
- schema values other than
1; - a mapping from future schema values to larger layouts;
- rules permitting interpretation of a known prefix when
structVersion > 1; - an explicit guarantee covering every historical or future
Tbs.dll.
Consequently, compile against the target SDK, pass sizeof(TPM_DEVICE_INFO), and reject an unrecognized structVersion rather than assuming prefix compatibility:
TPM_DEVICE_INFO info{}; // Defensive, not documented as required input.
TBS_RESULT rc = Tbsi_GetDeviceInfo(
static_cast<UINT32>(sizeof(info)), &info);
if (rc != TBS_SUCCESS)
return rc; // Do not inspect info.
if (info.structVersion != 1)
return ERROR_REVISION_MISMATCH;
// Interpret documented fields here.
Reserved and unknown values
For the Win32 API, tpmInterfaceType and tpmImpRevision are documented only as Reserved.^2^ Therefore:
- do not require them to be zero;
- do not branch security behavior on them;
- preserve tolerance for any returned value.
MS-HGSA assigns interface meanings and describes the implementation revision, but that protocol definition does not, by itself, extend the Tbs.dll API contract.^3^
Likewise, the protocol defines TPM version values 0 (unknown), 1 (1.2), and 2 (2.0), whereas the Win32 structure page promises only 1.2 or 2.0.^5^ An API caller should recognize the documented Win32 values and treat anything else as unsupported/unknown, not infer a future TPM generation.
No fields are documented as valid after failure. Ignore the entire buffer unless the return value is TBS_SUCCESS.
Buffer and error behavior
“Large enough to hold four 32-bit values” specifies a minimum of 16 writable bytes, not an exact-size requirement.^1^ The page does not explicitly guarantee acceptance of larger sizes, so sizeof(TPM_DEVICE_INFO) is the portable choice.
The API has no required-size or bytes-written parameter; no size-discovery or partial-output protocol is documented. General TBS return codes include bad parameter, invalid output pointer, insufficient buffer, and buffer too large, but the getter page specifically lists only success, bad parameter, and TPM-not-found.^6^ Do not depend on a particular error for undersized, oversized, or inaccessible memory, or on any partial write.
Service, hardware access, and waiting
No public Tbsi_GetDeviceInfo contract states whether its data is cached, whether the call starts TBS or another dependency, loads a driver, touches or powers the TPM, or submits a TPM command. TBS itself is an out-of-process service reached through RPC and manages TPM access across power states, but that architecture does not establish this getter’s implementation path.^7^
Similarly, the getter documents no maximum completion time, timeout parameter, asynchronous form, or cancellation mechanism. Tbsip_Cancel_Commands requires a previously created context and cancels outstanding commands for that context, so it does not provide a documented way to interrupt this context-free getter.^8^ The 90-second hardware-command requirement must not be projected onto this API.
A documentation correction can safely change structVersion to “Structure version; returned as 1” and remove the context-input wording. The remaining lifecycle, buffer-edge, failure-write, caching, I/O, and timing guarantees require an explicit Microsoft API-contract update; they cannot be derived from the currently published references.
References