<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    GDI Resource Leaks

    Using the techniques of Resource Management you can not only guarantee that your program will leak no memory, it will also leak no other resources. In a Windows program, GDI objects are an important class of resorces that should be protected using Resource Management techniques. In our own product, Code Co-op, we make extensive use of Resource Management, so we don't expect any leaks. Imagine our surprise when we found out that the Dispatcher, an important component of Code Co-op, turned out to be a voracious consumer of GDI resources.

    First of all, how do you find out such a thing? It turns out that the good old Task Manager can display GDI resources consumed by any running process. Use the Task Manager's menu item View>Select Columns and click the GDI Objects checkbox. A new column GDI Object will appear in the Processes view. If a number displayed in that column keeps increasing with time, you know there is a leak.

    By watching the Dispatcher process, we noticed that its allotment of GDI objects kept increasing every time the Dispatcher was checking email. Now we had to pinpoint exactly where in our code these resources were leaking. The way to do it is to count the GDI object usage before and after a given operation. The easiest (and most reliable) way to do it is to define an object which counts the resources first in its constructor and then in its destructor. If the numbers differ, it displays the difference. Here's the class we designed for this purpose.

    class DbgGuiLeak
    {
    public:
        explicit DbgGuiLeak ()
        {
            _guiResCount = ::GetGuiResources (::GetCurrentProcess (),
                                              GR_GDIOBJECTS);
        }
        ~DbgGuiLeak ()
        {
            int leaks = ::GetGuiResources (::GetCurrentProcess (), 
                                           GR_GDIOBJECTS) - _guiResCount;
            if (leaks != 0)
            {
                std::cout << "Gui Resources Leaked: " << leaks << std::endl;
            }
        }
    private:
        unsigned _guiResCount;
    };

    This kind of encapsulation has many advantages. Since we are using Resource Management religiously, all our resources are encapsulated in smart objects. The resource are automatically freed in those object's destructors. So if you define any smart object within the scope of the lifetime of your DbgGuiLeak, that object's destructor is guaranteed to be called before the destructor of DbgGuiLeak. C++ language guarantees this LIFO (last-in-first-out) ordering of lifetimes of stack objects. In other words, you won't see any spurious leaks resulting from the arbitrary order of resource deallocation. Compare the two examples below.

    In the first example, the leak counter will see no leaks (the destructor of gidObj will free its GDI resource before the destructor of leakCounter is called).

    {
        DbgGuiLeak leakCounter;
        SmartGdiObject gdiObj;
        // destructor of gdiObj
        // destructor of leakCounter
    }

    In the second example, a spurious leak will be displayed only because the implicit destructor of gidObj is called after the last explicit line of code is executed.

    { // bad code!
        unsigned gdiCount = ::GetGuiResources (::GetCurrentProcess (), 
                                               GR_GDIOBJECTS);
        SmartGdiObject gdiObj;
        int leaks = ::GetGuiResources (::GetCurrentProcess (),
                                       GR_GDIOBJECTS) - gdiCount;
        std::cout << "GDI Resources Leaked: " << leaks << std::endl;
        // destructor of gdiObj
    }

    Using a DLL

    To get access to Simple MAPI, we have to use mapi32.dll, a dynamic-load library located in the Windows system directory. A DLL is a resource, so it has to be managed (in the RM sense, not the .NET sense). We define a simple class, Dll, to take care of loading and freeing any DLL.

    To call a function defined in a DLL, you have to retrieve a function pointer either by ordinal or, as we do it here, by name. You not only have to know the name of the function, but, because you would like to call it, you must know its signature. The name is passed to Dll::GetFunction as a string argument, but the signature (which is a type) must be passed as a template argument. Internally, we cast the typeless pointer returned by the API to a function pointer of the appropriate signature.

    class Dll
    {
    public:
        Dll (std::string const & filename);
        ~Dll ();
    
        std::string const & GetFilename () const { return _filename; }
    
        template <class T>
        void GetFunction (std::string const & funcName, T & funPointer)
        {
            funPointer = static_cast<T> (GetFunction (funcName));
        }
    
    private:
        void * GetFunction (std::string const & funcName) const;
    
        std::string const _filename;
        HINSTANCE _h;
    };

    The constructor of Dll calls the LoadLibrary API. (Strictly speaking, we don't need to store the name of the DLL, but it might get handy in case we wanted to display meaningful error reports.)

    				
    						Dll::Dll
    				 (std::string const & filename)
        : _filename (filename),
          _h (::LoadLibrary (filename.c_str ()))
          
    {
        if (_h == 0)
            throw "Cannot load dynamic link library";
    }

    According to the rules of Resource Management, the destructor of the Dll object must free the DLL resource --it does it by calling FreeLibrary.

    				
    						Dll::~Dll
    				 ()
    {
        if (_h != 0)
        {
            ::FreeLibrary (_h);
        }
    }

    A (typeless) function pointer is retrieved from the DLL by calling the GetProcAddress API.

    void * Dll::GetFunction (std::string const & funcName) const
    {
        void * pFunction = ::GetProcAddress (_h, funcName.c_str ());
        if (pFunction == 0)
        {
            throw "Cannot find function in the dynamic link library";
        }
        return pFunction;
    }

    Incidentally, when calling the template member function Dll::GetFunction you don't have to explicitly specify the template argument. The complier will deduce it from the type of the second argument. You'll see an example soon.


    Simple MAPI

    For the purpose of our test, all we need from Simple MAPI is to log ourselves on and off. The result of a logon is a session. Since a session is associated with internal resources (including some GDI resources, which are of particular interest to us), it has to be managed in the RM sense. We define a SimpleMapi::Session to encapsulate this resource. It calls MAPILogon in its constructor and MAPILogoff in its destructor. The signatures of these two functions are typedef'd inside the class definition (that's the only place we will need them). Logon and Logoff are both very specific pointer-to-function types. Those typedefs not only specify the return- and argument types, but also the calling conventions (FAR PASCAL in this case).
    namespace SimpleMapi
    {
        class Session
        {
        public:
            Session (Dll & mapi);
            ~Session ();
        private:
            Dll        &_mapi;
            LHANDLE    _h;
        private:
            typedef ULONG (FAR PASCAL *Logon) (ULONG ulUIParam,
                                               LPTSTR lpszProfileName,
                                               LPTSTR lpszPassword,
                                               FLAGS flFlags,
                                               ULONG ulReserved,
                                               LPLHANDLE lplhSession);
            typedef ULONG (FAR PASCAL *Logoff) (LHANDLE session,
                                                ULONG ulUIParam,
                                                FLAGS flFlags,
                                                ULONG reserved);
        };
    }

    Here is the constructor of a Simple MAPI session. Note that we call the GetFunction template method of Dll without specifying the template parameter. That's because we are giving the compiler enough information through the type Logon (see the typedef above) of the logon argument. Once the (correctly typed) function pointer is initialized, we can make a call through it using the regular function-call syntax.

    namespace SimpleMapi
    {
        Session::Session (Dll & mapi)
            : _mapi (mapi)
        {
            Logon logon; // logon is a pointer to function
            _mapi.GetFunction ("MAPILogon", logon);
            std::cout << "Mapi Logon" << std::endl;
            ULONG rCode = logon (0, // Handle to window for MAPI dialogs
                                0,  // Use default profile
                                0,  // No password
                                0,  // No flags
                                0,  // Reserved -- must be zero
                                &_h); // Session handle
            if (rCode != SUCCESS_SUCCESS)
                throw "Logon failed";
        }
    }

    The destructor of SimpleMapi::Session calls MAPILogoff function thus closing the session and (hopefully!) freeing all the resources allocated on its behalf.

    namespace SimpleMapi
    {
        Session::~Session ()
        {
            Logoff logoff;
            _mapi.GetFunction ("MAPILogoff", logoff);
            std::cout << "Mapi Logoff" << std::endl;
            ULONG rCode = logoff (_h, 0, 0, 0);
    
            if (rCode != SUCCESS_SUCCESS)
                throw "Logoff failed";
        }
    }

    Testing for Leaks

    Here's our simple test. First we create a DbgGuiLeak object which will remember how many GUI resources are in use before the test starts. Then we create the Dll object which loads mapi32.dll into our address space. Finally, we create a SimpleMapi::Session which logs us into the MAPI subsystem.

    What happens next is all the destructors are called in the reverse order of creation. So first the destructor of SimpleMapi::Session logs us off and closes the session. Then the destructor of Dll frees the DLL. Finally, the destructor of DbgGuiLeak checks if any GUI resources have been leaked.

    void TestMapi ()
    {
        DbgGuiLeak leak;
        Dll mapi ("mapi32.dll");
        SimpleMapi::Session session (mapi);
        // Session destructor logs off
        // Dll destructor frees library
        // DbgGuiLeak destructor prints leak info
    }

    The main function of our program calls TestMapi three times, to get past any transcient effects (not that we think it's ok for the first logon to leak resources--but it wouldn't be such a disaster).

    int main ()
    {
        try
        {
            TestMapi ();
            TestMapi ();
            TestMapi ();
        }
        catch (char const * msg)
        {
            std::cerr << msg << std::endl;
        }
    }

    For completeness, these are the files you have to include in the test program for it to compile:

    #include <windows.h>
    #include <mapi.h>
    #include <iostream>

    Now you are ready to compile and run the test. Results may vary, depending on what email client is installed on your computer and what security updates you have downloaded from Microsoft. Here are some typical results for a well-updated system with Microsoft Outlook Express.

    C:\Test\MapiCons\Release>mapicons
    Mapi Logon
    Mapi Logoff
    Gui Resources Leaked: 16
    Mapi Logon
    Mapi Logoff
    Gui Resources Leaked: 2
    Mapi Logon
    Mapi Logoff
    Gui Resources Leaked: 2

    The first logon leaks 16 GDI resources, which is bad, but not terminally so. What is really bad is that every subsequent logon/logoff sequence leaks two additional resources. There is no excuse for this kind of shabby programming.

    Microsoft acknowledges having a similar problem with their Exchange Server

    from: http://www.relisoft.com/win32/gdileaks.html

    posted on 2006-10-02 09:59 weidagang2046 閱讀(742) 評(píng)論(0)  編輯  收藏 所屬分類: Windows

    主站蜘蛛池模板: 成人人免费夜夜视频观看| 国产成人综合亚洲AV第一页 | 日韩欧美亚洲中文乱码| 亚洲男人的天堂www| 国产a级特黄的片子视频免费| 久久久久久亚洲精品无码| 亚洲AV成人潮喷综合网| 成人五级毛片免费播放| 在线a免费观看最新网站| 亚洲av成人片在线观看| 亚洲伊人久久精品| 又大又黄又粗又爽的免费视频| 中国性猛交xxxxx免费看| 久久久国产精品亚洲一区| 我要看WWW免费看插插视频| 久久国产精品免费看| 亚洲国产美女精品久久久| 亚洲精品中文字幕无码AV| 久久久久久亚洲精品中文字幕| 亚洲免费网站观看视频| 人人公开免费超级碰碰碰视频| 亚洲男人天堂av| 在线a亚洲v天堂网2019无码| mm1313亚洲国产精品美女| 日韩精品视频免费在线观看| 国产成人A在线观看视频免费| 美女网站在线观看视频免费的| 亚洲乱码在线播放| 亚洲成年人免费网站| 亚洲熟妇无码久久精品| 亚洲精品视频免费观看| 曰曰鲁夜夜免费播放视频| 亚洲三级在线免费观看| 国产97视频人人做人人爱免费| 亚洲啪啪免费视频| 在线播放亚洲第一字幕| 亚洲日韩中文字幕在线播放| 国产亚洲一区二区精品| 亚洲日本在线观看| 亚洲伊人久久综合影院| 亚洲精品乱码久久久久久自慰|