Если вы хотите использовать глобальные идентификаторы (GUID) для идентификации ваших файлов, то никто вас не останавливает


Игорь Левицки предложил решение проблемы с расширениями файлов при помощи использования глобальных идентификаторов (GUID) для идентификации файлов вместо имен файлов.

Вам уже доступна такая возможность. Каждый файл тома NTFS имеет идентификатор объекта, который, формально, является 16-байтовым буфером, но, давайте, просто назовем его глобальным идентификатором (GUID). По умолчанию у файла нет идентификатора объекта, но вы можете отправить запрос на его создание при помощи операции FSCTL_CREATE_OR_GET_OBJECT_ID, которая возвращает существующий идентификатор объекта, ассоциированный с файлом, или создает новый идентификатор, если он еще не был создан. Если вы помешаны на тотальном контроле, вы можете использовать операцию FSCTL_SET_OBJECT_ID для явного указания глобального идентификатора, который вы хотите использовать в качестве идентификатора объекта. (Этот вызов вернет ошибку, если у файла уже есть идентификатор объекта). И, конечно же, есть операция FSCTL_GET_OBJECT_ID для получения идентификатора объекта, если он существует.

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <ole2.h>
#include <winioctl.h>

int __cdecl _tmain(int argc, PTSTR *argv)
{
  HANDLE h = CreateFile(argv[1], 0,
                  FILE_SHARE_READ | FILE_SHARE_WRITE |
                  FILE_SHARE_DELETE, NULL,
                  OPEN_EXISTING, 0, NULL);
  if (h != INVALID_HANDLE_VALUE) {
    FILE_OBJECTID_BUFFER buf;
    DWORD cbOut;
    if (DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID,
                  NULL, 0, &buf, sizeof(buf),
                  &cbOut, NULL)) {
      GUID guid;
      CopyMemory(&guid, &buf.ObjectId, sizeof(GUID));
      WCHAR szGuid[39];
      StringFromGUID2(guid, szGuid, 39);
      _tprintf(_T("Глобальный идентификатор: %ws\n"), szGuid);
    }
    CloseHandle(h);
  }
  return 0;
}

Эта программа принимает имя файла или директории в качестве единственного параметра и выводит на экран соответствующий идентификатор объекта.

Делов-то, теперь у нас есть глобальный идентификатор для каждого файла.

Второй частью задачи, конечно же, будет открытие файла по его глобальному идентификатору:

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <ole2.h>

int __cdecl _tmain(int argc, PTSTR *argv)
{
  HANDLE hRoot = CreateFile(_T("C:\\"), 0,
                  FILE_SHARE_READ | FILE_SHARE_WRITE |
                  FILE_SHARE_DELETE, NULL,
                  OPEN_EXISTING,
                  FILE_FLAG_BACKUP_SEMANTICS, NULL);
  if (hRoot != INVALID_HANDLE_VALUE) {
    FILE_ID_DESCRIPTOR desc;
    desc.dwSize = sizeof(desc);
    desc.Type = ObjectIdType;
    if (SUCCEEDED(CLSIDFromString(argv[1], &desc.ObjectId))) {
      HANDLE h = OpenFileById(hRoot, &desc, GENERIC_READ,
                  FILE_SHARE_READ | FILE_SHARE_WRITE |
                  FILE_SHARE_DELETE, NULL, 0);
      if (h != INVALID_HANDLE_VALUE) {
        BYTE b;
        DWORD cb;
        if (ReadFile(h, &b, 1, &cb, NULL)) {
          _tprintf(_T("Первый байт данного файла: 0x%02x\n"), b);
        }
        CloseHandle(h);
      }
    }
    CloseHandle(hRoot);
  }
  return 0;
}

Прежде чем открыть файл по его идентификатору, вам нужно открыть что-либо — неважно что — на том же томе, на котором расположен данный файл. Не имеет значения, что вы откроете: единственной причиной для получения этого дескриптора является указание функции OpenFileById того тома, который вы имеете в виду. В нашей небольшой тестовой программе мы используем диск C:, что означает, что поиск файла будет выполняться на диске C:.

Далее вы заполняете структуру FILE_ID_DESCRIPTOR, показывая, что вы хотите открыть файл по идентификатору его объекта, и теперь все готово для вызова функции OpenFileById. В качестве подтверждения мы считываем и выводим на экран первый байт открытого файла, который мы получили в результате выполнения функции.

Обратите внимание на то, что файл, который вы открываете по идентификатору его объекта, не обязан быть в текущей директории. Он может быть в любом месте диска C:. До тех пор, пока у вас есть глобальный идентификатор для этого файла, вы можете открывать его вне зависимости от того, в каком месте на диске он расположен.

Вы можете запускать эти две программы, просто чтобы насладиться трепетом открытия файлов по их глобальным идентификаторам. Обратите внимание, что после того, как вы получили глобальный идентификатор для файла, вы можете переместить его в любое место диска, и функция OpenFileById по-прежнему будет открывать его.

(И если вы хотите избавиться от этих утомительных букв дисков, вы можете использовать вместо них глобальные идентификаторы томов. Теперь каждый файл определяется парой глобальных идентификаторов: глобальным идентификатором тома и идентификатором объекта).

Таким образом, сказочная страна, в которой все файлы имеют свои глобальные идентификаторы, уже существует. Но почему же никто не пользуется этой утопией идентификации файлов по их глобальным идентификаторам?

Вероятно, вы уже знаете ответ: потому что люди предпочитают именовать вещи по какому-либо мнемоническому признаку, а не по глобальному идентификатору. Представьте себе диалог открытия файла в этой стране: «Введите глобальный идентификатор файла, который вы хотите открыть, или нажмите кнопку «Обзор», чтобы просмотреть глобальные идентификаторы всех файлов этого диска и выбрать нужный файл из списка». Сколько проживет такое диалоговое окно?

Начиная с сегодняшнего дня вам больше не нужно называть меня Рэймонд. Вы можете называть меня {7ecf65a0-4b78-5f9b-e77c-8770091c0100} или, для краткости, — «91c».

(И я полностью проигнорировал тот факт, что использование глобальных идентификаторов для идентификации файлов совсем не решает проблему выбора того, какой программой следует открывать тот или иной файл).

Дополнительная тема для беседы: вы также можете открывать файлы по их файловым идентификаторам, которые являются 64-битными значениями, специфичными в пределах одного тома. Но я выбрал использование глобальных идентификаторов, как в качестве дополнительной сложности, так и для того, чтобы просто показать Игорю, что его мир грез уже существует.


Skip to main content