Сообщений 3    Оценка 100        Оценить  
Система Orphus

Использование Windows Imaging API (WIMGAPI)

Автор: Ключевский Андрей Богданович
Перевод: Ключевский Андрей Богданович
Источники: RSDN Magazine #4-2010
Блог Software Know How

Материал предоставил: Ключевский Андрей Богданович
Опубликовано: 06.02.2011
Исправлено: 10.12.2016
Версия текста: 1.1
Введение в работу с образами и WIMGAPI
Формат файла Windows Imaging (WIM)
Работа с WIMGAPI
1. Захват образа диска или каталога в файл WIM.
2. Применение образа из файла WIM к диску или каталогу.
3. Удаление образа из файла WIM.
Замечания касательно демонстрационного кода.
Заключение
Список литературы

Демонстрационный код

Введение в работу с образами и WIMGAPI

Все OC Windows, начиная с Windows Vista, распространяются в виде .WIM файлов, новом формате файлов образов. Его преимущества:

WIMGAPI – это интерфейс прикладного программирования (API) Windows Imaging, который разработчики могут использовать для работы с файлами WIM. API предоставляет всю функциональность для работы с образами: основная цель Imaging API (Wimgapi.dll) состоит в том, чтобы программно захватывать, изменять, и применять образы для развертывания в производстве или корпоративной ИТ-среде. Фактически, инструмент ImageX - интерфейс командной строки для WIMGAPI. WIMGAPI введен начиная с Windows Vista и выше.

Возможности WIMGAPI безграничны. Например, разработчик может использовать функции API, чтобы создать новый файл WIM и захватить образ в него. Код разработчика может смонтировать файл WIM как папку, обновить ее содержимое, и размонтировать файл образа. Наконец, код разработчика может подготовить жесткий диск компьютера, создавая разделы и форматируя их, а потом применить файл образа к диску. API даже предоставляет сообщения обратного вызова (callback сообщения), которые позволяют коду разработчика показывать прогресс и уведомлять пользователя об ошибках.

WIMGAPI позволяет независимым поставщикам программного обеспечения и производителям комплектного оборудования разрабатывать для третьих сторон продукты развертывания и обслуживания образов для удовлетворения практически любых потребностей. Это также позволяет ИТ-разработчикам более легко создавать частные решения для работы с образами.

Формат файла Windows Imaging (WIM)

WIM – это файловый формат образа диска, который был введен в Windows Vista. Файлы WIM - сжатые пакеты, которые содержат много связанных файлов. Формат файла WIM оптимизирован для максимального сжатия (используя LZX), для быстрого сжатия (используя XPRESS), или может быть несжатым.

Структура файла WIM

Структура файла WIM содержит до шести типов ресурсов: заголовок, ресурсы файла, ресурс метаданных, справочную таблицу, данные XML и таблицу целостности. Иллюстрация ниже показывает общее описание файла WIM, который содержит два изображения.


Описание файла WIM

Работа с WIMGAPI

API библиотека для Windows Imaging состоит из следующих файлов:

По умолчанию, эти файлы установлены в %ProgramFiles%\<Kit_Name>\SDKs\WIMGAPI, где <Kit_Name> -Windows OPK или Windows AIK. Windows AIK (последний - для Windows 7) может быть загружен отсюда: http://www.microsoft.com/downloads/details.aspx?familyid=696DD665-9F76-4177-A811-39C26D3B3B34&displaylang=ru. Вы можете также найти последнюю документацию касательно WIMGAPI в AIK.

Я продемонстрирую, как использовать WIMGAPI на C++. C++ был выбран с тем, чтобы позволить приложению работать как в полной версии Windows ОС, так и в Windows PE. Windows PE не содержит .NET, однако если Ваше приложение разрабатывается только для полной версии Windows, то WIMGAPI может быть использовано из C# с помощью PInvoke/DLLImport. Демонстрационное приложение может быть собрано как для x86, так и для x64/AMD64 архитектур. Три операции WIMGAPI будут раскрыты в демонстрационном примере:

  1. Захват образа диска или каталога в файл WIM.
  2. Применение образа из файла WIM к диску или каталогу.
  3. Удаление изображение из файла WIM.

