Shell Extensions Tutorials

Writing shell extensions is one of those programming tasks in which C++ (with the help of a library like ATL) excels.

(A Microsoft guy explained here why it is better to avoid .NET for writing shell extensions.)

Michael Dunn (a former Visual C++ MVP) wrote a very interesting series of tutorials on CodeProject on developing shell extensions:

1. A step-by-step tutorial on writing shell extensions.
2. A tutorial on writing a shell extension that operates on multiple files at once.
3. A tutorial on writing a shell extension that shows pop-up info for files.
4. A tutorial on writing a shell extension that provides custom drag and drop functionality.
5. A tutorial on writing a shell extension that adds pages to the properties dialog of files.
6. A tutorial on writing a shell extension that can be used on the Send To menu.
7. A tutorial on using owner-drawn menus in a context menu shell extensions, and on making a context menu extension that responds to a right-click in a directory background.
8. A tutorial on adding columns to Explorer’s details view via a column handler shell extension.
9. A tutorial on writing an extension to customize the icons displayed for a file type.

 

Some links on writing exception-safe code in C++

I found a very interesting thread on StackOverflow about writing exception safe code in C++:

C++: do you (really) write exception safe code?

The first answer is worth reading.

And, as already written in the aforementioned thread, detailed information on this subject can be found on GotW website, too:

GotW #59: Exception-Safe Class Design, Part 1: Copy Assignment

GotW #60: Exception-Safe Class Design, Part 2: Inheritance

GotW #61: CHALLENGE EDITION: ACID Programming 
 

The secret QueryInterface call of CComPtr

CComPtr is a convenient smart pointer ATL class to manage reference counting of COM objects.
However, it seems that sometimes smart pointers are too smart… In particular, I’m referring to the secret QueryInterface‘ing assignment operator discussed by Jared Parsons on his blog:

http://blogs.msdn.com/jaredpar/archive/2009/11/04/type-safety-issue-when-assigning-ccomptr-t-instances.aspx

The problem is the IUnknown::QueryInterface call performed by AtlComQIPtrAssign in the following templated assignment operator overload of CComPtr:

template <typename Q>

T* operator=(_In_ const CComPtr<Q>& lp) throw()

{

  if( !IsEqualObject(lp) )

  {

    return static_cast<T*>(AtlComQIPtrAssign((IUnknown**)&p, lp, __uuidof(T)));

  }

  return *this;

}

As an example, the following C++ code compiles fine (and I think it shouldn’t) on VC9 (VS2008 SP1):

#include <atlcomcli.h>

 

int main()

{

    CComPtr<IMarshal> sp1;

    CComPtr<IPersist> sp2;

 

    // I think the following statement should not compile,

    // but instead it does compile…

    sp1 = sp2;

 

    return 0;

}

Frankly speaking, I consider this behavior of CComPtr a bug; in fact, CComPtr isn’t supposed to call QueryInterface automatically: there’s CComQIPtr for that.
A possible fix to the aforementioned CComPtr behavior (bug) could be to redefine the templated assignment operator using implicit conversion for underlying raw pointers (instead of using IUnknown::QueryInterface via AtlComQIPtrAssign), e.g.:

    template <typename Q>

    T* operator=(_In_ const CComPtr<Q>& lp) throw()

    {

        return (*this = lp.p);

    }

 

A VS2008 solution with sample C++ code including the fix to CComPtr is attached to this blog post.

Thanks to Igor Tandetnik for private communication about this issue.

 

 

 

 

 

STL strings: loading from resources

As we’ve already seen, while it is possible to load CString’s from resources out of the box, std::string and std::wstring classes do not offer this feature.

Carefully reading the MSDN documentation for LoadString Win32 API, an interesting point about nBufferMax parameter can be found:

If this parameter is zero, then lpBuffer receives a read-only pointer to the resource itself.


We can use this particular feature of LoadString to write a simple C++ function to load std::wstring’s from resources:

 

std::wstring LoadStringFromResource(

    __in UINT stringID,

    __in_opt HINSTANCE instance = NULL )

{

    WCHAR * pBuf = NULL;

 

    int len = LoadStringW(

        instance,

        stringID,

        reinterpret_cast< LPWSTR >( &pBuf ),

        0 );

   

    if( len )

        return std::wstring( pBuf, len );

    else

        return std::wstring();

}

 

