给IE换皮肤软件的制作这个软件制作起来非常简单,就是换掉注册表里的一个键值,难度在于对SHELL的扩展,而不在于注册表的操作。
我把它设计成在BMP文件上右击出一个右键菜单,然后有一个“给IE换皮肤”的命令项,点了以后就把此文件的全路径写到注册表里去。
SHELL扩展可以参考一些书,注册表操作我用了ATL里的CRegkey。
SHELL扩展的DLL组件需要实现IShellExtInit、IContextMenu这2个接口。
主要代码如下:
HRESULT CSimpleShlExt::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID)
{
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
// 在数据对象内查找 CF_HDROP 型数据.
if ( FAILED( pDataObj->GetData ( &fmt, &stg )))
{
// Nope! Return an "invalid argument" error back to Explorer.
return E_INVALIDARG;
}
// 获得指向实际数据的指针
hDrop = (HDROP) GlobalLock ( stg.hGlobal );
// 检查非NULL.
if ( NULL == hDrop )
{
return E_INVALIDARG;
}
// 有效性检查 – 保证最少有一个文件名.
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
if ( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
HRESULT hr = S_OK;
// 取得第一个文件名并把它保存在类成员m_szFile 中.
if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ))
{
hr = E_INVALIDARG;
}
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
HRESULT CSimpleShlExt::QueryContextMenu ( HMENU hmenu,UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
// 如果标志包含 CMF_DEFAULTONLY 我们不作任何事情.
if ( uFlags & CMF_DEFAULTONLY )
{
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
}
InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("给IE换皮肤") );
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );
}
HRESULT CSimpleShlExt::GetCommandString( UINT idCmd, UINT uFlags,UINT* pwReserved, LPSTR pszName, UINT cchMax )
{
USES_CONVERSION;
//检查 idCmd, 它必须是0,因为我们仅有一个添加的菜单项.
if ( 0 != idCmd )
return E_INVALIDARG;
// 如果 Explorer 要求帮助字符串,就将它拷贝到提供的缓冲区中.
if ( uFlags & GCS_HELPTEXT )
{
LPCTSTR szText = _T("给IE换皮肤");
if ( uFlags & GCS_UNICODE )
{
// 我们需要将 pszName 转化为一个 Unicode 字符串, 接着使用Unicode字符串拷贝 API.
lstrcpynW ( (LPWSTR) pszName, T2CW(szText), cchMax );
}
else
{
// 使用 ANSI 字符串拷贝API 来返回帮助字符串.
lstrcpynA ( pszName, T2CA(szText), cchMax );
}
return S_OK;
}
return E_INVALIDARG;
}
HRESULT CSimpleShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
// 如果lpVerb 实际指向一个字符串, 忽略此次调用并退出.
if ( 0 != HIWORD( pCmdInfo->lpVerb ))
return E_INVALIDARG;
// 点击的命令索引 – 在这里,唯一合法的索引为0.
switch ( LOWORD( pCmdInfo->lpVerb ))
{
case 0:
{
LONG lResult = 0;
CRegKey reg;
//open the required registry key
LPCTSTR lpszKey = "Software\\Microsoft\\Internet Explorer\\Toolbar";
lResult = reg.Open(HKEY_CURRENT_USER,lpszKey);
//check if opened successfully
if(ERROR_SUCCESS != lResult)
{
return FALSE;
}
//set the value
lResult = reg.SetValue(m_szFile,"BackBitmap");
if(ERROR_SUCCESS != lResult)
{
return FALSE;
}
//done, close and return success
reg.Close();
return S_OK;
}
break;
default:
return E_INVALIDARG;
break;
}
}
考虑到撤消IE皮肤的需要,让右键菜单在每种类型的文件里都出现吧。
J ATL里的RGS文件这样写:
HKCR
{
NoRemove *
{
NoRemove ShellEx
{
NoRemove ContextMenuHandlers
{
ForceRemove SimpleShlExt = s '{EBA72D87-BFEB-48FE-938E-A9480300F8EA}'
}
}
}
}
SHELL扩展很有意思,它让你先写好DLL组件,并且实现某些接口,然后IE怎么来调用你的这个扩展的DLL组件呢?很简单,也是很棒的一个方法。就是在注册表相应的地方有一个键,值就是你组件的GUID,IE发现有这个值,就去根据GUID创建组件,然后查询并调用你实现的接口方法。
我们的软件完全可以借鉴这种插件式的方法