灰常感謝各位達(dá)人昨天的熱心回帖,讓我受益匪淺。我仰望夜空,群星點(diǎn)點(diǎn),就如各位的點(diǎn)睛之語(yǔ),在無(wú)盡的蒼穹閃耀。這讓我深深地意識(shí)到,在這里,不僅可以分享成果,也可以分享困惑、分享寂寞。(開(kāi)場(chǎng)白到此結(jié)束~)
在平常的編程中,我發(fā)現(xiàn)很容易遇到這種結(jié)構(gòu):
(1號(hào)方案)
BOOL foo()
{
BOOL bRet = FALSE;
HANDLE hProcess = OpenProcess(...);
if (hProcess != NULL)
{
HANDLE hToken = OpenProcessToken(hProcess, ...);
if (hToken != NULL)
{
// ...
if (LookupPrivilegeValue(...))
{
if (AdjustTokenPrivileges(hToken, ...))
{
bRet = TRUE;
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
return bRet;
}
如上寫法,容易造成縮進(jìn)級(jí)別不斷增加。為了避免這種情況,可以改成:
(2號(hào)方案)
BOOL foo()
{
HANDLE hProcess = OpenProcess(...);
if (hProcess == NULL)
{
return FALSE;
}
HANDLE hToken = OpenProcessToken(hProcess, ...);
if (hToken == NULL)
{
CloseHandle(hProcess);
return FALSE;
}
// ...
if (!LookupPrivilegeValue(...))
{
CloseHandle(hToken);
CloseHandle(hProcess);
return FALSE;
}
if (!AdjustTokenPrivileges(hToken, ...))
{
CloseHandle(hToken);
CloseHandle(hProcess);
return FALSE;
}
CloseHandle(hToken);
CloseHandle(hProcess);
return TRUE;
}
這樣,又引來(lái)了新的問(wèn)題,每次 return FALSE 時(shí)的清理任務(wù)比較麻煩,要是每步操作都引進(jìn)新的 HANDLE 的話,后續(xù)的清理工作就變得非常繁重。有人推薦do…while(0)的結(jié)構(gòu),有人推薦goto。這兩種形式分別是——
do…while(0):
(3號(hào)方案)
BOOL foo()
{
HANDLE hProcess = OpenProcess(...);
if (hProcess == NULL)
{
return FALSE;
}
BOOL bRet = FALSE;
do
{
HANDLE hToken = OpenProcessToken(hProcess, ...);
if (hToken == NULL)
{
break;
}
// ...
BOOL bRetInner = FALSE;
do
{
if (!LookupPrivilegeValue(...))
{
break;
}
if (!AdjustTokenPrivileges(hToken, ...))
{
break;
}
bRetInner = TRUE;
} while (0);
CloseHandle(hToken);
if (!bRetInner)
{
break;
}
bRet = TRUE;
} while (0);
CloseHandle(hProcess);
return bRet;
}
這種結(jié)構(gòu)可以避免每次 return FALSE 前的一堆清理工作,但缺點(diǎn)是,有幾個(gè)依賴性的 HANDLE,就要嵌套幾層的 do…while(0),有時(shí)候也會(huì)遇到需要三四層嵌套的情形。
goto:
(4.1號(hào)方案)
BOOL foo() { BOOL bRet = FALSE;
HANDLE hProcess = OpenProcess(...);
if (hProcess == NULL) { goto CLEAR; }
HANDLE hToken = OpenProcessToken(hProcess, ...);
if (hToken == NULL) { goto CLEAR; }
// ...
if (!LookupPrivilegeValue(...)) { goto CLEAR; }
if (!AdjustTokenPrivileges(hToken, ...)) { goto CLEAR; }
bRet = TRUE;
CLEAR: if (hToken != NULL) { CloseHandle(hToken); }
if (hProcess != NULL) { CloseHandle(hProcess); }
return bRet; } | (4.2號(hào)方案)
BOOL foo() { BOOL bRet = FALSE;
HANDLE hProcess = OpenProcess(...);
if (hProcess == NULL) { goto ERROR_LEVEL0; }
HANDLE hToken = OpenProcessToken(hProcess, ...);
if (hToken == NULL) { goto ERROR_LEVEL1; }
// ...
if (!LookupPrivilegeValue(...)) { goto ERROR_LEVEL2; }
if (!AdjustTokenPrivileges(hToken, ...)) { goto ERROR_LEVEL2; }
bRet = TRUE;
ERROR_LEVEL2: CloseHandle(hToken); ERROR_LEVEL1: CloseHandle(hProcess); ERROR_LEVEL0: return bRet; } |
(左邊和右邊哪種好一點(diǎn)。。。?)
在這種情形下,goto 的方案似乎是完美的。但是 goto 如果遇到 C++,缺點(diǎn)體現(xiàn)出來(lái)了。下面這一段,現(xiàn)在是 do…while(0) 結(jié)構(gòu)(只有一層嵌套,這種結(jié)構(gòu)用在這里還算合理):
BOOL foo()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
while (true)
{
if (FAILED(hr))
{
break;
}
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
if (FAILED(hr))
{
break;
}
CComPtr<IWbemLocator> pLoc = NULL;
hr = pLoc.CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER);
if (FAILED(hr))
{
break;
}
CComPtr<IWbemServices> pSvc = NULL;
hr = pLoc->ConnectServer(_T("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &pSvc);
if (FAILED(hr))
{
break;
}
hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hr))
{
break;
}
CComPtr<IEnumWbemClassObject> pEnum = NULL;
_bstr_t bstrLang = _T("WQL");
_bstr_t bstrSql = _T("SELECT * FROM __InstanceCreationEvent WITHIN 10")
_T("WHERE TargetInstance ISA 'Win32_LogonSession' AND (TargetInstance.LogonType = 2 OR TargetInstance.LogonType = 11)");
hr = pSvc->ExecNotificationQuery(bstrLang, bstrSql, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
if (FAILED(hr))
{
break;
}
ULONG uCount = 1;
CComPtr<IWbemClassObject> pNext = NULL;
hr = pEnum->Next(WBEM_INFINITE, uCount, &pNext, &uCount);
if (FAILED(hr))
{
break;
}
// ...
break;
}
CoUninitialize();
return SUCCEEDED(hr);
}
如果改成 goto,則需要把所有需要對(duì)象的定義全放到最前面來(lái),不然 goto 會(huì)跳過(guò)他們的初始化,編譯不過(guò)。但是,所有對(duì)象都放到最前面定義,又違反了即用即聲明的規(guī)則,而且太多了也容易混淆。
最后,問(wèn)題是,如果遇到 C++ 的、多層嵌套的,大家一般如何組織代碼呢?
謝謝!
posted on 2010-03-30 09:55
溪流 閱讀(2780)
評(píng)論(26) 編輯 收藏 引用 所屬分類:
C++