I saw a pinvoke signature that passed a UInt64 instead of a FILETIME, what's up with that?


A customer had a question about a pinvoke signature that used a UInt64 to hold a FILETIME structure.

[DllImport("kernel32.dll", SetLastError = true)
static external bool GetProcessTimes(
    IntPtr hProcess,
    out UInt64 creationTime,
    out UInt64 exitTime,
    out UInt64 kernelTime,
    out UInt64 userTime);

Is this legal? The documentation for FILETIME says

Do not cast a pointer to a FILETIME structure to either a ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows.

Are we guilty of this cast in the above code? After all you can't treat a FILETIME as an __int64.

There are two types of casts possible in this scenario.

  • Casting from FILETIME* to __int64*.

  • Casting from __int64* to FILETIME*.

The FILETIME structure requires 4-byte alignment, and the __int64 data type requires 8-byte alignment. Therefore the first cast is unsafe, because you are casting from a pointer with lax alignment requirements to one with stricter requirements. The second cast is safe because you are casting from a pointer with strict alignment requirements to one with laxer requirements.

4-byte aligned 8-byte aligned

Everything in the blue box is also in the pink box, but not vice versa.

Which cast is the one occurring in the above pinvoke signature?

In the above signature, the UInt64 is being allocated by the interop code, and therefore it is naturally aligned for UInt64, which means that it is 8-byte aligned. The Get­Process­Times function then treats those eight bytes as a FILETIME. So we are in the second case, where we cast from __int64* to FILETIME*.

Mind you, you can avoid all this worrying by simply declaring your pinvoke more accurately. The correct solution is to declare the last four parameters as ComTypes.FILETIME. Now there are no sneaky games. Everything is exactly what it says it is.

Bonus reading: The article Use PowerShell to access registry last-modified time stamp shows how to use the ComTypes.FILETIME technique from PowerShell.

Comments (12)
  1. Cornelius says:

    I suppose this isn't a big problem anymore now that no-one cares about Itanium?

    {I think people still care about x86, x64, and ARM, all of which will raise alignment exceptions under certain circumstances. And who knows what processors may come in the future. -Raymond]
  2. Medinoc says:

    What about using pointers but marking them as UNALIGNED?

    [That doesn't help when you want to call GetProcessTimes. -Raymond]
  3. Yukkuri says:

    So is this pInvoke week? Not that I am complaining :D

  4. jon says:

    I preferred it when this was "not actually a .net blog" :)

  5. Henri Hein says:

    @jon,

    For my two cents, marshalling issues are always interesting, regardless of the framework.

  6. @Raymond "x86, x64, and ARM, all of which will raise alignment exceptions under certain circumstances."

    Indeed so, but I'm not aware of a circumstance when one of those CPUs requires an 8-byte alignment.  Is there one?

    [I think it's a bad idea to write code based on the assumption that current processors are the only processors you will ever need to support. -Raymond]
  7. cheong00 says:

    @jon: I think the subtitle is gone when they switched to the current forum/blog software.

  8. Yuhong Bao says:

    I think for the ARM64 port can we consider just making FILETIME == LARGE_INTEGER or does these structures gets stored on disk?

    [It's certainly possible that people save FILETIMEs to disk (e.g., event log entries, or embedded inside a WIN32_FIND_DATA). It would also break a lot of source code. Everybody would have to have put #ifdef around anything that uses FILETIME.dwLowDateTime and FILETIME.dwHighDateTime. -Raymond]
  9. John says:

    @Richard T Russell

    To dovetail off of Raymond to write code based on such an assumption violates the 10th C Commandment (Reproduced from the Annotated edition below):

    10. Thou shalt foreswear, renounce, and abjure the vile heresy which claimeth that ``All the world's a VAX'', and have no commerce with the benighted heathens who cling to this barbarous belief, that the days of thy program may be long even though the days of thy current machine be short.

    This particular heresy bids fair to be replaced by ``All the world's a Sun'' or ``All the world's a 386'' (this latter being a particularly revolting invention of Satan), but the words apply to all such without limitation. Beware, in particular, of the subtle and terrible ``All the world's a 32-bit machine'', which is almost true today but shall cease to be so before thy resume grows too much longer.

  10. Myria says:

    This is also an endianness issue.  The only big-endian target using Windows-like headers that I know of is Xbox 360; on 360, I believe that FILETIME still had dwLowDateTime as the first member.  This makes it improper to cast a FILETIME like this as well, even in the manner shown.

  11. Yuhong Bao says:

    [It's certainly possible that people save FILETIMEs to disk (e.g., event log entries, or embedded inside a WIN32_FIND_DATA). It would also break a lot of source code. Everybody would have to have put #ifdef around anything that uses FILETIME.dwLowDateTime and FILETIME.dwHighDateTime. -Raymond]

    I am talking about only the alignment, obviously.

    [I guess I don't follow. If you change the alignment, then that changes the file format (because the padding changes). -Raymond]
  12. Yuhong Bao says:

    [I guess I don't follow. If you change the alignment, then that changes the file format (because the padding changes). -Raymond]

    I mean that I didn't intend it to break source code.

Comments are closed.

Skip to main content