Чтобы обработать ошибку/сообщение о состоянии, мы должны реализовать функцию обратного вызова (Callback-функцию) следующим образом:

      //
      // Callback-функция (для обработки ошибок)
      //
      // in:      dwMsgId             идентификатор сообщения
      //          wParam1             имя файла (в большинстве случаев)
      //          lParam2             код ошибки (в большинстве случаев)
      //          pUnused             не используется
      //
      // returns: для указания успешного выполнения и разрешения другим подписчикам обработать
      // сообщение возвратите WIM_MSG_SUCCESS. Для воспрепятствования тому, чтобы другие
      // подписчики получили сообщение, возвратите WIM_MSG_DONE. Чтобы отменить захват или
      // применения образа, возвратите WIM_MSG_ABORT_IMAGE во время обработки сообщения
      // WIM_MSG_PROCESS. (В нашем случае мы только обрабатываем сообщения и возвращаем SUCCESS
      // (успешное выполнение))
      //
DWORD WINAPI WIMCallback(__in DWORD dwMsgId,
                         __in WPARAM wParam1,
                         __inout LPARAM lParam2,
                         __in void *pUnused)
{
    ...

    //    // Второй параметр: полный путь к файлу для WIM_MSG_PROCESS, или строка сообщения для    // других идентификаторов сообщений    //
    TCHAR *pszMessage = (TCHAR *) wParam1;
    TCHAR *pszFilePath = (TCHAR *) wParam1;
    DWORD percent = (DWORD) wParam1;

    //    // Третий параметр: сообщение назад к вызывающей функции для WIM_MSG_PROCESS, или код     // ошибки для других идентификаторов сообщений    //
    DWORD dwErrorCode = (DWORD) lParam2;
    DWORD *pdwMsgBack = (DWORD *) lParam2;

    switch (dwMsgId)
    {
        case WIM_MSG_PROCESS:
            //            // Это сообщение послано для каждого используемого файла, чтобы видеть будет            // ли этот файл обработан или нет. Если файл не надо обрабатывать,             // то установите pdwMsgBack в FALSE и все равно возвратите WIM_MSG_SUCCESS.            // По умолчанию pdwMsgBack установлен в TRUE.            //            //            // Распечатываем путь к используемому файлу            //
            ...
            break;

        case WIM_MSG_ERROR:
            //            // Это сообщение посылается в случае ошибки            //
            ...
            break;

        case WIM_MSG_RETRY:
            //            // Это сообщение посылают, когда файл повторно обрабатывается из-за            // обрыва сети. Повторная попытка производится до 5 раз.            //
            ...
            break;

        case WIM_MSG_INFO:
            //            // Это сообщение посылают, когда доступно информационное сообщение            //
            ...
            break;

        case WIM_MSG_WARNING:
            //            // Это сообщение посылают, когда доступно предупреждающее сообщение            //
            ...
            break;
    }

    ...

    //    // возвратить SUCCESS    //return WIM_MSG_SUCCESS;
}

Функция обратного вызова должна быть зарегистрирована каждый раз перед началом работы с WIMGAPI:

FARPROC Callback = (FARPROC) WIMCallback;
...
//// Зарегистрировать функцию обратного вызова (callback-функцию)//if (WIMRegisterMessageCallback(NULL, Callback, NULL) == INVALID_CALLBACK_VALUE)
{
    //    // Ошибка регистрации функции обратного вызова (callback-функции)    //
    ...
}

В конце работы с WIMGAPI производим очистку. Я реализовал очистку следующим образом:

      //
      // Функция очистки
      //
      // in:      hWimFile                дескриптор файла WIM
      //          hImage                  дескриптор образа
      //          Callback                функция обратного вызова (Callback функция)
      void WIMCleanup(__in HANDLE hWimFile, __in HANDLE hImage, __in FARPROC Callback)
{
    if (hImage)
    {
        //        // закрыть дескриптор образа        //
        WIMCloseHandle(hImage);
    }
    if (hWimFile) 
    {
        //        // закрыть дескриптор WIM файла        //
        WIMCloseHandle(hWimFile);
    }

    if (Callback) 
    {
        //        // Отменить регистрацию функции обратного вызова (Callback функции)        //
        WIMUnregisterMessageCallback(NULL, Callback);
    }
}

1. Захват образа диска или каталога в файл WIM.

Захват образа поддерживает два случая: создание нового файла WIM с образом определенного диска/каталога или добавления образа к уже существующему файлу. В первом случае, если файл WIM уже будет существовать, то он будет переписан. Во втором случае образ будет добавлен к существующему файлу и получит новый автоматически назначенный индекс образа.

