.NET big-endian или little-endian?
От: Shadedsun  
Дата: 24.06.08 14:54
Оценка:
В WinAPI есть такие функции как FindFirstFile и FindNextFile, которые используют структуру WIN32_FIND_DATA, для .NET, Микрософт определяет её в виде класса:
[Serializable, StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto), BestFitMapping(false)]
internal class WIN32_FIND_DATA
{
    internal int dwFileAttributes;
    internal int ftCreationTime_dwLowDateTime;
    internal int ftCreationTime_dwHighDateTime;
    internal int ftLastAccessTime_dwLowDateTime;
    internal int ftLastAccessTime_dwHighDateTime;
    internal int ftLastWriteTime_dwLowDateTime;
    internal int ftLastWriteTime_dwHighDateTime;
    internal int nFileSizeHigh;
    internal int nFileSizeLow;
    internal int dwReserved0;
    internal int dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
    internal string cFileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)]
    internal string cAlternateFileName;
    public WIN32_FIND_DATA();
}

Я его чуть переделал, и расчета того что ftCreationTime_dwLowDateTime и ftCreationTime_dwHighDateTime располагаются как раз в таком порядке, что если их накрыть long-полем, то оно получит валидное значение:
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
[BestFitMapping(false)]
class WIN32_FIND_DATA
{
    public int dwFileAttributes;
    //public int ftCreationTime_dwLowDateTime;
    //public int ftCreationTime_dwHighDateTime;
    public long CreationTime;
    public int ftLastAccessTime_dwLowDateTime;
    public int ftLastAccessTime_dwHighDateTime;
    public int ftLastWriteTime_dwLowDateTime;
    public int ftLastWriteTime_dwHighDateTime;
    public int nFileSizeHigh;
    public int nFileSizeLow;
           int Reserved0;
           int Reserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string FileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
    public string AlternateFileName;
}

но не тут то было, DateTime.FromFileTime() вылетает с руганью на неверное значение параметра. Тогда я поменял в long-поле первое слово и второе местами:
fileData.CreationTime = (long)(((ulong)fileData.CreationTime >> 32) | ((ulong)fileData.CreationTime << 32));

и, о чудо, оно зароботало. Но появилась другая проблема. FileName сдвинулось на одно слово (4 байта) так, как будто я добавил лишнее intовое поле (пример: fileData.FileName вместо "Yahoo!" теперь содержит "hoo!").
Вопрос не жизненно важный, я проблему решил оставив структуру в прежнем виде
public static DateTime FileTimeToDateTime(int highWord, int lowWord)
{
    return DateTime.FromFileTime((((long)highWord) << 32) + lowWord);
}

Интересно почему при замене двух Int32 на одно Int64 поля сдвинулись, и почему Int64 ведет себя так, как будто слова в нем располагаются в big-endian порядке т.е. сначала старшее слово потом младшее, а не так как принято на x86 платформе?
.net big-endian little-endian
Re: .NET big-endian или little-endian?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 24.06.08 15:06
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>Микрософт определяет её в виде класса:


Microsoft не имеет отношения к определению выше. Смотри Си хедеры и всё встанет на свои места.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: .NET big-endian или little-endian?
От: Shadedsun  
Дата: 24.06.08 20:20
Оценка:
Здравствуйте, adontz, Вы писали:
A>Microsoft не имеет отношения к определению выше. Смотри Си хедеры и всё встанет на свои места.

