在做DeskBand过程中,遇到了一些问题,这些问题说大可大,说小可小,不是三下两下就解决了,还是花了一些时间来解决。
一、会导致explorer.exe挂掉
原因:
这个问题导致是由于当前DeskBand的DLL的引用计数不正确导致的。系统每隔一段时间就会去调用 DllCanUnloadNow函数来检测当前DLL是否可以被系统卸载。系统大约每隔15分钟会检查,当DllCanUnloadNow函数返回S_OK时,系统就会把这个DLL从他的进程地址空间中卸载,那么此时DLL中的DeskBand Window的消息处理函数还在运行,然而DLL已经从进程中移除了,那么消息处理函数的调用就会失败,导致explorer.exe挂掉。
解决方案:
1)当创建一个ClassFactory对象时对DLL的引用计数g_lDllRefCount(全局变量)加1,对象释放时把g_lDllRefCount减1。
2)当Band Object对象创建时也要对这个引用计数加1,释放时减1,因为只要Band Object存在的话,这个DLL必须存在,如果Band Object释放的话,那么这个DLL就可以被释放了。
二、通过代码显示DeskBand,去掉会弹出询问对话框
这是由于通过程序来控制一个COM对象在任务栏上面显示,微软基于安全性的考虑,就会弹出一个对话框来询问用户是否显示这个Band对象,这样有一个好处就是防止有些广告程序会莫名其妙地显示在任务栏中。
怎么去掉这个对话框呢?
利用一个钩子,::SetWindowsHookEx(WH_CALLWNDPROCRET, (HOOKPROC)HookCallWndProcRet, hCurInst, 0);
问题是什么去注册这个钩子呢,当你写的程序使Band显示或隐藏的时候就应该去注册这个钩子,所以一般情况下你这个DLL可以写一个导出方法来使Band显示或隐藏。
得到HINSTANCE的代码如下:
HMODULE WINAPI SdkDeskBandHook::GetCurrentModuleHandle()
{
// s_somevar must be static variable, otherwise the returned HMODULE is not correct
// instance of current dll.
static int s_somevar = 0;
MEMORY_BASIC_INFORMATION mbi;
if(!::VirtualQuery(&s_somevar, &mbi, sizeof(mbi)))
{
return NULL;
}
return static_cast<HMODULE>(mbi.AllocationBase);
}
BOOL WINAPI SdkCommonHelper::ShowDeskBand(BOOL fShowOrHide)
{
ITrayDeskBand *pTrayDeskBand = NULL;
HRESULT hr = CoCreateInstance(CLSID_TrayDeskBand, NULL, CLSCTX_ALL,
IID_PPV_ARGS(&pTrayDeskBand));
// Vista and higher operating system
if ( SUCCEEDED(hr) )
{
if ( TRUE == fShowOrHide )
{
hr = pTrayDeskBand->DeskBandRegistrationChanged();
if ( SUCCEEDED(hr) )
{
// If window hook is not started, starts it.
if ( FALSE == CSdkDeskBandHook::IsHookStart() )
{
CSdkDeskBandHook::StartHook();
}
hr = pTrayDeskBand->IsDeskBandShown(CLSID_SampleDeskBand);
if ( SUCCEEDED(hr) && (S_FALSE == hr) )
{
hr = pTrayDeskBand->ShowDeskBand(CLSID_SampleDeskBand);
}
}
}
else
{
hr = pTrayDeskBand->IsDeskBandShown(CLSID_SampleDeskBand);
if ( SUCCEEDED(hr) && (S_OK == hr) )
{
hr = pTrayDeskBand->HideDeskBand(CLSID_SampleDeskBand);
}
}
}
SAFE_RELEASE(pTrayDeskBand);
return (SUCCEEDED(hr)) ? TRUE : FALSE;
}
利用钩子,去钩窗体的WM_INITDIALOG消息,找到那个对话框窗体上的YES按钮,给它发送一个BN_CLICKED消息。反正就是模拟点击事件。在钩子的处理函数中加入如下代码:
// Find [Yes] button on the dialog box for prompting user allow desk band show in task bar.
LPCWPRETSTRUCT lpMsg = (LPCWPRETSTRUCT)lParam;
if ( (NULL != lpMsg) && (WM_INITDIALOG == lpMsg->message) )
{
// Get caption of dialog box, which is same with the name of sub menu item of toolbar.
WCHAR szCaption[100] = { 0 };
GetWindowText(lpMsg->hwnd, szCaption, 100);
// Get tool bar menu item name from registry, because the caption of dialog box is same
// with the tool bar menu item.
wstring strMenuName = GetToolbarMenuNameFromRegistry();
if ( (0 == CommonHelper::OrdinalIgnoreCaseCompareStrings(szCaption, strMenuName.c_str()))
|| (0 == CommonHelper::OrdinalIgnoreCaseCompareStrings(szCaption, L"explorer.exe")) )
{
HWND destHwnd = FindWindowEx(lpMsg->hwnd, NULL, L"DirectUIHWND", NULL);
if ( destHwnd != NULL )
{
HWND sink = FindWindowEx(destHwnd, NULL , L"CtrlNotifySink", NULL);
int i = 0;
while ( i++ < 6 )
{
sink = FindWindowEx(destHwnd, sink, L"CtrlNotifySink", NULL);
}
HWND button = FindWindowEx(sink, NULL, L"Button", NULL);
SendMessage(sink, WM_COMMAND, BN_CLICKED, (LPARAM)button);
}
}
}