🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.nativeapi
7 messages
7 total messages Started by Kris Thu, 13 Mar 2008 08:56
Cleaning up after using GetDisplayNameOf()
#474
Author: Kris
Date: Thu, 13 Mar 2008 08:56
47 lines
2312 bytes
Hi,

I'm using GetDisplayNameOf() to obtain the (localized) display name of 
the MyDocuments folder (i.e. CSIDL_PERSONAL).

This was somewhat confusing to implement, but I managed to get it 
working properly. However, I am unsure if I'm doing a proper clean-up 
after I'm finished ?

I'm especially concerned about the STRRET info at 
http://msdn2.microsoft.com/en-us/library/bb759820(VS.85).aspx that states:

pOleStr:
A pointer to the string. This memory must be allocated with 
CoTaskMemAlloc. It is the calling application's responsibility to free 
this memory with CoTaskMemFree when it is no longer needed.

Do I have to take care of freeing that memory, or is someone else taking 
care of freeing the STRRET.pOleStr (e.g. pDesktopFolder) ?

Please feel free to mention any other (clean-up) issues that you might 
notice with the code sample below.

Thanks,

Kris

CoInitialize(0) ;
LPITEMIDLIST pidl;
STRRET sName;
LPSHELLFOLDER pDesktopFolder;
if(SUCCEEDED(SHGetSpecialFolderLocation(NULL,CSIDL_PERSONAL,&pidl)))
{
if(SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
{
if(SUCCEEDED(pDesktopFolder->GetDisplayNameOf(pidl,SHGDN_INFOLDER,&sName)))
{
// Probably safe to assume STRRET_WSTR is set in this specific case
// So simply use the pOleStr member without any additional checks
WideString DisplayName=sName.pOleStr;
DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%s]",String(DisplayName).c_str());
}
pDesktopFolder->Release();
}
CoTaskMemFree((void*)pidl);
}
CoUninitialize();
Re: Cleaning up after using GetDisplayNameOf()
#475
Author: Darko Miletic
Date: Thu, 13 Mar 2008 13:23
138 lines
5091 bytes
> Do I have to take care of freeing that memory, or is someone else taking 
> care of freeing the STRRET.pOleStr (e.g. pDesktopFolder) ?

Yes you do as stated in documentation.

> 
> Please feel free to mention any other (clean-up) issues that you might 
> notice with the code sample below.
> 
> Thanks,
> 
> Kris


First of all if your app is VCL CoInitialize is not needed. By default 
COM is initialized in VCL app.

I would never assume anything in API. You should always check uType to 
see what is the appropriate action. Also for safety reasons it would be 
good to use RAII idiom to handle resource cleanup properly.

Here is my version of the same thing. It is longer and uses only 
standard C and C++ library together with Windows API hence it is console 
app.

The only thing that can be removed in VCL app is use of ole_init.

//---------------------------------------------------------------------------
#include <windows.h>
#include <shlobj.h>
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>

#pragma comment(lib,"shell32.lib")

//---------------------------------------------------------------------------
struct ole_init {
   ole_init(void) : _isinit(false) {
     _isinit = SUCCEEDED(::CoInitialize(NULL));
   }
   ~ole_init(void) {
     if (_isinit) {
       _isinit = false;
       ::CoUninitialize();
     }
   }
   bool init_ok(void) const { return _isinit; }
private:
   bool _isinit;
};

std::wstring string2wstring (const std::string& str) {
   std::wstring result;
   if (!str.empty()) {
     std::vector<wchar_t> buffer(str.size());
     std::size_t res = std::mbstowcs(&buffer[0],str.c_str(),str.size());
     if (res > 0) {
      result.assign(buffer.begin(),buffer.end());
     }
   }
   return result;
}

struct pidl_holder {
  explicit pidl_holder(int folder) : _pidl(NULL) {
     LPITEMIDLIST ppidl = NULL;
     HRESULT hr = ::SHGetSpecialFolderLocation(NULL,folder,&ppidl);
     if (SUCCEEDED(hr)) {
       _pidl = ppidl;
     }
  }
  ~pidl_holder(void) { release(); }
  LPITEMIDLIST ptr(void) {return _pidl;}
private:
   LPITEMIDLIST _pidl;
   void release(void) {
     if (NULL != _pidl) {
        ::CoTaskMemFree(_pidl);
        _pidl = NULL;
     }
   }
};

struct shdesktop_folder {
   shdesktop_folder(void) : _desktop_folder(NULL) {
     LPSHELLFOLDER df = NULL;
     if (SUCCEEDED(::SHGetDesktopFolder(&df))) {
       _desktop_folder = df;
     }
   }
   ~shdesktop_folder(void) {
     if (NULL != _desktop_folder) {
      _desktop_folder->Release();
      _desktop_folder = NULL;
     }
   }
   LPSHELLFOLDER ptr(void) { return _desktop_folder; }
private:
   LPSHELLFOLDER _desktop_folder;
};