Вы видели первый вариант класса? Я его выдернул рефлектором, и то как у них там все устроено — все работает, второй вариант — это подправленный мной, по моему мнению должен был работать тоже, но нет. К тому же я получил неожиданный результат и задал вопрос: почему так?
Хорошо, смотрим Си хедеры:
//WinBase.h
typedef struct _WIN32_FIND_DATAW {
    DWORD dwFileAttributes;
    FILETIME ftCreationTime;
    FILETIME ftLastAccessTime;
    FILETIME ftLastWriteTime;
    DWORD nFileSizeHigh;
    DWORD nFileSizeLow;
    DWORD dwReserved0;
    DWORD dwReserved1;
    WCHAR  cFileName[ MAX_PATH ];
    WCHAR  cAlternateFileName[ 14 ];
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;

//WinDef.h
typedef struct _FILETIME {
    DWORD dwLowDateTime;
    DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

И что мы видим? — Тоже самое. Как это связанно с моим вопросом? — IMHO никак. Так о чем был коментарий?
Re[3]: .NET big-endian или little-endian?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 24.06.08 20:50
Оценка:
Здравствуйте, Shadedsun, Вы писали:


S>Так о чем был коментарий?


О том что FILETIME сруктура из двух 32битных полей, а не одно 64битное и порядов этих 32битных полей фиксирован и абсолютно никак не связан со способом представления 64битного числа. То что кому-то вдруг захотелось вместо FILETIME использовать long это его личная пробелма и замена не является корректной.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: .NET big-endian или little-endian?
От: Shadedsun  
Дата: 25.06.08 14:42
Оценка:
Здравствуйте, adontz, Вы писали:
A>О том что FILETIME сруктура из двух 32битных полей, а не одно 64битное и порядов этих 32битных полей фиксирован и абсолютно никак не связан со способом представления 64битного числа. То что кому-то вдруг захотелось вместо FILETIME использовать long это его личная пробелма и замена не является корректной.

Так вот это я и пытаюсь понять, почему некорректна? Перое 32битное поле является младшей частью, второе — старшей, 4байта+4байта=8байт=64бит размер подходит, расположение тоже, так в чем проблема? У long'a на x86 архитектуре сначала идут младшие 32 бит замем старшие 32 бит, и под .NET long имеет размер 64 бита.
Re[5]: .NET big-endian или little-endian?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 25.06.08 16:13
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>Так вот это я и пытаюсь понять, почему некорректна? Перое 32битное поле является младшей частью, второе — старшей, 4байта+4байта=8байт=64бит размер подходит, расположение тоже, так в чем проблема? У long'a на x86 архитектуре сначала идут младшие 32 бит замем старшие 32 бит, и под .NET long имеет размер 64 бита.


По определению

http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA_%D0%B1%D0%B0%D0%B9%D1%82%D0%BE%D0%B2#.D0.9F.D0.BE.D1.80.D1.8F.D0.B4.D0.BE.D0.BA_.D0.BE.D1.82_.D0.BC.D0.BB.D0.B0.D0.B4.D1.88.D0.B5.D0.B3.D0.BE_.D0.BA_.D1.81.D1.82.D0.B0.D1.80.D1.88.D0.B5.D0.BC.D1.83

64bit little endian : 0x0807060504030201
32bit little endian : 0x04030201
FILETIME:           : 0x04030201, 0x08070605
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[6]: .NET big-endian или little-endian?
От: Shadedsun  
Дата: 25.06.08 19:13
Оценка:
A>http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA_%D0%B1%D0%B0%D0%B9%D1%82%D0%BE%D0%B2#.D0.9F.D0.BE.D1.80.D1.8F.D0.B4.D0.BE.D0.BA_.D0.BE.D1.82_.D0.BC.D0.BB.D0.B0.D0.B4.D1.88.D0.B5.D0.B3.D0.BE_.D0.BA_.D1.81.D1.82.D0.B0.D1.80.D1.88.D0.B5.D0.BC.D1.83

Я тож читал эту статью.
Создаем чистый консольный проект, вставляем, запускаем, смотрим.
#include "stdafx.h"
#include <Windows.h>

typedef struct
{
    DWORD dwFileAttributes;
    __int64 ftCreationTime;
    __int64 ftLastAccessTime;
    __int64 ftLastWriteTime;
    DWORD nFileSizeHigh;
    DWORD nFileSizeLow;
    DWORD dwReserved0;
    DWORD dwReserved1;
    WCHAR  cFileName[ MAX_PATH ];
    WCHAR  cAlternateFileName[ 14 ];
} MY_WIN32_FIND_DATA;

int _tmain(int argc, _TCHAR* argv[])
{
    SYSTEMTIME sysTime;
    WIN32_FIND_DATA findData;
    MY_WIN32_FIND_DATA myFindData;
    HANDLE hFind;
    
    //Работаем с нормальной структурой
    hFind = FindFirstFile(L"c:\\windows\\*", &findData);
    FindClose(hFind);
    
    FileTimeToSystemTime(&findData.ftLastWriteTime, &sysTime);
    wprintf(L"%2d-%2d-%4d\r\n", sysTime.wDay, sysTime.wMonth, sysTime.wYear);
    
    //Работаем с переделанной структурой
    hFind = FindFirstFile(L"c:\\windows\\*", (WIN32_FIND_DATA*)&myFindData);
    FindClose(hFind);

    FileTimeToSystemTime((FILETIME*)&findData.ftLastWriteTime, &sysTime);
    wprintf(L"%2d-%2d-%4d\r\n", sysTime.wDay, sysTime.wMonth, sysTime.wYear);

    getchar();
    return 0;
}

Или еще проще:

int wmain(int argc, wchar_t* argv[])
{    
    union
    {
        FILETIME filetime;
        __int64 int64;
    } myUnion;
    
    myUnion.filetime.dwLowDateTime=0;
    myUnion.filetime.dwHighDateTime=0;
    myUnion.int64 = 1;
    //Куда попадет единица? Если так как говорю я то в myUnion.filetime.dwLowDateTime.
    printf("low = %d, high = %d\r\n", myUnion.filetime.dwLowDateTime, myUnion.filetime.dwHighDateTime);
    //Первое int32 соответствует младшей части int64
    myUnion.filetime.dwLowDateTime=666;
    printf("int64 = %I64d\r\n", myUnion.int64);    
    //Второе int32 соответствует старшей части int64
    myUnion.filetime.dwLowDateTime=0;
    myUnion.filetime.dwHighDateTime=666;
    // 666*4294967296=2860448219136
    printf("int64 = %I64d\r\n", myUnion.int64);    
    getchar();
    return 0;
}
Re: .NET big-endian или little-endian?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 25.06.08 21:01
Оценка:
Здравствуйте, Shadedsun, Вы писали:

Я гоняю. Надо в StructLayout указать Pack.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: .NET big-endian или little-endian?
От: Pzz Россия https://github.com/alexpevzner
Дата: 25.06.08 22:24
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>Интересно почему при замене двух Int32 на одно Int64 поля сдвинулись, и почему Int64 ведет себя так, как будто слова в нем располагаются в big-endian порядке т.е. сначала старшее слово потом младшее, а не так как принято на x86 платформе?


Я думаю, когда вы попытались накрыть ftCreationTime_dwLowDateTime и ftCreationTime_dwHighDateTime одним 64-битным интом, этот инт из-за выравнивания сдвинулся еще на 32 бита, и накрыл собой ftLastAccessTime_dwHighDateTime и ftLastWriteTime_dwLowDateTime. Результат выглядел как валидное время с перепутанными младшей и старшей половинками, но на самом деле младшая и старшая половинки были взяты из 2-х разных времен. Это же соображение объясняет и тот факт, что FileName тоже сдвинулось на 4 байта (т.е., 32 бита).
Re[2]: .NET big-endian или little-endian?
От: Shadedsun  
Дата: 25.06.08 22:52
Оценка:
Здравствуйте, Pzz, Вы писали:
Pzz>Я думаю, когда вы попытались накрыть ftCreationTime_dwLowDateTime и ftCreationTime_dwHighDateTime одним 64-битным интом, этот инт из-за выравнивания сдвинулся еще на 32 бита, и накрыл собой ftLastAccessTime_dwHighDateTime и ftLastWriteTime_dwLowDateTime. Результат выглядел как валидное время с перепутанными младшей и старшей половинками, но на самом деле младшая и старшая половинки были взяты из 2-х разных времен. Это же соображение объясняет и тот факт, что FileName тоже сдвинулось на 4 байта (т.е., 32 бита).

Браво, в точку. [StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Unicode)] и все стало на свои места.
Умница Pzz.
Re[2]: .NET big-endian или little-endian?
От: Shadedsun  
Дата: 25.06.08 23:06
Оценка:
A>Я гоняю. Надо в StructLayout указать Pack.

Спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.