The above code can be used like this:

       wstring str = LoadStringFromResource( IDS_SOMETHING );

Note:

In 2010, I believe we can forget about ANSI strings (which depend on code pages, etc.) and just assume that strings are stored using Unicode (BTW: this is what modern languages like Java, Python, C#, etc. do). So I just wrote a function to load Unicode strings (i.e. wstring) from resources. Moreover, in the aforementioned LoadString MSDN documentation, there is a comment specifying that the special behaviour of nBufferMax == 0 applies only to the Unicode version of LoadString (i.e. LoadStringW).

EDIT 2011/01/08: String construction code updated accordingly to Bo Persson’s suggestion.

STL strings are not zero-terminated

In a previous blog post, I listed some advantages (IMHO) of using CString instead of std::string class.

There is another subtle (but important!) point which turns the scales in CString‘s favour: in fact, CString is a C++ class that wraps zero-terminated strings (i.e. the good old C strings), instead it seems that the C++ standard library code wants to pretend that zero-terminated strings do not exist…

Let me show this with a sample code (the full C++ source code is attached to this blog post):

    //

    // Check with CString

    //

    CString str;

    WCHAR * psz = str.GetBuffer(100);

    psz[0] = 0; // …or GetWindowText(hwnd, psz, 100)

    str.ReleaseBuffer();

    psz = NULL; // psz is no more valid

    assert( wcslen(str) == str.GetLength() ); // OK

 

 

 

    //

    // Check with std::string

    //

    std::wstring s;

    s.resize(100);

    s[0] = 0; // …or GetWindowText(hwnd, &s[0], 100)

    assert( wcslen(s.c_str()) == s.length() ); // BANG!

So, basically, we have a situation in which (assuming ‘s‘ is an instance of std::wstring) s.length() and wcslen(s.c_str()) do not return the same value.

Considering that Windows APIs (like say GetWindowText) work with zero-terminated strings, CString seems to me a clear winner as a convenient string class for C++ Win32 programming.

 

CString or std::string – that is the question

…with apologies to Shakespeare [:)]

So, should we use CString or std::string class to store and manage strings in our C++ code?

Well, if there is a need of writing portable C++ code, the choice should be std::string, which is part of the C++ standard library.

But, in the context of C++ Win32 programming (using ATL or MFC), I find CString class much more convenient than std::string.

These are some reasons:

1) CString allows loading strings from resources, which is good also for internationalization.

2) CString offers a convenient FormatMessage method (which is good for internationalization, too; see for example the interesting problem of “Yoda speak” discussed in this post on Mihai Nita’s blog).

3) CString integrates well with Windows APIs (the implicit LPCTSTR operator comes in handy when passing instances of CString to Windows APIs, like e.g. SetWindowText).

4) CString is reference counted, so moving instances of CString around is cheap.

5) CString offers convenient methods to e.g. tokenize strings, to trim them, etc.

 

Note:
In Visual C++ 6’s timeframe, CString class was part of MFC. But since Visual Studio .NET 2003, CString was factored out of MFC, and was made part of ATL. So, post VC6, it is possible to use CString also in non-MFC code. To use CString in non-MFC code, just #include <atlstr.h> (a good place for that would be “StdAfx.h” precompiled header).

 

Conversion between Unicode UTF-16 and UTF-8 in C++/Win32

For fresh updated and richer information and modern C++ usage, please read my MSDN Magazine article (published on the 2016 September issue):

Unicode Encoding Conversions with STL Strings and Win32 APIs

New updated modern C++ code can be found here on GitHub.


Check out My Pluralsight Courses here.


Code working with ATL’s CStringW/A classes and throwing exceptions via AtlThrow() can be found here on GitHub. For convenience, the core part of that code is copied below:

//////////////////////////////////////////////////////////////////////////////
//
// *** Functions to convert between Unicode UTF-8 and Unicode UTF-16 ***
//                      using ATL CStringA/W classes
//
// By Giovanni Dicanio 
//
//////////////////////////////////////////////////////////////////////////////


