前言
昨天我發布了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的注冊表
插件開發教程
配置好環境,打開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)討論
毛利