WIMGAPI库是 Windows 操作系统中的一个应用程序编程接口(API)可用于创建,编辑,提取浏览和部署 WIM、ESD格式的镜像文件。
这里只简单演示在Qt Creator, Mscv2017 Debug,WINDOWS 开发环境下的调用完整实例。
目录
- WIMGAPI描述
- 数据预处理
- 获取镜像的相关信息
- 导出指定系统中的文件
- 获取镜像目录结构(装载/卸载镜像)
- 提取镜像到指定目录
- 获取源码
WIMGAPI描述
WIMGAPI (Windows Imaging API)是 Windows 操作系统中的一个应用程序编程接口(API),用于处理 Windows 映像(.wim)文件格式。Windows 映像是一种压缩的文件容器,可用于存储操作系统、应用程序和驱动程序等文件
注:
WIMGAPI是适合WINDOWS 环境下处理WIM、ESD镜像格式的库,
常用方法说明:
- WIMCreateFile 创建新的图像文件或打开现有的图像文件。
- WIMApplyImage 将图像应用于Windows图像的目录路径(。wim)文件。
- WIMCaptureImage 从目录路径捕获图像,并将其存储在图像文件中。
- WIMCloseHandle 关闭打开的Windows imaging(。wim)文件或图像句柄。
- WIMCommitImageHandle 将已装载映像中的更改保存回. wim文件。
- WIMCopyFile 将现有文件复制到新文件中。通过回调函数通知应用程序它的进度。如果源文件包含验证数据,则在复制操作期间会验证文件的内容。
- WIMDeleteImage 从. wim (Windows映像)文件中删除映像,使其无法被访问。但是,文件资源仍然可供WIMSetReferenceFile功能。
- WIMDeleteImageMounts 从以前装载映像的所有目录中删除映像。
- WIMExportImage 从一个Windows映像(.wim)文件复制到另一个文件。
- WIMExtractImagePath 从Windows映像(.wim)文件复制到指定位置。
- WIMGetAttributes 返回存储在映像文件中的卷映像的数量。
- WIMGetImageCount 返回存储在Windows映像中的卷映像的数量。wim)文件。
- WIMGetImageInformation 返回有关中图像的信息。wim (Windows映像)文件。
- WIMGetMessageCallbackCount 返回图像库当前注册的回调例程的计数。
- WIMGetMountedImageHandle 返回一个WIM句柄和一个镜像句柄,对应于一个挂载的镜像目录。
- WIMGetMountedImageInfo 返回当前装载的映像列表。
- WIMGetMountedImageInfoFromHandle 查询已装载图像句柄的状态。
- WIMGetMountedImages 返回当前装载的映像列表。此功能已被取代WIMGetMountedImageInfo.
- WIMLoadImage 从Windows映像加载卷映像(。wim)文件。
- WIMMessageCallback 与一起使用的应用程序定义的函数WIMRegisterMessageCallback或者WIMUnregisterMessageCallback功能。
- WIMMountImage 将映像装入Windows映像(。wim)文件复制到指定目录。
- WIMMountImageHandle 将映像装入Windows映像(。wim)文件复制到指定目录。
- WIMRegisterLogFile 将用于调试或跟踪目的的日志文件注册到当前WIMGAPI会话中。
- WIMRegisterMessageCallback 用特定于图像的数据注册要调用的函数。有关可以处理的消息的信息,请参见信息.
- WIMRemountImage 重新激活以前装载到指定目录的已装载映像。
- WIMSetBootImage 将具有给定映像索引的映像标记为可启动。
- WIMSetImageInformation 将有关图像的信息存储在Windows图像(。wim)文件。
- WIMSetReferenceFile 启用WIMApplyImage和WIMCaptureImage使用替代的函数。文件资源的wim文件。当使用相似数据捕获多个图像时,这可以优化存储。
- WIMSetTemporaryPath 设置临时图像文件的存储位置。
- WIMSplitFile 启用大的Windows映像(。wim)文件分割成较小的部分,以便在较小形式的介质上复制或存储。
- WIMUnmountImage 卸载Windows映像中已装载的映像(。wim)文件从指定的目录。
- WIMUnmountImageHandle 从Windows映像中卸载映像(。wim ),它以前与一起安装WIMMountImageHandle功能。
- WIMUnregisterLogFile 出于调试或跟踪目的,从当前WIMGAPI会话中注销日志文件。
- WIMUnregisterMessageCallback 注销用特定于图像的数据调用的函数。
具体参数建议查看WIMGAPI 中的API说明
或者通过GItHub中获取wingapi的一些宏定义和类结构
https://github.com/VulpesSARL/MiniNT5-Tools/blob/b3bbc6c2c1af7207438a34b1546fa67ab7a038e2/FoxCWrapperWIM/wimgapi.h
数据预处理
WIMGAPI 是通过LoadLibraryExW 加载Wimgapi库获取各种Api的调用。
这里只包含部分使用到的api和宏定义,
完整的APi和宏定义建议查看GItHub上面的wimgapi.h文件内容
#define PF_INIT(proc, name) if (pf##proc == NULL) pf##proc = \
(proc##_t) GetProcAddress(GetLibraryHandle(#name), #proc)
#define PF_INIT_OR_OUT(proc, name) do {PF_INIT(proc, name); \
if (pf##proc == NULL) {qDebug("Unable to locate %s() in '%s.dll': %s", \
#proc, #name, GetLastError()); goto out;} } while(0)
// From https://docs.microsoft.com/en-us/previous-versions/msdn10/dd834960(v=msdn.10)
// as well as https://msfn.org/board/topic/150700-wimgapi-wimmountimage-progressbar/
enum WIMMessage {
WIM_MSG = WM_APP + 0x1476,
WIM_MSG_TEXT,
WIM_MSG_PROGRESS, // Indicates an update in the progress of an image application.
WIM_MSG_PROCESS, // Enables the caller to prevent a file or a directory from being captured or applied.
WIM_MSG_SCANNING, // Indicates that volume information is being gathered during an image capture.
WIM_MSG_SETRANGE, // Indicates the number of files that will be captured or applied.
WIM_MSG_SETPOS, // Indicates the number of files that have been captured or applied.
WIM_MSG_STEPIT, // Indicates that a file has been either captured or applied.
WIM_MSG_COMPRESS, // Enables the caller to prevent a file resource from being compressed during a capture.
WIM_MSG_ERROR, // Alerts the caller that an error has occurred while capturing or applying an image.
WIM_MSG_ALIGNMENT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_RETRY, // Sent when the file is being reapplied because of a network timeout.
WIM_MSG_SPLIT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_FILEINFO, // Used in conjunction with WimApplyImages()'s WIM_FLAG_FILEINFO flag to provide detailed file info.
WIM_MSG_INFO, // Sent when an info message is available.
WIM_MSG_WARNING, // Sent when a warning message is available.
WIM_MSG_CHK_PROCESS,
WIM_MSG_SUCCESS = 0,
WIM_MSG_ABORT_IMAGE = -1
};
/* Action type, for progress bar breakdown */
enum action_type {
OP_NOOP_WITH_TASKBAR = -3,
OP_NOOP = -2,
OP_INIT = -1,
OP_ANALYZE_MBR = 0,
OP_BADBLOCKS,
OP_ZERO_MBR,
OP_PARTITION,
OP_FORMAT,
OP_CREATE_FS,
OP_FIX_MBR,
OP_FILE_COPY,
OP_PATCH,
OP_FINALIZE,
OP_MAX
};
enum
{
WIM_COMPRESS_NONE = 0,
WIM_COMPRESS_XPRESS = 1,
WIM_COMPRESS_LZX = 2
};
#define WIM_GENERIC_READ GENERIC_READ
#define WIM_GENERIC_WRITE GENERIC_WRITE
#define WIM_GENERIC_MOUNT GENERIC_EXECUTE
#define WIM_OPEN_EXISTING OPEN_EXISTING
#define WIM_OPEN_ALWAYS OPEN_ALWAYS
#define WIM_UNDOCUMENTED_BULLSHIT 0x20000000
#define WIM_FLAG_RESERVED 0x00000001
#define WIM_FLAG_VERIFY 0x00000002
#define WIM_FLAG_INDEX 0x00000004
#define WIM_FLAG_NO_APPLY 0x00000008
#define WIM_FLAG_NO_DIRACL 0x00000010
#define WIM_FLAG_NO_FILEACL 0x00000020
#define WIM_FLAG_SHARE_WRITE 0x00000040
#define WIM_FLAG_FILEINFO 0x00000080
#define WIM_FLAG_NO_RP_FIX 0x00000100
#define WIM_FLAG_MOUNT_READONLY 0x00000200
#define INVALID_CALLBACK_VALUE 0xFFFFFFFF
static HMODULE GetLibraryHandle(const char* szLibraryName)
{
HMODULE h = NULL;
const wchar_t* wszLibraryName = NULL;
int size;
if (szLibraryName == NULL || szLibraryName[0] == 0)
goto out;
size = MultiByteToWideChar(CP_UTF8, 0, szLibraryName, -1, NULL, 0);
if ((size <= 1) || ((wszLibraryName = (wchar_t*)calloc(size, sizeof(wchar_t))) == NULL) ||
(MultiByteToWideChar(CP_UTF8, 0, szLibraryName, -1, (LPWSTR)wszLibraryName, size) != size))
goto out;
// If the library is already opened, just return a handle (that doesn't need to be freed)
if ((h = GetModuleHandleW(wszLibraryName)) != NULL)
goto out;
h = LoadLibraryExW(wszLibraryName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if(h==NULL)
qDebug("Unable to load '%S.dll': %s", wszLibraryName, GetLastError());
out:
free((LPWSTR)wszLibraryName);
return h;
}
静态实例:
typedef HANDLE (WINAPI *WIMCreateFile_t)(PWSTR, DWORD, DWORD, DWORD, DWORD, PDWORD);
static WIMCreateFile_t pfWIMCreateFile=NULL;
typedef BOOL (WINAPI *WIMGetImageInformation_t)(HANDLE, PVOID , PDWORD);
static WIMGetImageInformation_t pfWIMGetImageInformation=NULL;
typedef BOOL (WINAPI *WIMSetTemporaryPath_t)( HANDLE ,PWSTR );
static WIMSetTemporaryPath_t pfWIMSetTemporaryPath=NULL;
typedef DWORD (WINAPI *WIMGetImageCount_t)( HANDLE );
static WIMGetImageCount_t pfWIMGetImageCount=NULL;
typedef HANDLE (WINAPI *WIMLoadImage_t)(HANDLE ,DWORD );
static WIMLoadImage_t pfWIMLoadImage=NULL;
typedef BOOL (WINAPI *WIMCloseHandle_t)(HANDLE);
static WIMCloseHandle_t pfWIMCloseHandle=NULL;
typedef BOOL (WINAPI *WIMApplyImage_t)(HANDLE, PCWSTR, DWORD);
static WIMApplyImage_t pfWIMApplyImage=NULL;
typedef BOOL (WINAPI *WIMSetBootImage_t)(HANDLE, DWORD);
static WIMSetBootImage_t pfWIMSetBootImage=NULL;
typedef BOOL (WINAPI *WIMMountImage_t)(PCWSTR, PCWSTR, DWORD, PCWSTR);
static WIMMountImage_t pfWIMMountImage=NULL;
typedef BOOL (WINAPI *WIMUnmountImage_t)(PCWSTR, PCWSTR, DWORD, BOOL);
static WIMUnmountImage_t pfWIMUnmountImage=NULL;
typedef DWORD (WINAPI *WIMRegisterMessageCallback_t)(HANDLE, FARPROC, PVOID);
static WIMRegisterMessageCallback_t pfWIMRegisterMessageCallback=NULL;
typedef DWORD (WINAPI *WIMUnregisterMessageCallback_t)(HANDLE, FARPROC);
static WIMUnregisterMessageCallback_t pfWIMUnregisterMessageCallback=NULL;
typedef HANDLE (WINAPI *WIMCaptureImage_t)(HANDLE , PCWSTR , DWORD);
static WIMCaptureImage_t pfWIMCaptureImage=NULL;
typedef BOOL (WINAPI *WIMMountImageHandle_t)(HANDLE, PCWSTR, DWORD);
static WIMMountImageHandle_t pfWIMMountImageHandle=NULL;
typedef BOOL (WINAPI *WIMUnmountImageHandle_t)(HANDLE, DWORD);
static WIMUnmountImageHandle_t pfWIMUnmountImageHandle=NULL;
typedef BOOL (WINAPI *WIMExtractImagePath_t) (HANDLE, PWSTR, PWSTR, DWORD);
static WIMExtractImagePath_t pfWIMExtractImagePath=NULL;
格式转换:
在涉及到windows api的接口调用时,
Qt开发环境下,这两个方法是绝对少不了的,毕竟传入路径的时候,都需要转换文本格式。
#define wchar_to_utf8_no_alloc(wsrc, dest, dest_size) \
WideCharToMultiByte(CP_UTF8, 0, wsrc, -1, dest, dest_size, NULL, NULL)
#define utf8_to_wchar_no_alloc(src, wdest, wdest_size) \
MultiByteToWideChar(CP_UTF8, 0, src, -1, wdest, wdest_size)
/*
* Converts an UTF8 string to UTF-16 (allocate returned string)
* Returns NULL on error
*/
static wchar_t* _utf8_to_wchar(const char* str)
{
// qDebug()<<"[_utf8_to_wchar] -->";
int size = 0;
wchar_t* wstr = NULL;
if (str == NULL)
return NULL;
// Convert the empty string too
if (str[0] == 0)
return (wchar_t*)calloc(1, sizeof(wchar_t));
// Find out the size we need to allocate for our converted string
size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (size <= 1) // An empty string would be size 1
return NULL;
if ((wstr = (wchar_t*)calloc(size, sizeof(wchar_t))) == NULL)
return NULL;
if (utf8_to_wchar_no_alloc(str, wstr, size) != size) {
sfree(wstr);
return NULL;
}
return wstr;
}
/*
* Converts an UTF-16 string to UTF8 (allocate returned string)
* Returns NULL on error
*/
static char* _wchar_to_utf8(const wchar_t* wstr)
{
int size = 0;
char* str = NULL;
/* Find out the size we need to allocate for our converted string */
size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
if (size <= 1) /* An empty string would be size 1 */
return NULL;
if ((str = (char*)calloc(size, 1)) == NULL)
return NULL;
if (wchar_to_utf8_no_alloc(wstr, str, size) != size) {
free(str);
return NULL;
}
return str;
}
获取镜像的相关信息
WIM、ESD镜像版本信息实际上就是一个XML格式保存的,
通过WIMGetImageInformation 获取
示例中整个操作是将XML写入了一个临时文件,
值得注意的是pfWIMCreateFile 打开WIM时需要将dwFlagsAndAttributes设置为WIM_UNDOCUMENTED_BULLSHIT
如果设置为0,测试部分镜像会无法加载;
TempFilePath tempObj; //一个用于生成临时文件的类
//临时文件夹
wchar_t* wtemp=tempObj.GetTempDirU();
wchar_t* wimage;
///临时文件
wchar_t* wdst=tempObj.GetTempFileNameU(1);
///XML格式信息
wchar_t* wim_XMLinfo;
HANDLE hWim = NULL;
HANDLE hFile = NULL;
DWORD dw = 0;
DWORD dwFlagsAndAttributes=WIM_UNDOCUMENTED_BULLSHIT;
//Image_path 镜像文件路径
///使用wimgapi库读取数据
wimage=_utf8_to_wchar(Image_path.toStdString().c_str());
//实例化API
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
PF_INIT_OR_OUT(WIMGetImageInformation, Wimgapi);
PF_INIT_OR_OUT(WIMGetImageCount, Wimgapi);
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING,dwFlagsAndAttributes , 0, NULL);
if (hWim == NULL) {
qDebug(" Could not access image: %d", GetLastError());
goto out;
}
///设置临时文件夹路径
if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
qDebug(" Could not set temp path: %d", GetLastError());
goto out;
}
///获取镜像包含的系统数量
int Image_Count=pfWIMGetImageCount(hWim);
if(Image_Count==0)
qDebug(" Could not get image count: %d", GetLastError());
qDebug()<<"[Image_Count] "<<Image_Count;
///获取 系统的xml信息
if (!pfWIMGetImageInformation(hWim, &wim_XMLinfo, &dw) || (dw == 0)) {
qDebug(" Could not access WIM info: %d", GetLastError());
goto out;
}
///写入临时文件
hFile = CreateFileW(wdst, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == INVALID_HANDLE_VALUE)
|| (!WriteFile(hFile, wim_XMLinfo, dw, &dw, NULL))) {
qDebug(" Could not extract file: %s", GetLastError());
goto out;
}
CloseHandle(hFile);
hFile=INVALID_HANDLE_VALUE;
///打开xml读取数据
//Parser_XML(QString::fromWCharArray(wdst));
out:
if ( (hWim != NULL)) {
qDebug("Closing: %s", Image_path.toStdString().c_str());
if (hWim != NULL)
pfWIMCloseHandle(hWim);
}
if(hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
safe_free(wtemp);
safe_free(wimage);
safe_free(wdst);
导出指定系统中的文件
一个WIM、ESD镜像文件中至少包含一个系统镜像,
pfWIMLoadImage加载镜像时都需要先设置一个临时文件夹;
默认镜像索引从1开始,小于等于镜像系统数量;
void Lib_Wimgapi::Export_File_by(int index,QString wimpath,QString outfile)
{
TempFilePath tempObj;
//临时文件夹
wchar_t* wtemp=tempObj.GetTempDirU();
///镜像路径
wchar_t* wimage=_utf8_to_wchar(Image_path.toStdString().c_str());
///WIM 镜像路径 如 windows\\boot.exe
wchar_t* wsrc=_utf8_to_wchar(wimpath.toStdString().c_str());
///输出路径 如 C:\\Users\\admin\\Desktop\\boot.exe
wchar_t* wdst=_utf8_to_wchar(outfile.toStdString().c_str());
HANDLE hWim = NULL;
HANDLE hImage=NULL;
DWORD dw=0;
///实例化函数方法
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
PF_INIT_OR_OUT(WIMLoadImage, Wimgapi);
PF_INIT_OR_OUT(WIMExtractImagePath, Wimgapi);
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING,WIM_UNDOCUMENTED_BULLSHIT , 0, NULL);
if (hWim == NULL) {
qDebug(" Could not access image: %d", GetLastError());
goto out;
}
///设置临时文件夹路径
if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
qDebug(" Could not set temp path: %d", GetLastError());
goto out;
}
///至少为1
int WIM_INDEX=qMin(qMax(1,index),Image_Count);
hImage = pfWIMLoadImage(hWim, WIM_INDEX);
if (hImage == NULL) {
qDebug(" Could not set index: %d", GetLastError());
goto out;
}
///输出文件
if (!pfWIMExtractImagePath(hImage, wsrc, wdst, dw)) {
qDebug(" Could not extract file: %d", GetLastError());
goto out;
}
out:
if (hWim != NULL || hWim != hImage) {
qDebug("Closing: %s", Image_path.toStdString().c_str());
if (hWim != NULL)
pfWIMCloseHandle(hWim);
if (hImage != NULL)
pfWIMCloseHandle(hImage);
}
safe_free(wtemp);
safe_free(wimage);
safe_free(wsrc);
safe_free(wdst);
}
获取镜像目录结构(装载/卸载镜像)
WIMGAPI库中貌似没有可以直接获取目录结构的函数,写示例的时候我是先通过
加载镜像(pfWIMLoadImage)->
装载镜像(WIMMountImageHandle)->
读取目录结构(文件夹遍历)->
卸载镜像(WIMUnmountImageHandle)
这个流程来获取目录结构的;
TempFilePath tempObj;
HANDLE hWim = NULL;
HANDLE hImage = NULL;
///Image_path
wchar_t* wimage = _utf8_to_wchar(Image_path.toStdString().c_str());
//临时文件夹
wchar_t* wtemp=tempObj.GetTempDirU();
//用于装载的路径
QDir tempMountDir(QString::fromWCharArray(wtemp)+"\\TempMount_WIMGAPI");
if(!tempMountDir.exists())
tempMountDir.mkpath(QString::fromWCharArray(wtemp)+"\\TempMount_WIMGAPI");
wchar_t* wdst=_utf8_to_wchar(QString(QString::fromWCharArray(wtemp)+"\\TempMount_WIMGAPI").toStdString().c_str());
QString TempFile=QString::fromWCharArray(wdst);
qDebug()<<"临时装载路径: "<<TempFile;
//PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi);
PF_INIT_OR_OUT(WIMLoadImage, Wimgapi);
PF_INIT_OR_OUT(WIMApplyImage, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
//PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMMountImageHandle, Wimgapi);
PF_INIT_OR_OUT(WIMUnmountImageHandle, Wimgapi);
// 调试信息
// if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
// qDebug(" Could not set progress callback: %d", GetLastError());
// goto out;
// }
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ|WIM_GENERIC_MOUNT, WIM_OPEN_EXISTING,WIM_UNDOCUMENTED_BULLSHIT , 0, NULL);
if (hWim == NULL) {
qDebug(" Could not access image: %d", GetLastError());
goto out;
}
if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
qDebug(" Could not set temp path: %d", GetLastError());
goto out;
}
int WIM_INDEX=qMax(1,index.toInt());
hImage = pfWIMLoadImage(hWim, WIM_INDEX);
if (hImage == NULL) {
qDebug(" Could not set index: %d", GetLastError());
goto out;
}
///清除历史缓存
//! 方案一 用这种方法可以检查文件目录 但是无法获取文件的属性
// Fetch_Files.clear();
// // Run a first pass using WIM_FLAG_NO_APPLY to count the files
// if (!pfWIMApplyImage(hImage, wdst, WIM_FLAG_NO_APPLY)) {
// qDebug(" Could not count the files to apply: %d", GetLastError());
// goto out;
// }
//! 方案二 通过装载卸载的方式 获取文件目录结构
if(!pfWIMMountImageHandle(hImage,wdst,WIM_FLAG_MOUNT_READONLY))
{
qDebug("Could not Mount Image: %d", GetLastError());
goto out;
}
//遍历目录结构
//Lib_Deploy_Environment::getInstance().EnumerateFiles(TempFile,Fetch_Files);
//! 如果需要对镜像镜像修改 可以在此处操作文件目录/文件 然后提交保存
//!WIMCommitImageHandle 保存装载后的更改!
if(!pfWIMUnmountImageHandle(hImage,0))
{
//dism /Get-MountedWimInfo
//dism /Cleanup-Wim
//CMD_WRITE("dism /Cleanup-Wim");
qDebug("Could not Unmount Image: %d", GetLastError());
}
out:
if ((hImage != NULL) || (hWim != NULL)) {
qDebug("Closing: %s", Image_path.toStdString().c_str());
if (hImage != NULL)
pfWIMCloseHandle(hImage);
if (hWim != NULL)
pfWIMCloseHandle(hWim);
}
// if (pfWIMUnregisterMessageCallback != NULL)
// pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
safe_free(wimage);
safe_free(wdst);
safe_free(wtemp);
其中需要注意的是:
如果因为各种原因卸载或者装载失败,都需要调用Windwos系统自带的Dism.exe删除不完整的记录CMD执行 dism.exe /Cleanup-Wim
提取镜像到指定目录
通过pfWIMApplyImage 提取镜像时,可以加上WIMRegisterMessageCallback的回调函数,获取提取文件进度,也可以实现简单的进度条效果,
//!回调函数
static DWORD WimProgressCallback(DWORD dwMsgId, WPARAM wParam, LPARAM lParam, PVOID pvIgnored)
{
//qDebug()<<"[dwMsgId] "<<dwMsgId;
PBOOL pbCancel = NULL;
PWIN32_FIND_DATA pFileData;
const char* level = NULL;
uint64_t size;
switch (dwMsgId) {
case WIM_MSG_PROGRESS:
qDebug()<<"[WIM_MSG_PROGRESS] -->";
// The default WIM progress is useless for apply (freezes at 95%, which is usually when
// only half the files have been processed), so we only use it for mounting/unmounting.
//UpdateProgressWithInfo(progress_op, progress_msg, progress_offset + wParam, progress_total);
break;
case WIM_MSG_PROCESS:
{
qDebug()<<"[WIM_MSG_PROCESS] -->";
PWSTR pszFullPath=(PWSTR)wParam ;
qDebug()<<"WIM_MSG_PROCESS pszFullPath : "<<QString::fromWCharArray(pszFullPath);
// The amount of files processed is overwhelming (16k+ for a typical image),
// and trying to display it *WILL* slow us down, so we don't.
pbCancel = (PBOOL)lParam;
*pbCancel = TRUE;
break;
}
// break;
case WIM_MSG_FILEINFO:
{
qDebug()<<"[WIM_MSG_FILEINFO] -->";
pFileData = (PWIN32_FIND_DATA)lParam;
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//!文件夹
qDebug()<<"[Dir] "<<QString::fromWCharArray((PWSTR)pFileData->cFileName);
qDebug()<<"[wParam] "<<QString::fromWCharArray((PWSTR)wParam);
} else {
//!文件
qDebug()<<"[File] "<<QString::fromWCharArray(pFileData->cFileName);
qDebug()<<"[wParam] "<<QString::fromWCharArray((PWSTR)wParam);
}
break;
}
case WIM_MSG_RETRY:
qDebug()<<"[WIM_MSG_RETRY] -->";
level = "retry";
// fall through
case WIM_MSG_INFO:
if (level == NULL) level = "info";
// fall through
case WIM_MSG_WARNING:
if (level == NULL) level = "warning";
// fall through
case WIM_MSG_ERROR:
if (level == NULL) level = "error";
SetLastError((DWORD)lParam);
qDebug("WIM processing %s: %S [%d]\n", level, (PWSTR)wParam, GetLastError());
break;
}
return WIM_MSG_SUCCESS;
}
//!提取镜像
TempFilePath tempObj;
//临时文件夹
wchar_t* wtemp=tempObj.GetTempDirU();
//QString Imagepath 镜像路径
wchar_t* wimage=_utf8_to_wchar(Imagepath.toStdString().c_str());
//QString descpath 提取到指定文件夹
wchar_t* wdst=_utf8_to_wchar(descpath.toStdString().c_str());
HANDLE hWim = NULL;
HANDLE hImage=NULL;
DWORD dw=0;
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi);
PF_INIT_OR_OUT(WIMLoadImage, Wimgapi);
PF_INIT_OR_OUT(WIMApplyImage, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
//设置回调函数
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
qDebug(" Could not set progress callback: %d", GetLastError());
goto out;
}
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ|WIM_GENERIC_MOUNT, WIM_OPEN_EXISTING,WIM_UNDOCUMENTED_BULLSHIT , 0, NULL);
if (hWim == NULL) {
qDebug(" Could not access image: %d", GetLastError());
goto out;
}
if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
qDebug(" Could not set temp path: %d", GetLastError());
goto out;
}
//指定镜像索引index
hImage = pfWIMLoadImage(hWim, index);
if (hImage == NULL) {
qDebug(" Could not set index: %d", GetLastError());
goto out;
}
//! WIM_FLAG_FILEINFO 提取镜像
if (!pfWIMApplyImage(hImage, wdst, WIM_FLAG_FILEINFO)) {
qDebug(" Could not Export WIM Image: %d", GetLastError());
goto out;
}
out:
if ((hImage != NULL) || (hWim != NULL)) {
qDebug("Closing: %s", Image_path.toStdString().c_str());
if (hImage != NULL)
pfWIMCloseHandle(hImage);
if (hWim != NULL)
pfWIMCloseHandle(hWim);
}
if (pfWIMUnregisterMessageCallback != NULL)
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
safe_free(wimage);
safe_free(wdst);
safe_free(wtemp);
提示:
pfWIMApplyImage 函数的
- WIM_FLAG_NO_APPLY 属性不提取文件,只会在回调函数中返回WIM_MSG_PROCESS信号,同时可输出文件路径,可用于统计文件目录数量
- WIM_FLAG_FILEINFO 属性会提取文件,按目录结构写入指定文件夹中,会在回调函数中返回WIM_MSG_FILEINFO信号。
获取源码
整个WIMGAPI库示例的关键代码上面已经完整的展示了,
而且整个示例并不复杂花点时间就可以搞懂,但是你想省下这段时间,少走点弯路那么可以:
订阅专栏获取所有源码 下载链接。
源码版本:Qt Creator 5.13 | MSCV2017