随笔-90  评论-947  文章-0  trackbacks-0
 

灰常感谢各位达人昨天的热心回帖,让我受益匪浅。我仰望夜空,群星点点,就如各位的点睛之语,在无尽的苍穹闪耀。这让我深深地意识到,在这里,不仅可以分享成果,也可以分享困惑、分享寂寞。(开场白到此结束~)

在平常的编程中,我发现很容易遇到这种结构:

(1号方案)

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;
}

如上写法,容易造成缩进级别不断增加。为了避免这种情况,可以改成:

(2号方案)

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;
}

这样,又引来了新的问题,每次 return FALSE 时的清理任务比较麻烦,要是每步操作都引进新的 HANDLE 的话,后续的清理工作就变得非常繁重。有人推荐do…while(0)的结构,有人推荐goto。这两种形式分别是——

do…while(0)

(3号方案)

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;
}

这种结构可以避免每次 return FALSE 前的一堆清理工作,但缺点是,有几个依赖性的 HANDLE,就要嵌套几层的 do…while(0),有时候也会遇到需要三四层嵌套的情形。

goto

(4.1号方案)

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号方案)

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;
}

(左边和右边哪种好一点。。。?)

在这种情形下,goto 的方案似乎是完美的。但是 goto 如果遇到 C++,缺点体现出来了。下面这一段,现在是 do…while(0) 结构(只有一层嵌套,这种结构用在这里还算合理):

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,则需要把所有需要对象的定义全放到最前面来,不然 goto 会跳过他们的初始化,编译不过。但是,所有对象都放到最前面定义,又违反了即用即声明的规则,而且太多了也容易混淆。

最后,问题是,如果遇到 C++ 的、多层嵌套的,大家一般如何组织代码呢?

谢谢!

posted @ 2010-03-30 09:55 溪流 阅读(2734) | 评论 (26)编辑 收藏

我的观点可能有点激进,我觉得单件模式啥也不是,纯粹是个全局变量的贞洁牌坊而已。全局变量如果有必要,用就用了,何必伪装;如无必要,就算穿上单件模式的马甲,到头来也会搞得一片狼籍——随处可见GetInstance。

欢迎讨论~

posted @ 2010-03-29 10:58 溪流 阅读(4408) | 评论 (63)编辑 收藏

如题,不知道这样说是不是清楚了。

就是说,我们把新的类引入我们自己的工程后,

如果我们的工程打开了预编译头,就需要在.cpp加上#include <stdafx.h>,或者关闭本工程或者那个cpp的预编译头选项;如果我们的工程关闭了预编译头,就要确保那个.cpp里没有#include <stdafx.h>。

这样感觉很不爽。如果是用别人的成品库,我会觉得最好不改别人的代码,于是只好改工程里的选项,麻烦。如果自己写个类,也想让用的人(虽然通常是我自己)不要每次遇到这个麻烦。

有没有某种方法,在代码里加上类似:
#pragma precompileheader(close)
然后就不用管stdafx.h了,直接把文件加到工程里就好了

posted @ 2010-03-29 10:30 溪流 阅读(8695) | 评论 (17)编辑 收藏

网上提得较多的是 2K/XP 的句柄表,以及句柄分配算法。其中 Win2K 的句柄表在 _EPROCESS + 0x128 处,WinXP 在 _EPROCESS + 0x0c4 处。Vista 和 Win7 找遍了 Internet 没找到,于是只好下载符号表,装系统自己找。其实也就 dt _EPROCESS 一下了。Vista 在 _EPROCESS + 0x0dc 处,Win7 在 _EPROCESS + 0x0f4 处。以上均是 32 位系统下的地址。句柄分配算法在 Vista 和 Win7 中都没有变化,和 XP 一样(至少我的测试结果是这样的)。

小记一笔。明天继续看 64 位的。

==================================================
WinXP x64: 0x158
Vista x64: 0x160
Win7 x64: 0x200

posted @ 2009-11-17 19:18 溪流 阅读(761) | 评论 (0)编辑 收藏

陆陆续续搞了一个多月了,不过其实也就一开始的几天和最近几天在好好搞。

前两天把 Set、Map 写完的时候,突然发现我还是完全没有理解 STL 的迭代器所玩的花样。其中的类型萃取我看出来了,其余的都没有。我这里的迭代器是很土的,每个容器自顾自的(尽管很“巧合”有几个一样的接口)。

String 类我还想继续拓展功能。不过没想好的就是要不要有 Format 功能:如果没有,使用上或许偶尔会有一点点不方便(如果也不提供数值和字符串相互转换的函数的话);如果有,基本上不会去手工解释 %d、%s 之类的了,那么势必要用到 sprintf 之类的东西了,那么我的零依赖的设想就落空了。

MultiSet 和 MultiMap 有点儿倾向于不提供了,真有需求的到时候去 Set<List<T>>、Map<List<T>> 好了。

文件在此,点击下载(还没测试仔细,可能有不少 Bug,甚至可能某些函数有语法错误没测到,这点请谅解)

 

请各位给点意见~

posted @ 2009-11-09 22:01 溪流 阅读(2135) | 评论 (32)编辑 收藏
仅列出标题
共18页: First 10 11 12 13 14 15 16 17 18