• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            統(tǒng)計(jì)

            • 隨筆 - 24
            • 文章 - 0
            • 評論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            相冊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            2011年12月3日

            BSTR CComBSTR及ATL字符串轉(zhuǎn)換宏

                 摘要: 總結(jié)了BSTR,CComBSTR及ATL字符串轉(zhuǎn)換宏。  閱讀全文

            posted @ 2011-12-03 22:56 小蔥蘸醬 閱讀(5299) | 評論 (0)編輯 收藏

            2011年12月1日

            DOM編程中的接口引用計(jì)數(shù)

            在C++ DOM編程中,通過DOM對象接口的各種函數(shù)獲得的指向各種DOM對象的接口指針,其引用計(jì)數(shù)的變化是怎樣的?在程序中該如何使用(顯式或隱式調(diào)用AddRef)和釋放(顯式調(diào)用Release)接口指針呢?

            MSDN DOM sample 上的一個(gè)示例很好的說明了這些原則,下面對這篇文章的代碼做些總結(jié)說明。

            總結(jié)一下,DOM接口指針使用和釋放的規(guī)律為:
            1. 通過DOM對象接口函數(shù)(當(dāng)然也包括通過CoCreateInstance獲得的IXMLDOMDocument)獲得的接口指針,需要顯式的調(diào)用 Release釋放。這些函數(shù)如IXMLDOMDocument->createElement, IXMLDOMNode->appendChild。
            2. 對于手動賦值的接口指針(如代碼中的pElemA = pElemOut),需要顯式的在被賦值的指針上(pElemA)調(diào)用AddRef和Release,來增、減引用計(jì)數(shù)。
            3.其他的使用則無需增減引用計(jì)數(shù)。

            其他:
            Release()返回引用計(jì)數(shù)減1后的值,即如果原來引用計(jì)數(shù)為1,則Release()返回值為0.
            IXMLDOMNode->appendChild()的輸出參數(shù)接口指針在值上與輸入?yún)?shù)接口指針相同,因此二者指向同一個(gè)DOM對象。

            posted @ 2011-12-01 00:38 小蔥蘸醬 閱讀(1893) | 評論 (1)編輯 收藏

            2009年5月11日

            用C實(shí)現(xiàn)的一個(gè)基本COM接口IFoo(來自COM Programmer's Cookbook)——C實(shí)現(xiàn)COM接口系列2

            C實(shí)現(xiàn)COM接口系列1中實(shí)現(xiàn)的com接口IFoo與使用它的客戶耦合在一起,沒有實(shí)現(xiàn)在各自分離的模塊,因此不符合模塊化編程思想。本期添加類廠支持,以使接口的實(shí)現(xiàn)與接口的使用相分離。

            ---------------------------------------------------
            類廠的作用到底是什么?
            將接口的實(shí)現(xiàn)與客戶使用分離開來嗎?

            不盡然。使用CoCreateInstance,客戶可以完全不必知道類廠的存在,而創(chuàng)建組件,獲取組件實(shí)現(xiàn)的接口并使用。

            即COM庫可以完全拋開類廠的概念,而是提供一個(gè)這樣的函數(shù)原型:
            CoCreateObject(REFID rclsid,...,REFID riid,void **ppItf);
            用戶在調(diào)用的時(shí)候可以對riid提供IID_Unknown或者特定于該對象的一個(gè)接口,直接獲取該對象的IUnknown或特定的接口指針。

            可以看到,這正是CoCreateInstance所作的事情。

            1 類廠提供了間接創(chuàng)建類對象的方式:用戶可以先獲取并持有類廠接口指針,通過該指針?biāo)赶虻念悘S接口創(chuàng)建類對象。適用于需要?jiǎng)?chuàng)建多個(gè)(或重復(fù)創(chuàng)建)類對象的地方,減少了每次都要定位對象庫并把對象庫裝入內(nèi)存的開銷。
            2 類廠提供了保證組件庫留在內(nèi)存不被卸載出去的另一種方法:類廠接口函數(shù)LockServer。組件庫維護(hù)一個(gè)庫范圍計(jì)數(shù)器,只有該計(jì)數(shù)器為0時(shí),組件庫才允許自己被卸載出內(nèi)存。(與此相對,引用計(jì)數(shù)是類對象范圍的,通過該類實(shí)現(xiàn)的各個(gè)接口來維護(hù)。如果一個(gè)類對象的引用計(jì)數(shù)達(dá)到0,那么該對象占有的內(nèi)存就被釋放,該對象上的接口指針也不再有效。)
            除了調(diào)用LockServer鎖定組件庫以外,當(dāng)創(chuàng)建的組件個(gè)數(shù)大于0時(shí),組件庫也不能被卸載。也可以說,調(diào)用一次LockServer()的作用相當(dāng)于創(chuàng)建了一個(gè)組件。

            -----------------------------------------------------------------------
            客戶一側(cè):
            1 使用一個(gè)接口需要知道哪些信息?
            備選:
            接口IID
            類對象(類廠)CLSID(或ProgID)
            接口函數(shù)原型(參數(shù)個(gè)數(shù),類型,返回值)
            實(shí)現(xiàn)接口組件的線程模型(進(jìn)程內(nèi)、進(jìn)程外、遠(yuǎn)程)?
            類型庫typelib信息?

            服務(wù)一側(cè):
            2 實(shí)現(xiàn)一個(gè)組件和接口以供客戶調(diào)用,需要提供哪些東西?
            備選:
            所有客戶使用組件和接口所需的內(nèi)容
            額外的還有:


            --------------------------------------------------------------------
            為dll添加.def文件與直接在需要導(dǎo)出的函數(shù)定義處指定_declspec( dllexport )有區(qū)別嗎?如果有是什么區(qū)別?

            我發(fā)現(xiàn)在outdll.c中這樣指定:
            __declspec( dllexport ) HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)
            會產(chǎn)生編譯錯(cuò)誤:
            1>------ Build started: Project: outside, Configuration: Debug Win32 ------
            1>Compiling...
            1>outdll.c
            1>d:\outside-cf\outside\outdll.c(19) : error C2375: 'DllGetClassObject' : redefinition; different linkage
            1>        c:\program files\microsoft visual studio 8\vc\platformsdk\include\objbase.h(833) : see declaration of 'DllGetClassObject'
            1>Build log was saved at "file://d:\outside-cf\outside\Debug\BuildLog.htm"
            1>outside - 1 error(s), 0 warning(s)
            ========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

            c2375的解釋意思是出錯(cuò)的函數(shù)使用的鏈接指示符與之前聲明的不同。
            Compiler Error C2375
            'function' : redefinition; different linkage

            The function is already declared with a different linkage specifier.

            objbase.h中聲明了DllGetClassObject()函數(shù):
            STDAPI  DllGetClassObject(IN REFCLSID rclsid, IN REFIID riid, OUT LPVOID FAR* ppv);

            而使用.def文件就沒有問題。

            -----------------------------------------------------------------------------
            初次執(zhí)行結(jié)果:

            問題就是總有一個(gè)分配的內(nèi)存沒有釋放:

             


            根據(jù)打印出來的內(nèi)存地址可以判斷,應(yīng)該是先創(chuàng)建的類廠對象的內(nèi)存沒有釋放。
            檢查代碼,main()中并沒有忘記調(diào)用Release(pCF)釋放類廠對象。打印Release(pCF)的返回值,發(fā)現(xiàn)是1,即在類廠接口指針上少調(diào)用了一次Release,那么,究竟是哪里少的呢?

            main()函數(shù)中有關(guān)類廠對象引用計(jì)數(shù)的地方就是CoGetClassObject和Release(CreateInstance跟類廠自己的引用計(jì)數(shù)無關(guān)),這是一對增加引用計(jì)數(shù)和減少引用計(jì)數(shù)的對應(yīng)操作,所以,main()中應(yīng)該沒有問題。

            那么,就只有創(chuàng)建類廠對象的時(shí)候了。下面看一下類廠對象是如何創(chuàng)建的。
            首先,main調(diào)用CoGetClassObject,該函數(shù)就調(diào)用dll中的DllGetClassObject。由于是第一次調(diào)用(不考慮其他客戶使用該dll的情況),程序執(zhí)行到CreateClassFactory(...),該函數(shù)執(zhí)行完后,類廠對象的引用計(jì)數(shù)是1。

            由于創(chuàng)建成功,因此繼續(xù)向下執(zhí)行到QueryInterface,此時(shí),類廠對象的引用計(jì)數(shù)變成了2。然后,DllGetClassObject返回,com庫函數(shù)CoGetClassObject也應(yīng)該返回。注意,此時(shí)的類廠對象引用計(jì)數(shù)已經(jīng)是2了!

            因此,問題就出在這里。main調(diào)用一次CoGetClassObject后,類廠對象的引用計(jì)數(shù)是2,而不是我想向中的1。于是,后面調(diào)用一次Release也就當(dāng)然無法釋放掉類場對象了。

             

             

             1 HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)
             2 {
             3     *ppv = 0;
             4     if (IsEqualCLSID (rclsid, &CLSID_Outside))
             5     {
             6 
             7         if (!vpcfOutside)
             8 
             9         {
            10 
            11             HRESULT hr = CreateClassFactory (&CLSID_Outside, CreateOutside,
            12                                              &IID_IClassFactory, &vpcfOutside);
            13 
            14                 if (hr != NOERROR)
            15 
            16                     return hr;
            17         }
            18 
            19         return QueryInterface (vpcfOutside, riid, ppv);
            20 
            21     }
            22 
            23     return E_FAIL;
            24 }

             

             


            找到了原因,改正就很容易了。這里我覺得需要把DllGetClassObject作如下修改:

             1 HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)
             2 {
             3     *ppv = 0;
             4     if (IsEqualCLSID (rclsid, &CLSID_Outside))
             5     {
             6 
             7         if (!vpcfOutside)
             8 
             9         {
            10 
            11             HRESULT hr = CreateClassFactory (&CLSID_Outside, CreateOutside,
            12                                              &IID_IClassFactory, &vpcfOutside);
            13 
            14                 if (hr != NOERROR)
            15 
            16                     return hr;
            17 
            18     if(IsEqualIID(riid,&IID_IClassFactory))
            19     {
            20      *ppv = vpcfOutside;// Set *ppv to vpcfOutside directly instead of QueryInterface if first time creation
            21      return NOERROR;
            22     }
            23     else
            24     {
            25      Release(vpcfOutside);// Any interface requested (riid) other than IID_ClassFactory and IID_Unknown not support by class factory,
            26                           // call Release to free the memory.
            27      return E_FAIL;
            28     }
            29 
            30         }
            31 
            32         return QueryInterface (vpcfOutside, riid, ppv);
            33 
            34     }
            35 
            36     return E_FAIL;
            37 }

             

            修改后在執(zhí)行,內(nèi)存都正常釋放了。

            -------------------------------------------------------------------------------------------
            CreateClassFactory代碼說明

             1 HRESULT CreateClassFactory (REFCLSID rclsid,
             2     HRESULT (*pfnCreate)(IUnknown *, REFIID, void **), 
             3     REFIID riid, void **ppv)
             4 {
             5     ClassFactory *this;
             6     HRESULT hr;
             7     
             8     *ppv = 0;
             9     if (hr = Alloc (sizeof (ClassFactory), &this))
            10     return hr;
            11 
            12     this->icf.lpVtbl = &vtblClassFactory;
            13     this->cRef = 1;  // After this call, cRef==1
            14 
            15     this->pfnCreate = pfnCreate;
            16     
            17     hr = QueryInterface (&this->icf, riid, ppv);  // After this call, cRef==2
            18     Release (&this->icf);  // Corresponds to "this->cRef = 1", ater this call, cRef==1
            19 
            20     return hr;
            21 }

             

            可以看到,兩行代碼的效果是對引用計(jì)數(shù)增1及減1,這兩行代碼執(zhí)行后,對引用計(jì)數(shù)的影響互相抵消,等于沒有改變引用計(jì)數(shù)。那么,把這兩行同時(shí)注釋掉,是不是可以呢?
            我的回答是:在本例中可以。因?yàn)檫@兩行代碼之間的QueryInterface總是可以執(zhí)行成功的(因?yàn)槭怯肐DD_ClassFactory來調(diào)用該函數(shù)的)。所以,即便把這兩行代碼同時(shí)注釋掉,CreateClassFactory執(zhí)行結(jié)束后,類廠對象的引用計(jì)數(shù)也增了1,以后調(diào)用Release就可以釋放掉類廠對象占用的內(nèi)存。
            但是,如果CFQueryInterface的代碼編寫中除了錯(cuò)誤,比如,像這樣寫:

             1 static HRESULT CFQueryInterface (IClassFactory *pcf, REFIID riid, void **ppv)
             2 {
             3     ClassFactory *this = IMPL (ClassFactory, icf, pcf);
             4 
             5     if (IsEqualIID (riid, &IID_IUnknown) ||
             6 //            IsEqualIID (riid, &IID_IClassFactory))   // Comment out this condition to create an error
             7         *ppv = &this->icf;
             8     else
             9     {
            10         *ppv = 0;
            11         return E_NOINTERFACE;
            12     }
            13 
            14     AddRef ((IClassFactory *)*ppv);
            15 
            16     return NOERROR;
            17 }


            那么,這兩行代碼之間的QueryInterface就會執(zhí)行出錯(cuò),那么類廠對象占用的內(nèi)存就永遠(yuǎn)沒有機(jī)會釋放了。
            也就是說,AddRef和Release雖然在作用上對引用計(jì)數(shù)來說相互抵消,但Release函數(shù)提供了釋放對象內(nèi)存的機(jī)會(當(dāng)引用計(jì)數(shù)為0時(shí)),如果不成對的調(diào)用他們,也就失去了管理對象內(nèi)存(釋放對象占用的內(nèi)存)的機(jī)會。

            ---------------------------------------------------------------------------
            組件庫outside文件說明:
             IFoo.h      IFoo接口聲明
             outside.c   組件對象、IFoo接口實(shí)現(xiàn)
             cf.c        類廠對象、IClassFactory接口實(shí)現(xiàn)
             outdll.c    組件庫導(dǎo)出函數(shù)實(shí)現(xiàn)
             outside.def 組件庫模塊定義文件,導(dǎo)出函數(shù)聲明
             outside.reg 組件庫注冊文件

            ----------------------------------------------------------------------------
            源碼: outside-cf

            posted @ 2009-05-11 16:28 小蔥蘸醬 閱讀(1150) | 評論 (0)編輯 收藏

            2009年4月29日

            用C實(shí)現(xiàn)的一個(gè)基本COM接口IFoo(來自COM Programmer's Cookbook)——C實(shí)現(xiàn)COM接口系列1

            用C實(shí)現(xiàn)的一個(gè)基本COM接口IFoo(來自COM Programmer's Cookbook)


            把該文中實(shí)現(xiàn)的代碼整理匯總到一個(gè)項(xiàng)目中。目前只是實(shí)現(xiàn)到一個(gè)中間階段,重點(diǎn)在說明COM接口的實(shí)現(xiàn)原理,還沒有包含類廠的部分。以后還需陸續(xù)添加類廠等高級功能。

            文件組成:
            ifoo.h         COM接口IFoo,接口ID IID_IFoo 聲明文件。
            outside.c      COM接口實(shí)現(xiàn)。這里實(shí)現(xiàn)IFoo的是一個(gè)結(jié)構(gòu)體COutside.
            util.h         一些宏定義、全局函數(shù)、變量聲明文件。
            main.c         筆者為實(shí)現(xiàn)項(xiàng)目添加的文件。提供main函數(shù)、內(nèi)存管理函數(shù)Alloc,Free的實(shí)現(xiàn)(封裝C運(yùn)行庫函數(shù)malloc和free.)、接口ID定義。

            COM接口到底是什么?
            COM接口是一個(gè)指向虛函數(shù)表的指針。通過這個(gè)指針可以訪問內(nèi)存中某處的各個(gè)功能塊,執(zhí)行預(yù)定義的功能,完成用戶的任務(wù)。這些功能塊以函數(shù)的形式存在(想不出還有其他形式:))并被調(diào)用。它們有一個(gè)共同點(diǎn):都包含一個(gè)指針參數(shù),指向這些功能要操作的數(shù)據(jù)地址。在C++中,這個(gè)地址就是對象的首地址,也就是類成員函數(shù)中隱含的this指針。在C函數(shù)中并沒有這種現(xiàn)成的便利,因此代碼實(shí)現(xiàn)中在接口定義時(shí)仍使用了接口指針(HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **)),而在接口函數(shù)實(shí)現(xiàn)時(shí)根據(jù)結(jié)構(gòu)體布局結(jié)構(gòu),從這個(gè)接口指針推算得到對象實(shí)例指針。

            typedef struct IFoo
            {
             struct IFooVtbl * lpVtbl;
            } IFoo;
            typedef struct IFooVtbl IFooVtbl;
            struct IFooVtbl
            {
             
             HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **) ;
             ULONG (__stdcall * AddRef)    (IFoo * This) ;
             ULONG (__stdcall * Release)   (IFoo * This) ;

             HRESULT (__stdcall * SetValue)         (IFoo * This,  int) ;
             HRESULT (__stdcall * GetValue)         (IFoo * This,  int *) ;
            };

            COM接口的要求:

            每一個(gè)COM接口(指向的虛函數(shù)表)的頭三個(gè)函數(shù)必須是IUnknown接口的函數(shù):QueryInterface,AddRef和Release。在C++中,稱為從IUnknown接口繼承。
            對于調(diào)用QueryInterface響應(yīng)查詢IID_IUnknwon得到的接口指針值,同一個(gè)對象實(shí)現(xiàn)的所有接口必須相同。這是判斷兩個(gè)COM對象是否是同一個(gè)對象的標(biāo)準(zhǔn)。

             

            宏定義“#define IUNK_VTABLE_OF(x) ((IUnknownVtbl *)((x)->lpVtbl))“說明

            在預(yù)處理輸出文件main.i中可以找到IUnknownVtbl和IFooVtbl的聲明:
                typedef struct IUnknownVtbl
                {
                   
                   
                    HRESULT ( __stdcall *QueryInterface )(
                        IUnknown * This,
                         const IID * const riid,
                         void **ppvObject);
                   
                    ULONG ( __stdcall *AddRef )(
                        IUnknown * This);
                   
                    ULONG ( __stdcall *Release )(
                        IUnknown * This);
                   
                   
                } IUnknownVtbl;
               
                struct IUnknown
                {
                     struct IUnknownVtbl *lpVtbl;
                };   
               

            struct IFooVtbl
            {
             
             HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **) ;
             ULONG (__stdcall * AddRef)    (IFoo * This) ;
             ULONG (__stdcall * Release)   (IFoo * This) ;

             HRESULT (__stdcall * SetValue)         (IFoo * This,  int) ;
             HRESULT (__stdcall * GetValue)         (IFoo * This,  int *) ;
            };

            該宏定義的作用就是把IFoo接口中的IFooVtbl類型的指針拿出來((x)->lpVtbl)),并強(qiáng)制轉(zhuǎn)換((IUnknownVtbl *))成IUnknownVtbl。
            “強(qiáng)制轉(zhuǎn)換”的結(jié)果是什么呢?是怎么做到的呢?
            很明顯,結(jié)果就是得到的指針不再是IFooVtbl *類型,而是變成了IUnknownVtbl *類型。至于做法,系統(tǒng)應(yīng)該記錄每一個(gè)變量、表達(dá)式的類型。當(dāng)進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí),就(臨時(shí)地)修改其類型為轉(zhuǎn)換到的類型。
            同理,QueryInterface, AddRef, Release宏定義中的(IUnknown *)也是這種用法。

            可以看到,宏“IUNK_VTABLE_OF“的作用是供宏QueryInterface,宏AddRef,宏Release引用,把IFooVtbl *類型轉(zhuǎn)換為IUnknownVtbl *類型,最終達(dá)到調(diào)用IUnknownVtbl中定義的三個(gè)QueryInterface,AddRef,Release函數(shù)。

            那么,這種大費(fèi)周章的目的是什么呢?為什么不以IFooVtbl中三個(gè)函數(shù)的定義形式(不通過強(qiáng)制轉(zhuǎn)換來轉(zhuǎn)換成必須的類型),直接調(diào)用IFooVtbl中定義的函數(shù)呢?雖然強(qiáng)制轉(zhuǎn)換在參數(shù)值上并不會造成改變,最終調(diào)用的也是IFooVtbl定義的函數(shù)(FooQueryInterface,FooAddRef,FooRelease)。

            為什么一定要通過IUnknown接口指針調(diào)用這三個(gè)函數(shù)呢?修改QueryInterface宏定義如下:
            #define QueryInterface(pif, iid, pintf) \
             (((pif)->lpVtbl)->QueryInterface(pif, iid, (void **)(pintf)))
            即通過IFoo接口指針來調(diào)用由IUnknown引入的函數(shù),有什么不對的地方嗎?

            試驗(yàn)表明,將QueryInterface宏定義如下也可以編譯通過,執(zhí)行起來也沒有出現(xiàn)任何異常。
            #define QueryInterface(pif, iid, pintf) \
             (((pif)->lpVtbl)->QueryInterface(pif, iid, (void **)(pintf)))

             

            對于IUnknown接口的三個(gè)函數(shù),調(diào)用時(shí)傳遞的參數(shù)是IUnknown *類型(見QueryInterface, AddRef, Release宏定義),而函數(shù)定義中(FooQueryInterface, FooAddRef, FooRelease)聲明的參數(shù)是IFoo *類型,這種不一致的情況是怎么出現(xiàn)的?這種不一致不會有問題嗎?

            這種不一致的產(chǎn)生是由于從不同的角度看待引起的。如果從IUnknown接口來看,那么接口函數(shù)中的第一個(gè)參數(shù)類型就是IUnknown *;如果從IFoo來看,那么第一個(gè)參數(shù)的類型就是IFoo *。

            這種不一致性只是針對于編譯器對于類型的編譯要求有意義的,在接口實(shí)現(xiàn)及使用時(shí),傳遞給lpVtbl->QueryInterface, lpVtbl->AddRef,lpVtbl->Release的第一個(gè)參數(shù)在值上都是相同的,都是實(shí)現(xiàn)該接口的內(nèi)存地址(在本例中是COutside對象的首地址)。

             

            一些語法現(xiàn)象回顧

            函數(shù)指針變量定義、賦值及調(diào)用。
            HRESULT (__stdcall * pQI)   (IFoo * This,  const IID * const, void **) ;
            定義一個(gè)函數(shù)指針變量pQI,該變量指向“返回HRESULT,取3個(gè)參數(shù)分別為類型IFoo *,const IID * const, void **”的函數(shù)。

            typedef HRESULT (__stdcall * QIType)   (IFoo * This,  const IID * const, void **) ;
            定義一個(gè)函數(shù)指針類型,該類型的指針指向“返回HRESULT,取3個(gè)參數(shù)分別為類型IFoo *,const IID * const, void **”的函數(shù)。

            HRESULT __stdcall QueryInterface(IFoo * This,  const IID * const, void **) ;//函數(shù)聲明示例
            pQI = 0;   // 函數(shù)指針賦值,0表示不指向任何函數(shù)。
            pQI = QueryInterface;  // 函數(shù)指針賦值,pQI指向QueryInterface。
            pQI = &QueryInterface; // 與上面等價(jià)。

            QueryInterface(&this->ifoo, riid, ppv);  // 使用函數(shù)名直接調(diào)用
            pQI(&this->ifoo, riid, ppv);             // 函數(shù)指針調(diào)用
            (*pQI)(&this->ifoo, riid, ppv);          // 第二種函數(shù)指針調(diào)用方式


            宏定義、展開規(guī)則
            對于宏,一直有一種霧里看花的感覺,似乎很隨意,怎么來都行,比如:
            #define AddRef(pif) \
             (IUNK_VTABLE_OF(pif)->AddRef((IUnknown *)(pif)))

            宏定義應(yīng)該是可以嵌套的,即宏定義的“內(nèi)容“中還可以包含(嵌套)宏,如本例,“IUNK_VTABLE_OF”就是嵌套宏。在展開的時(shí)候,將嵌套的宏也一并展開(替換成定義的內(nèi)容),直到不再有宏為止。
            那么就有兩個(gè)疑問:
            1。如果被嵌套的宏包含(直接或間接)定義的宏,那么展開就沒完沒了,死循環(huán)了。
            2。如果定義的內(nèi)容中有跟定義的宏同名的字符串(比如上面的例子IUNK_VTABLE_OF),那么怎么區(qū)分這同名的東東是嵌套的宏(需要展開),還是一般的字符串(不需要展開)?


            函數(shù)調(diào)用規(guī)范約定、main函數(shù)調(diào)用規(guī)范。

            一開始把幾個(gè)文件匯總到項(xiàng)目里時(shí),編譯通不過,錯(cuò)誤提示大致意思是,不能把一種調(diào)用規(guī)范的函數(shù)指針轉(zhuǎn)換成另一種調(diào)用規(guī)范的函數(shù)指針。后來把調(diào)用規(guī)范改為   /Gz(__stdcall),編譯為(Compile As)改為/TC(Compile As C Code)就好了。

            想來是對于.c文件,編譯器缺省使用的是__cdecl,而IFoo中的接口宏定義在win32下展開成了__stdcall,所以出現(xiàn)了矛盾。而使用/Gz強(qiáng)制未聲明調(diào)用規(guī)范的函數(shù)使用__stdcall,實(shí)現(xiàn)就與聲明一致了。


            (size_t)&(((s *)0)->m)

            c++程序員也許都知道,訪問地址“0”處的成員是一大忌,會造成GP。然而,取地址“0”處的成員的地址,卻是個(gè)合法的操作。雖然地址“0”處并沒有什么內(nèi)容,但是,如果在地址0處存放一個(gè)內(nèi)容,那么該內(nèi)容中的成員也是有地址的。本例中正是巧妙地利用這種方法,從接口地址計(jì)算得出實(shí)現(xiàn)該接口的實(shí)例地址,進(jìn)而訪問實(shí)例的內(nèi)部變量。

            ------------------------------------------------------------------------------------
            2009年5月6日
            附上源碼:/Files/gracelee/outside.zip

            代碼執(zhí)行結(jié)果:

            posted @ 2009-04-29 12:08 小蔥蘸醬 閱讀(1987) | 評論 (3)編輯 收藏

            2009年2月19日

            有關(guān)include guard的一個(gè)好帖子(收藏)

            終于弄清楚了原來說的同一個(gè)頭文件不能被兩次或兩次以上包含是針對同一個(gè)源文件而言的。借用80后的流行語,真是漢哪!

            原貼地址:http://www.keil.com/forum/docs/thread10237.asp

            作者 Per Westermark

            The

            #ifndef xx
            #define xx
            ...
            #endif
            

            method is to make sure that a header file isn't included more than once from the same c file.

            You can not - and normally don't want to - stop multiple c files from including the same header file.

            A header file is included because:
            1) You have specifically added a line #include "xx" or #include <xx> in the source file. Don't do that unless you want the file to be included :)
            2) You are including one header file, that it it's turn (one or more steps away) includes another header file. But a header file should only contain a recursive #include if it really needs that other file for some declarations. Hence, you need to include it.

            What does this mean?

            If the header file must be seen by multiple source files, you can't use it to allocate global variables, since the linker would then complain about multiple sets of global variables with the same name. This can be solved with the following:

            //globals.h
            #ifndef _GLOBALS_H
            #define _GLOBALS_H
            #if defined MAIN
            #define EXTERN
            #else
            #define EXTERN extern
            #endif
            ...
            EXTERN int my_global_variable;
            #endif // _GLOBALS_H
            

            // main.c
            #define MAIN
            #include "globals.h"
            ...
            // misc.c
            #include "globals.h"
            ...
            

            In this case, only the inclusion in main.c will result in an "allocation" of global variables, because the #define EXTERN will be empty. All other source files that includes "globals.h" will just see the type information for the global variables.

            posted @ 2009-02-19 09:04 小蔥蘸醬 閱讀(994) | 評論 (2)編輯 收藏

            2009年1月16日

            自動類型轉(zhuǎn)換——操作符重載

            用戶自定義類(class)類型可以當(dāng)作系統(tǒng)內(nèi)建類型(build-in type)來處理,對這一點(diǎn)我一直很驚奇,也很迷惑,特別是在類定義、繼承關(guān)系很復(fù)雜的時(shí)候,要找到來龍去脈真的很抓頭。最近工作中碰到這一塊的東西,順便借這個(gè)機(jī)會澄清一些概念。

            看下面代碼:

             1 class CBase {
             2 public:
             3     CBase()
             4     {
             5         cout << "CBase constructor()" << endl;
             6     }
             7     virtual ~CBase()
             8     {
             9         cout << "CBase destructor()" << endl;
            10     }
            11     
            12     operator long()
            13     {
            14         cout << "CBase::operator long()" << endl;
            15         return 0;
            16     }
            17     
            18     operator char()
            19     {
            20         cout << "CBase::operator char()" << endl;
            21         return 'a';
            22     }
            23 }
            24 ;
            25 class CDerived:public CBase
            26 {
            27 public:
            28     CDerived()
            29     {
            30          cout << "CDerived constructor()" << endl;
            31     }
            32              
            33     virtual ~CDerived()
            34     {
            35         cout << "CDerived destructor()" << endl;
            36     }
            37              
            38     operator long()
            39     {
            40         cout << "CDerived::operator long()" << endl;
            41         //return CBase::operator long();
                       return *((CBase *)this);//change the above code to this looks better
            42     }
            43              
            44 };
            45 
            46 void main()
            47 {
            48     CDerived d;
            49     long lTmp = d;
            50     cout << "lTmp=" << lTmp << endl;
            51     char cTmp = d;
            52     cout << "cTmp=" << cTmp << endl;
            53 
            54 }


            由于定義了操作符重載CDerived::operator long() 和CBase::operator long(),49行得以編譯通過。同理,定義了CBase::operator char(),51行可以編譯。

            執(zhí)行結(jié)果為:
            CBase constructor()
            CDerived constructor()
            CDerived::operator long()
            CBase::operator long()
            lTmp=0
            CBase::operator char()
            cTmp=a
            CDerived destructor()
            CBase destructor()

            這里涉及到的概念主要有:
            1 類成員操作符重載(使得用戶定義類型轉(zhuǎn)換為內(nèi)建類型成為可能。對于用戶定義類型之間的轉(zhuǎn)換,還可以通過構(gòu)造函數(shù)的方式進(jìn)行)
            2 自動類型轉(zhuǎn)換。自動類型轉(zhuǎn)換發(fā)生的情況有以下幾種:
            函數(shù)調(diào)用時(shí)傳遞的實(shí)參類型與函數(shù)聲明中指定的參數(shù)類型不匹配
            函數(shù)返回的對象類型與函數(shù)聲明中指定的返回類型不匹配
            表達(dá)式中操作數(shù)的類型不一致(這正是上面例子中的情況)

            有意思的是,即使不定義CBase::operator char(),上面的51行仍能通過,真得感嘆編譯器的聰明才智了,或許只是編譯器source中多了一些的if{}else{}呢?

            posted @ 2009-01-16 15:51 小蔥蘸醬 閱讀(2047) | 評論 (0)編輯 收藏

            2008年10月31日

            回歸

            看看最新的隨筆,竟然有一年多沒來了,自己設(shè)的博客標(biāo)題,真是對自己的諷刺。

            很久以前寫的帖子,現(xiàn)在看到還有人在回復(fù),有點(diǎn)感動,也受到一點(diǎn)鼓舞:如果有什么是永恒的話,那么文字當(dāng)屬一種吧,雖然我蒼白的語言會在網(wǎng)絡(luò)浩瀚而五光十色的信息中被深埋而無人注意,畢竟也是一種曾經(jīng)存在過,發(fā)生過的見證。

            記錄自己的心情,讓生命在這種書寫中找到歸屬和安慰,這就是博客于我的魅力吧。

            posted @ 2008-10-31 13:24 小蔥蘸醬 閱讀(380) | 評論 (0)編輯 收藏

            2007年8月3日

            VBScript寫的計(jì)算異或的一個(gè)小工具

            工作中經(jīng)常會需要計(jì)算一大串字節(jié)的異或,用計(jì)算器手工一個(gè)一個(gè)的輸入太累人了,碰巧這兩天在關(guān)注VBScript,于是就想到寫了這個(gè)工具。這是我第一次用VB,代碼很簡單,希望能對你有用。

            '==========================================================================
            '
            '
            '
             NAME: calc_xor.vbs
            '
            '
             COMMENT: Calculates the result of 'xor' all elements in the input
            '
            '
            ==========================================================================

            Option Explicit

            Dim strInput

            'Promt for string to search for in log files
            strInput = InputBox("Enter data to calc on.","calc xor","")

            if strInput = "" then
                wscript.quit
            end If

            'MsgBox(Len(strInput))

            Dim arrayBytes()
            ReDim arrayBytes(Len(strInput)/2)

            Dim i,nBytes
            Dim chHalf1,chHalf2,chWhole
            = 1
            nBytes 
            = 0

            Do While (i < Len(strInput))
            'Skip spaces between elements
            Do While Asc(Mid(strInput,i,1)) = 32 'space
                i = i + 1
            Loop 

                chHalf1 
            = Asc(Mid(strInput,i,1))
                chHalf2 
            = Asc(Mid(strInput,i+1,1))
                
            'Check and convert first half

                
            If chHalf1 >= 48 And chHalf1 <= 57 Then
                    chHalf1 
            = chHalf1 - 48
                
            ElseIf chHalf1 >= 65 And chHalf1 <= 70 Then
                    chHalf1 
            = chHalf1 - 65 + 10
                
            ElseIf chhalf1 >= 97 And chHalf1 <= 104 Then
                    chHalf1 
            = chHalf1 - 97 + 10
                
            Else
                    
            MsgBox("invalid character")
                    wscript.quit
                
            End If
                
                
            ' Check and convert the second half
                If chHalf2 >= 48 And chHalf2 <= 57 Then
                    chHalf2 
            = chHalf2 - 48
                
            ElseIf chHalf2 >= 65 And chHalf2 <= 70 Then
                    chHalf2 
            = chHalf2 - 65 + 10
                
            ElseIf chHalf2 >= 97 And chHalf2 <= 104 Then
                    chHalf2 
            = chHalf2 - 97 + 10
                
            Else
                    
            MsgBox("invalid character")
                    wscript.quit
                
            End If
                
                
            ' Combine the first and second halves together to form a whole byte
                chWhole = chHalf1 * 16 + chHalf2
                arrayBytes(nBytes) 
            = chWhole
                i 
            = i + 2
                nBytes 
            = nBytes + 1
            Loop

            'MsgBox(CStr(nBytes) + " bytes all together")

            Dim WshSHell
            set WshShell = CreateObject("WScript.Shell")
            WshShell.Run(
            "calc")
            WScript.Sleep(
            100)
            WshShell.AppActivate(
            "Calculator")
            WScript.Sleep(
            100)

            WshShell.SendKeys(
            "{F6}"'Change to Decimal
            '
            WshShell.SendKeys("{F4}") 'Change to single byte
            WScript.Sleep(100)
            WshShell.SendKeys(
            "0")
            For i =1 to nBytes
                WshShell.SendKeys(
            "{^}")
                WScript.Sleep(
            100)
                WshShell.SendKeys(arrayBytes(i
            -1))
                WScript.Sleep(
            100)
                WshShell.SendKeys(
            "{=}")
                WScript.Sleep(
            100)
            Next

            WshShell.SendKeys(
            "{F5}"'Change to Hex for easy recognition



            posted @ 2007-08-03 13:05 小蔥蘸醬 閱讀(1275) | 評論 (0)編輯 收藏

            2007年7月31日

            用sprintf格式化字符串

            寫代碼時(shí)經(jīng)常會用到用sprintf格式化某個(gè)字符串,比如:
            sprintf(mess,"This is field 1,Field 2,Field 3\n");

            當(dāng)這樣的域很多的時(shí)候,一行放不下,為了便于閱讀,需要把它們分成幾行:
            sprintf(mess,"This is filed 1,\
                Field 2,\
                Field 3,\
                ...
                Field n\n");

            但是,這樣帶來個(gè)問題,就是格式化后的mess的各個(gè)域之間就產(chǎn)生了不想要的字符(如空格等,使用UE可以清楚的看到),原因是由于使用了續(xù)行符"\"(line-continuation character),而不用續(xù)行符又無法通過編譯(C2001)。

            這時(shí)可以用雙引號來把各個(gè)域分隔開,這樣就既解決了可讀性的問題,又解決了編譯問題:
            sprintf(mess,"This is filed 1,"
                "Field 2,"
                "Field 3,"
                ...
                "Field n\n");


            posted @ 2007-07-31 16:43 小蔥蘸醬 閱讀(2520) | 評論 (2)編輯 收藏

            2007年6月19日

            [隨見隨記]living的兩種詞性用法

            1.noun 生活,生計(jì)

            What Londoners do to make a living have changed considerably since the 19th century.

            19世紀(jì)以來,倫敦人謀生的方式發(fā)生了很大改變。

            幾個(gè)常見的詞組:
            living room
            living condition


            2.adj  活的

            London is itself a living museum with thousand years of history and culture.

            倫敦以其幾千年的歷史和文化,其本身就是一座博物院。

            “living”作為形容詞用就有必要提一下“live”。“live”作形容詞也有“活的”意思,但感覺上這里似乎不能替換“living”,不知道對不對。

            posted @ 2007-06-19 09:58 小蔥蘸醬 閱讀(763) | 評論 (0)編輯 收藏
            僅列出標(biāo)題  下一頁
            久久精品青青草原伊人| 国产亚洲婷婷香蕉久久精品| 久久99精品久久久久子伦| 中文字幕乱码人妻无码久久| 91久久香蕉国产熟女线看| 国产精品美女久久久久| 久久久无码人妻精品无码| 亚洲中文字幕无码久久2020 | 一本大道久久香蕉成人网| 亚洲午夜久久影院| 91精品国产色综久久 | 狠狠色噜噜色狠狠狠综合久久| 亚洲人成网站999久久久综合| 热久久国产欧美一区二区精品| 久久国产视频99电影| 欧美精品丝袜久久久中文字幕 | 久久99精品久久久大学生| 亚洲精品乱码久久久久66| 久久久精品国产sm调教网站| 久久偷看各类wc女厕嘘嘘| 91精品国产高清久久久久久io| 成人妇女免费播放久久久| 99久久精品免费看国产| 久久涩综合| 亚洲综合精品香蕉久久网| 国内精品九九久久久精品| 国产精品无码久久四虎| 亚洲国产精品无码久久久久久曰| 综合久久给合久久狠狠狠97色| 久久中文字幕人妻熟av女| 麻豆一区二区99久久久久| 国产精品久久毛片完整版| 久久久国产精品| 国产精品一区二区久久国产| 久久国产精品一区| 久久人人妻人人爽人人爽| 久久精品这里只有精99品| 欧美喷潮久久久XXXXx| 久久九九免费高清视频| 国产婷婷成人久久Av免费高清| 精品久久久久久久久久中文字幕 |