//----------------------------------------------------------------------------
// FUNCTION: Utf8ToUtf16
// DESC:     Converts Unicode UTF-8 text to Unicode UTF-16 (Windows default).
//----------------------------------------------------------------------------
CStringW Utf8ToUtf16(const CStringA& utf8)
{
    // Special case of empty input string
    if (utf8.IsEmpty())
    {
        // Return empty string
        return CStringW();
    }


    // "Code page" value used with MultiByteToWideChar() for UTF-8 conversion 
    const UINT codePageUtf8 = CP_UTF8;

    // Safely fails if an invalid UTF-8 character is encountered
    const DWORD flags = MB_ERR_INVALID_CHARS;

    // Get the length, in WCHARs, of the resulting UTF-16 string
    const int utf16Length = ::MultiByteToWideChar(
            codePageUtf8,       // source string is in UTF-8
            flags,              // conversion flags
            utf8.GetString(),   // source UTF-8 string
            utf8.GetLength(),   // length of source UTF-8 string, in chars
            nullptr,            // unused - no conversion done in this step
            0);                 // request size of destination buffer, in WCHARs
    if (utf16Length == 0)
    {
        // Conversion error
        AtlThrowLastWin32();
    }


    // Allocate destination buffer to store the resulting UTF-16 string
    CStringW utf16;
    WCHAR* const utf16Buffer = utf16.GetBuffer(utf16Length);
    ATLASSERT(utf16Buffer != nullptr);


    // Do the conversion from UTF-8 to UTF-16
    int result = ::MultiByteToWideChar(
            codePageUtf8,       // source string is in UTF-8
            flags,              // conversion flags
            utf8.GetString(),   // source UTF-8 string
            utf8.GetLength(),   // length of source UTF-8 string, in chars
            utf16Buffer,        // pointer to destination buffer
            utf16Length);       // size of destination buffer, in WCHARs  
    if (result == 0)
    {
        // Conversion error
        AtlThrowLastWin32();
    }

    // Don't forget to release internal CString buffer 
    // before returning the string to the caller
    utf16.ReleaseBufferSetLength(utf16Length);

    // Return resulting UTF-16 string
    return utf16;
}



//----------------------------------------------------------------------------
// FUNCTION: Utf16ToUtf8
// DESC:     Converts Unicode UTF-16 (Windows default) text to Unicode UTF-8.
//----------------------------------------------------------------------------
CStringA Utf16ToUtf8(const CStringW& utf16)
{
    // Special case of empty input string
    if (utf16.IsEmpty())
    {
        // Return empty string
        return CStringA();
    }


    // "Code page" value used with WideCharToMultiByte() for UTF-8 conversion 
    const UINT codePageUtf8 = CP_UTF8;

    // Safely fails if an invalid UTF-16 character is encountered
    const DWORD flags = WC_ERR_INVALID_CHARS;

    // Get the length, in chars, of the resulting UTF-8 string
    const int utf8Length = ::WideCharToMultiByte(
            codePageUtf8,       // convert to UTF-8
            flags,              // conversion flags
            utf16.GetString(),  // source UTF-16 string
            utf16.GetLength(),  // length of source UTF-16 string, in WCHARs
            nullptr,            // unused - no conversion required in this step
            0,                  // request size of destination buffer, in chars
            nullptr, nullptr);  // unused
    if (utf8Length == 0)
    {
        // Conversion error
        AtlThrowLastWin32();
    }


    // Allocate destination buffer to store the resulting UTF-8 string
    CStringA utf8;
    char* const utf8Buffer = utf8.GetBuffer(utf8Length);
    ATLASSERT(utf8Buffer != nullptr);


    // Do the conversion from UTF-16 to UTF-8
    int result = ::WideCharToMultiByte(
            codePageUtf8,       // convert to UTF-8
            flags,              // conversion flags
            utf16.GetString(),  // source UTF-16 string
            utf16.GetLength(),  // length of source UTF-16 string, in WCHARs
            utf8Buffer,         // pointer to destination buffer
            utf8Length,         // size of destination buffer, in chars
            nullptr, nullptr);  // unused
    if (result == 0)
    {
        // Conversion error
        AtlThrowLastWin32();
    }


    // Don't forget to release internal CString buffer 
    // before returning the string to the caller
    utf8.ReleaseBufferSetLength(utf8Length);

    // Return resulting UTF-8 string
    return utf8;
}