Последовательность API вызовов для захвата образа нижеследующая:

  1. f = WIMCreateFile() – создание нового/открытие существующего файла WIM
  2. WIMSetTemporaryPath() – установка временного пути для операций с образами (опционально для захвата)
  3. i = WIMCaptureImage() – захват образа
  4. WIMCloseHandle(i) – закрытие дескриптора образа
  5. WIMCloseHandle(f) – закрытие дескриптора файла WIM

Рассмотрим реализацию этой последовательности в коде.

1. Создание нового/открытие существующего файла WIM (флаг для добавления образа устанавливается вызывающей функцией)

LPCTSTR pszWimFileName = ...; // целевое имя файла WIM/путь для захвата образа
BOOL fAppend = ...; // TRUE, если надо добавить образ к существующему файлу WIM,// FALSE - если создать новый/перезаписать существующий файл
...
//// дескриптор файла WIMs//
HANDLE hWimFile = NULL;
DWORD dwFlag = 0, dwAccess = 0;
DWORD dwCreationResult = 0;
...
//// устанавливаем режим доступа и флаг открытия файла WIM//if (fAppend == FALSE)
{
        //        // для создания нового/перезаписи существующего файла        //dwFlag = WIM_CREATE_ALWAYS;
dwAccess = WIM_GENERIC_WRITE;
}
else 
{
        //        // для добавления образа в существующий файл        //dwFlag = WIM_OPEN_EXISTING;
dwAccess = WIM_GENERIC_WRITE | WIM_GENERIC_READ;
}
//// Создать/открыть файл//
hWimFile = WIMCreateFile(pszWimFileName,      // существующий файл WIM для создания/добавленияdwAccess,            // режим доступаdwFlag,              // флаг открытия0,                   // не проверять файл на наличие поврежденийWIM_COMPRESS_XPRESS, // либо WIM_COMPRESS_LZX, либо WIM_COMPRESS_NONE&dwCreationResult);

if (hWimFile == NULL)
{
        //        // Ошибка открытия файла WIM        //...
}

2. Установка временного пути для операций с образами (опционально для захвата)

        //
        // Установить временный путь для работы с образами (опционально, но рекомендуется
        // для больших файлов WIM)
        //
TCHAR szTempDir[MAX_PATH] = ...;
...
WIMSetTemporaryPath(hWimFile, szTempDir); // ошибки установки временного пути допустимы в этом случае

3. Захват образа

LPCTSTR pszCaptureDir = ...; // диск/каталог для захвата
...
//// дескриптор образа//
HANDLE hImage = NULL;

...
//// Захват образа//
hImage = WIMCaptureImage(hWimFile,
                         pszCaptureDir,   // имя захватываемого диска/каталога
                         WIM_FLAG_VERIFY);// рекомендуемый флаг для проверки целостности файла,                                          // 0 – если проверка не нужнаif (hImage == NULL)
{
        //        // Ошибка захвата образа        //...
}
else
{
        //        // Захват образа произведен успешно        //...
}

4. Закрытие дескриптора образа и закрытие дескриптора файла WIM

Закрытие дескрипторов реализовано в функции очистки, таким образом, мы только должны вызвать ее.

        //
        // Произвести очистку
        //
WIMCleanup(hWimFile, hImage, Callback);

После успешного выполнения всех этих шагов образ выбранного диска/каталога будет захвачен в файл WIM, который Вы определили.

2. Применение образа из файла WIM к диску или каталогу.

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

Последовательность API вызовов для применения образа:

  1. f = WIMCreateFile() – открытие существующего файла WIM
  2. WIMGetAttributes() – получение атрибутов файла WIM, таких как количество образов в нем (для проверочных целей)
  3. WIMSetTemporaryPath() – установка временного пути для операций с образами (обязательный шаг для операции применения)
  4. i = WIMLoadImage(f, index) – загрузка образа с определенным индексом из файла WIM
  5. WIMApplyImage(i) – применение образа к диску/каталогу
  6. WIMCloseHandle(i) – закрытие дескриптора образа
  7. WIMCloseHandle(f) – закрытие дескриптора файла WIM

Рассмотрим реализацию этой последовательности в коде.

1. Открытие существующего файла WIM

Аналогично открытию существующего файла WIM для операции захвата:

LPCTSTR pszWimFileName = ...; // имя/путь исходного файла WIM с применяемым образом
...
//// Дескриптор файла WIM//
HANDLE hWimFile = NULL;
DWORD dwCreationResult = 0;
...
//// Открытие файла//
hWimFile = WIMCreateFile(pszWimFileName,    // существующий файл WIM с применяемым образом
                         WIM_GENERIC_READ,  // режим доступа
                         WIM_OPEN_EXISTING, // флаг открытия
                         0,                 // не проверять файл на наличие повреждений
                         WIM_COMPRESS_NONE, // не использовать сжатие
                         &dwCreationResult);

if (hWimFile == NULL)
{
        //        // Ошибка открытия файла WIM        //...
}

2. Получение атрибутов файла WIM (например, количество образов в нем)

Перед применением образа необходимо удостовериться, что он присутствует в файле WIM. Мы проверяем это, сравнивая индекс нужного нам образа (начиная с 1) с количеством образов в файле. Если индекс образа меньше или равен количеству образов в файле, значит, такой образ присутствует в этом файле WIM. Отметим, что индекс образа предоставляется функцией, вызывающей операцию применения.

DWORD dwImgIndex = ...; // индекс образа в файле WIM (начиная с 1)
... 
WIM_INFO hWimInfo = {0};
... 
//// Получение атрибутов файла WIM (например, для сравнения индекса с количеством образов в файле)//if (WIMGetAttributes(hWimFile, &hWimInfo, sizeof(hWimInfo)) == FALSE)
{
        //        // Ошибка получения атрибутов файла WIM        //
        ...
}
else
{
        //        // Сравниваем индекс образа с количеством образов        //if (hWimInfo.ImageCount < dwImgIndex) 
        {
                //                // Ошибка: образ с нужным индексом отсутствует в файле WIM                //...
}
...
}

3. Установка временного пути для операций с образами (обязательный шаг для операции применения)

Аналогично операции захвата:

        //
        // Установка временного пути (обязательный шаг для операции применения)
        //
TCHAR szTempDir[MAX_PATH] = ...;
...
if (WIMSetTemporaryPath(hWimFile, szTempDir) == FALSE)
{
        //        // Ошибка установления временного пути        //
        ...
}

4. Загрузка образа с определенным индексом из файла WIM

        //
        // Загрузка образа
        //
hImage = WIMLoadImage(hWimFile, dwImgIndex);
if (hImage == NULL)
{
        //        // Ошибка загрузки образа        //
        ...
}

5. Применение образа к диску/каталогу

LPCTSTR pszApplyDir = ...; // диск/каталог, к которым будет применен образ
BOOL fListOnly = ...; // TRUE, если надо вывести содержимое образа без применения,                      // FALSE, если надо применить образ
...
//// Применение образа//if (WIMApplyImage(hImage,
pszApplyDir, // применить к каталогу или диску((fListOnly == TRUE) ? WIM_FLAG_NO_APPLY : 0) // только вывести содержимое образа или                                                                // применить его| WIM_FLAG_VERIFY // использовать проверку во время применения) == FALSE)
{
        //        // Ошибка применения образа        //...
}
else
{
        //        // Образ применен успешно        //...
}

6. Закрытие дескриптора образа и закрытие дескриптора файла WIM

Аналогично операции захвата, закрытие дескрипторов реализовано в функции очистки, таким образом, мы только должны вызвать ее.

        //
        // Произвести очистку
        //
WIMCleanup(hWimFile, hImage, Callback);

После успешного выполнения всех этих шагов образ будет применен к выбранному диску/каталогу, или его содержимое будет выведено на экран.

3. Удаление образа из файла WIM.

Удаление образа из файла WIM – достаточно простая операция. Отметим, тем не менее, что только образы из многоóбразных файлов (2 или больше образов в одном файле) могут быть удалены. Если Ваш файл WIM содержит только один образ, то этот образ не может быть удален.

Последовательность API вызовов для удаления образа:

  1. f = WIMCreateFile() – открытие существующего файла WIM
  2. WIMGetAttributes() – получение атрибутов файла WIM, таких как количество образов в нем (для проверочных целей)
  3. WIMSetTemporaryPath() – установка временного пути для операций с образами (обязательный шаг для операции удаления)
  4. WIMDeleteImage(index) – удаление образа с заданным индексом
  5. WIMCloseHandle(f) – закрытие дескриптора файла WIM

Снова рассмотрим реализацию этой последовательности в коде.

1. Открытие существующего файла WIM

Аналогично открытию уже существующего файла WIM для операций захвата или применения:

LPCTSTR pszWimFileName = ...; // имя/путь исходного файла WIM с удаляемым образом
...
//// Дескриптор файла WIM//
HANDLE hWimFile = NULL;
DWORD dwCreationResult = 0;
...
//// Открытие файла//
hWimFile = WIMCreateFile(pszWimFileName,    // существующий файл WIM, из которого будет удален образWIM_GENERIC_READ | WIM_GENERIC_WRITE,  // режим доступа
WIM_OPEN_EXISTING, // флаг открытия
                         0,                 // не проверять файл на наличие повреждений
WIM_COMPRESS_NONE, // не использовать сжатие
&dwCreationResult);

if (hWimFile == NULL)
{
        //        // Ошибка открытия файла WIM        //...
}

2. Получение атрибутов файла WIM (таких как количество образов в нем)

Прежде чем удалять образ, мы должны удостовериться, что файл WIM содержит два или больше образов, и индекс удаляемого образа присутствует в файле. Реализация кода подобна той, которая используется в операциях захвата и применения.

DWORD dwImgIndex = ...; // индекс образа в файле WIM (начиная с 1)
... 
WIM_INFO hWimInfo = {0};
... 
//// Получение атрибутов файла WIM (например, для сравнения индекса с количеством образов в файле)//if (WIMGetAttributes(hWimFile, &hWimInfo, sizeof(hWimInfo)) == FALSE)
{
        //        // Ошибка получения атрибутов файла WIM        //...
}
else
{
if (hWimInfo.ImageCount < 2)
{
                //                // Ошибка: нельзя удалить единственный образ из файла WIM                //...
}
else{
                if (hWimInfo.ImageCount < dwImgIndex) 
{
                        //                        // Ошибка: образ с нужным индексом отсутствует в файле WIM                        //...
}
...
}
...
}
...

3. Установка временного пути для операций с образами (обязательный шаг для операции удаления)

Подобно операциям захвата и применения:

        //
        // Установка временного пути (обязательный шаг для операции удаления)
        //
TCHAR szTempDir[MAX_PATH] = ...;
...
if (WIMSetTemporaryPath(hWimFile, szTempDir) == FALSE)
{
        //        // Ошибка установления временного пути        //...
}

4. Удаление образа с заданным индексом

        //
        // Удаление образа
        //
        if (WIMDeleteImage(hWimFile, dwImgIndex /* индекс удаляемого образа */) == FALSE)
{
        //        // Ошибка удаления образа        //...
}
else
{
        //        // Образ удален успешно        //...
}

5. Закрытие дескриптора файла WIM

Аналогично операциям захвата и применения, закрытие дескриптора файла WIM реализовано в функции очистки, таким образом, мы только должны вызвать ее.

        //
        // Perform cleanup
        //
WIMCleanup(hWimFile, NULL, Callback);

Замечания касательно демонстрационного кода.

Демонстрационный код, доступный вместе с этой статьей, реализовывает три базовые операции WIMGAPI изложенные выше. Он может быть использован в качестве основы для более комплексных решений по работе с образами. Код может быть собран как в Visual Studio 2008 так и в Windows Driver Kit (с помощью соответствующей командной среды). Он может исполняться как в полной версии Windows, так и в Windows PE. Отдельное замечание касательно построения в среде Visual Studio приложений на C++, исполняемых в Windows PE: должна быть установлена специальная опция компиляции – для этого перейдите в свойства проекта Configuration Properties C/C++ Code Generation. Убедитесь, что опция Runtime Library установлена в Multi-threaded (/MT) для Release версии или Multi-threaded Debug (/MTd) для отладочной версии (см. рисунок ниже). Это обеспечит возможность исполнения Вашего приложения как в Windows PE, так и в полной версии Windows.


Заключение

Как мы видим, работа с WIMGAPI является довольно простой и прямой. WIMGAPI позволяет независимым поставщикам программного обеспечения и производителям комплектного оборудования разрабатывать свои собственные настраиваемые решения для развертывания и восстановления образов, для использования в среде Windows.

Список литературы

  1. Windows 7 AIK Documentation


Эта статья опубликована в журнале RSDN Magazine #4-2010. Информацию о журнале можно найти здесь
    Сообщений 3    Оценка 100        Оценить