std::wstring get_display_name(void) {
   ole_init ol;
   std::wstring result;
   if (ol.init_ok()) {
     pidl_holder pidlobj(CSIDL_PERSONAL);
     LPITEMIDLIST pidl = pidlobj.ptr();
     if (NULL != pidl) {
         shdesktop_folder deskFolder;
         LPSHELLFOLDER pDesktopFolder = deskFolder.ptr();
         if (NULL != pDesktopFolder) {
            STRRET sname = {};
            HRESULT hr = pDesktopFolder->GetDisplayNameOf(  pidl
                                                           ,SHGDN_INFOLDER
                                                           ,&sname);
            if (SUCCEEDED(hr)) {
              if (sname.uType == STRRET_WSTR) {
                result = sname.pOleStr;
                ::CoTaskMemFree(sname.pOleStr);
                sname.pOleStr = NULL;
              } else if (sname.uType == STRRET_CSTR) {
                result = string2wstring(sname.cStr);
              } //else here add the third case code
            }
         }
     }
   }
   return result;
}

int main(int /*argc*/, char** /*argv[]*/)
{
  std::wcout << get_display_name() << std::endl;
  return EXIT_SUCCESS;
}
//---------------------------------------------------------------------------
Re: Cleaning up after using GetDisplayNameOf()
#477
Author: Remy Lebeau (TeamB)
Date: Thu, 13 Mar 2008 16:37
13 lines
1444 bytes
"Darko Miletic" <kiklop@fibertel.com.ar> wrote in message 
news:47d92ad2@newsgroups.borland.com...

> First of all if your app is VCL CoInitialize is not needed.
> By default COM is initialized in VCL app.

There is no harm in doing it yourself, though.  The COM library is reference 
counted.  As long as all calls to CoInitialize() have balanced calls to 
CoUnitialize(), you will be fine.


Gambit 
Re: Cleaning up after using GetDisplayNameOf()
#476
Author: Remy Lebeau (TeamB)
Date: Thu, 13 Mar 2008 16:45
73 lines
3028 bytes
"Kris" <x@x.com> wrote in message news:47d8ec90$1@newsgroups.borland.com...

> Do I have to take care of freeing that memory

Yes.

> is someone else taking care of freeing the STRRET.pOleStr
> (e.g. pDesktopFolder) ?

No.

> Probably safe to assume STRRET_WSTR is set in this specific case
> So simply use the pOleStr member without any additional checks

You cannot assume that.  You need to look at the STRRET's uType parameter to 
know exactly how to interpret the STRRET's contents properly, for example:

    switch( sName.uType )
    {
        case STRRET_CSTR
            DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%s]", sName.cStr);
            break;
        case STRRET_OFFSET:
        {
            char *pName = (char*)(((LPBYTE)pidl) + sName.uOffset);
            DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%s]", pName);
            break;
        }
        case STRRET_WSTR:
        {
            // assuming DEBUG() is based on printf() or similar function...
            DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%ls]", sName.pOleStr);
            CoTaskMemFree(sName.pOleStr);
            break;
        }
    }

Alternatively, you can use one of the StrRetTo...() functions instead, which 
also handle freeing the STRRET_WSTR memory for you, ie:


    TCHAR szBuf[MAX_PATH+1] = {0};
    StrRetToBuf(&sName, pidl, szBuf, MAX_PATH+1);

    #ifdef UNICODE
    DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%ls]", szBuf);
    #else
    DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%s]", szBuf);
    #endif

Or:

    LPTSTR pName;
    StrRetToStr(&sName, pidl, &pName);

    #ifdef UNICODE
    DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%ls]", szBuf);
    #else
    DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%s]", szBuf);
    #endif

    CoTaskMemFree(pName);

Or:

    WideString wName;
    StrRetToBSTR(&sName, pidl, &wName);

    DEBUG("DisplayNameof(CSIDL_PERSONAL)=[%ls]", wName.c_bstr());


Gambit 
Re: Cleaning up after using GetDisplayNameOf()
#478
Author: Darko Miletic
Date: Thu, 13 Mar 2008 17:20
13 lines
1765 bytes
Remy Lebeau (TeamB) wrote:
> "Darko Miletic" <kiklop@fibertel.com.ar> wrote in message 
> news:47d92ad2@newsgroups.borland.com...
> 
>> First of all if your app is VCL CoInitialize is not needed.
>> By default COM is initialized in VCL app.
> 
> There is no harm in doing it yourself, though.  The COM library is reference 
> counted.  As long as all calls to CoInitialize() have balanced calls to 
> CoUnitialize(), you will be fine.

You are right but I was just saying that if it is already there no need 
to add more code than needed.
Re: Cleaning up after using GetDisplayNameOf()
#479
Author: Remy Lebeau (TeamB)
Date: Thu, 13 Mar 2008 17:43
14 lines
1608 bytes
"Darko Miletic" <kiklop@fibertel.com.ar> wrote in message 
news:47d96258$1@newsgroups.borland.com...

> You are right but I was just saying that if it is already
> there no need to add more code than needed.

On the other hand, it is not good practice to assume what the framework does 
internally.  The VCL calls CoInitialize/Ex() at startup only when certain 
units (namely - ComObj, DBTables, HTTPIntr, ScktMain, SConnect, and VCLCom) 
are being used by the project.


Gambit 
Re: Cleaning up after using GetDisplayNameOf()
#480
Author: Kris
Date: Fri, 14 Mar 2008 09:02
6 lines
1200 bytes
Thank you once again for a very comprehensive and complete answer. And 
yes, simply assuming STRRET_WSTR was quite lazy on my part :-)

Regards,

Kris
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads