前言
昨天我发布了NCleaner,一款Dism++清理插件(地址:http://bbs.pcbeta.com/viewthread-1692182-1-1.html)
有些人想要我开源NCleaner;我只能说很遗憾,鉴于国内环境,是不可能的。我就说个真实故事吧(其实很悲哀)
曾经,一位大牛写了一个充满了黑科技的软件,在论坛发布并公布了源代码
某天,那位大牛发现某些人修改他的源代码用于商业用途,更可气的是某些人只修改了软件的名字
最终,那位大牛改掉了他的软件名,从此再也不公布主程序源代码
那个软件就是Dism管理器,即Dism++前身
虽然NCleaner闭源,但是我可以写一篇Dism++插件开发教程作为补偿(笑)
虽然我不能保证该教程写的多么生动;但是我会尽力(笑)
Dism++,我只能说这软件真的是非常强大(这只是我个人看法);至于可靠性,你只要不作死开启专家模式那就没问题(笑)
说到Dism++插件开发,其实以前作者提供过SDK(但是很长时间就再也没提供过);上次和作者谈了谈我想开发插件的想法(希望作者可以提供SDK);最终如愿以偿,并发布了NCleaner(用游戏相关话语来说,我也是过了一周目的人)
开发环境
1. Dism++ 10.1.5.3及以后版本的Dism++(没有Dism++开发Dism++插件是不可能的)
2. 一个可以编译dll的C++编译器,我推荐使用Visual Studio 2015
3. 如果可以的话,最好安装Windows SDK 10.0.10586
Dism++清理插件相关内容科普
首先说下Dism++清理插件函数定义(参考Dism++帮助文档.pdf 68页)
//一个清理插件函数定义
HRESULT WINAPI CleanupPlugin(
_In_ DismSession Session,
_Reserved_ DWORD Flags,
_In_opt_ UINT64 *CleanUpSpace,
_In_ DismCallBack CallBack,
_In_ LPVOID UserData);
DismSession Session
映像会话,可以使用此获取映像的各种信息(可以看作映像会话的句柄)
DWORD Flags
保留,Dism++现在不使用此参数,请忽略
UINT64 *CleanUpSpace
如果 CleanUpSpace 为空,那么函数需要执行清理。
如果不为空,说明仅要预估可清理的空间。最后将预估大小用此变量返回
DismCallBack CallBack
Dism++清理回调函数,用于展示进度,文件路径等信息。
如果此参数为 NULL,则表示没有回调。
回调函数定义参考下一段介绍
LPVOID UserData
回调函数的 UserData 部分,请务必传入 CallBack 中。
返回值: 如果函数执行成功,请返回 S_OK,其他任何值都表示错误
回调函数定义
typedef DWORD(WINAPI *DismCallBack)(
DWORD dwMessageId,
WPARAM wParam,
LPARAM lParam,
PVOID UserData);
回调函数支持以下消息:
DISM_MSG_PROGRESS – 用于反馈处理进度
wParam =当前完成百分比
lParam = 0
DISM_MSG_PROCESS – 用于在状态栏中展示正在处理的文件路径
wParam = (PWSTR) pszFullPath
lParam = 0
DISM_MGS_RemoveInfo
报告 UI 需要删除的文件,此消息仅扫描时可用,清理时将无视此消息
wParam = 0
lParam = (LPCWSTR) 需要删除的文件路径
Dism++收到此消息后,会将文件路径展示在详细信息中。
我经常使用的Dism++ API介绍(希望对其他人有用)
HRESULT WINAPI DismGetSystemInfoBySession(
DismSession Session,
DismSystem** Info);
该API作用是获取当前映像信息;你会得到DismSystem结构的指针(看Dism++作者对于该结构的说明,我想你们应该都能理解)
HRESULT WINAPI DismFreeMemory(void* pStruct);
切记通过Dism++获得的结构指针需要用该API释放
HRESULT WINAPI DismRegOpenKeyEx(
DismSession Session,
HKEY hKey,
LPCWSTR lpSubKey,
REGSAM samDesired,
PHKEY phkResult);
获取注册表键值,用法类似RegOpenKeyEx
HRESULT WINAPI DismWriteLog(
DWORD LogLevel,
LPCWSTR LogName,
LPCWSTR LogValue);
写入日志,LogLevel定义如下,LogName是日志类别,LogValue是日志内容
DismLogLevelSilent 不输出任何信息
DismLogLevelFailure 仅错误
DismLogLevelWarning 错误和警告
DismLogLevelInformation 错误、警告和信息
DismLogLevelDebug 以上所有内容和调试输出
插件开发注意事项
1. Dism++基于CBS,而CBS是一个COM组件;所以在启动时会自动进行COM初始化;你不需要在清理插件函数中执行COM初始化;切记不要在清理插件函数中调用COM反初始化,否则后果我和Dism++的作者们都不敢想(当然不敢试)
2. 注意DismSystem结构的RootPath的路径类似"C:","D:\Image"这样的(在写文件操作代码时需要注意)
3. Dism++的展开环境变量API在离线下会受限
4. Dism++的打开注册表API不支持打开离线映像的HKEY_USERS(Dism++旧版本支持;只是作者在某个版本移除了);HKEY_CURRENT_USER打开的是Default User的注册表
5. 只有返回S_OK,才代表Dism++ API正确执行
插件开发教程
配置好环境,打开Visual Studio;首先新建一个Win32动态链接库项目
接着把Dism++SDK(Dism++目录\Dism++SDK目录中的内容)复制到你的项目目录,并加入你的解决方案
然后你可以在cpp文件中根据前文内容编写你要编写的代码(下面举个例子)
#include <Windows.h>
#include "Dism++API.h"
#include "Plugin.h"
#ifdef _AMD64_
#pragma comment(lib,"Dism++x64.lib")
#else
#pragma comment(lib,"Dism++x86.lib")
#endif
// Dism++清理插件开发入门
HRESULT WINAPI TestCleanup(
_In_ DismSession Session,
_Reserved_ DWORD Flags,
_In_ UINT64 *CleanUpSpace,
_In_ DismCallBack CallBack,
_In_ LPVOID UserData)
{
MessageBoxW(nullptr, L"Hello Dism++", L"HelloWorld", MB_ICONINFORMATION);
return S_OK;
}
顺便你需要建立一个def文件导出你的符号(同样举个例子)
LIBRARY
EXPORTS
TestCleanup
还有你需要编写Dism++的插件配置文件(需要命名为Custom.xml,下面举个例子)
<?xml version="1.0" encoding="utf-8"?>
<Data>
<CleanCollection4>
<Item Name="清理项目名" Level="2">
<Discription>清理项目描述 </Discription>
<Warning>警告对话框要显示的内容</Warning>
<Group>清理项目所属组</Group>
<ScanCollection>
<Scan Type="Custom">
<Activate>
<Custom ProcName="插件dll对应的导出符号"/>
</Activate>
</Scan>
</ScanCollection>
</Item>
</CleanCollection4>
</Data>
和Dism++插件信息文件(需要命名为Info.xml)
<?xml version="1.0" encoding="utf-8"?>
<Data>
<Plugin>
<Name>插件名称</Name>
<Version>填写插件的版本号,例如1.0.0.0</Version>
</Plugin>
<Languages>
<zh>
<FriendlyName>插件名称(中文)</FriendlyName>
<Decription>插件注释(中文)</Decription>
</zh>
<en>
<FriendlyName>插件名称(英文)</FriendlyName>
<Decription>插件注释(英文)</Decription>
</en>
</Languages>
</Data>
编译
64位dll需要命名为Plugin.amd64.dll;32位dll需要命名为Plugin.x86.dll
Plugin.amd64.dll , Plugin.x86.dll , Custom.xml , Info.xml这四个文件需要放在一个以 [插件名]_[发布者名称Base64加密密文;发布者名称要求16个字符]目录中,并把该目录复制入Dism++目录\Config\Plugin目录即可
然后开启Dism++就可以进行调试了(VisualStudio->调试->附加到进程)
Demo项目下载(要求VS2015)
http://pan.baidu.com/s/1o8znY7w
结语
最后我得提醒想开发Dism++清理插件的开发者,虽然插件写起来是容易的,但要注意的细节是很多(写本教程时,楼主的体会越发深刻)
如果想获取更多信息,建议加入Dism++官方群(200783396)讨论
毛利