среда, 10 марта 2010 г.

FILETIME и LARGE_INTEGER

Читая Рихтера «Windows для профессионалов» нашел ответ на вопрос, который некоторое время меня интересовал:

Зачем в делают поэлеметное копирование данных из одной 64-битной структуры (например, FILETIME) в LARGE_INTEGER, дабы потом передать его аргументом в WinAPi-функцию?

Например передача времени срабатывания объекту-таймеру:

 

SYSTEMTIME st;
st.wYear = 2002;
st.wMonth = 1;
st.wDayOfWeek = 0; // кстати, игнорируется
st.wDay = 1;
st.wHour = 13;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;

FILETIME ft;
SystemTimeToFileTime(&st, &ft);

LARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;

SetWaitableTimer(..., &li, ...);
Оказывается дело в выравнивании, цитирую Рихтера:
«Хотя двоичные форматы структур FILETIME и LARGE_INTEGER совпадают, выравнивание этих структур осуществляется по-разному. Адрес любой структуры FILETIME должен начинаться на 32-битной границе, а адрес любой структуры LARGE_INTEGER — на 64-битной. Вызов фукнции с передачей ей структуры FILETIME может сработать корректно, но может и не сработать — все зависит от того, попадет ли начало структуры FILETIME на 64-битную границу. В то же время компилятор гарантирует, что структура LARGE_INTEGER всегда будет начинаться на 64-битной границе...».

Кстати, о LARGE_INTEGER. MSDN описывает ее так:
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
} ;
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;
С неименованной структурой и LONGLONG все понятно, старые компиляторы могли не поддерживать 64-битные переменные и анонимная структура позволяла разделить ее на 2 половинки. Но зачем еще и именованная структура u? А ответ вообщем-то такой же: компиляторы могли не поддерживать неименованные структуры и для них можно было явно использовать u.