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

There are differenet ways to “encode” Unicode text, i.e. to represent Unicode text “in bits”. For a Windows/C++ programmer, the most important onse are UTF-16 and UTF-8.

RegEditX – Tweaks for the Windows Registry Editor

RegEditX
RegEditX

UTF-16 is the default Unicode encoding form used by Windows.

UTF-8 is a common encoding form used to exchange text data on the Internet.
One of the advantages of UTF-8 is that there is no endian problem (i.e. big-endian vs. little-end), because UTF-8 is interpreted just as a sequence of bytes (instead, it is important to specify the correct endiannes of UTF-16 code units).

To convert text between Unicode UTF-8 and UTF-16 encodings, a couple of Win32 APIs come in handy: MultiByteToWideChar and WideCharToMultiByte functions.

Suppose we want to convert text from UTF-8 to UTF-16. In this case, MultiByteToWideChar function can be used. To request a conversion from UTF-8, the CP_UTF8 code page value must be specified as first parameter of MultiByteToWideChar.
This function should be called twice: the first time it is called, we set the cchWideChar parameter to 0, so the function returns the required buffer size for the resulting UTF-16 (“wide char”) string. So, we can dynamically allocate a buffer to store the UTF-16 string (this is done using CStringW::GetBuffer method in code sample presented here). Then, we can call the MultiByteToWideChar function again, to perform the actual conversion from UTF-8 to UTF-16.

(So, to summarize: the purpose of the first call to the function is to get the destination buffer size, the second call to the function does the actual conversion.)

A similar process occurs for WideCharToMultiByte, which can be used to convert text from Unicode UTF-16 (“wide char”) to UTF-8.

The following C++ commented code shows how to use these Win32 functions to convert text between UTF-8 and UTF-16.

This code is Win32 C++ code; it uses the ATL convenient CString class (the UTF-16 strings are stored in instances of CStringW; UTF-8 strings are stored in instances of CStringA). This code can be used in the context of MFC as well.

RegEditX: Free 30 day trial

EDIT: An updated version of this code can be found here on GitHub.

Check out My Pluralsight Courses here.

 

//////////////////////////////////////////////////////////////////////////////

//

// *** Routines to convert between Unicode UTF-8 and Unicode UTF-16 ***

//

// By Giovanni Dicanio <giovanni.dicanio AT gmail.com>

//

// Last update: 2010, January 2nd

//

//

// These routines use ::MultiByteToWideChar and ::WideCharToMultiByte

// Win32 API functions to convert between Unicode UTF-8 and UTF-16.

//

// UTF-16 strings are stored in instances of CStringW.

// UTF-8 strings are stored in instances of CStringA.

//

// On error, the conversion routines use AtlThrow to signal the

// error condition.

//

// If input string pointers are NULL, empty strings are returned.

//

//

// Prefixes used in these routines:

// ——————————–

//

//  – cch  : count of characters (CHAR’s or WCHAR’s)

//  – cb   : count of bytes

//  – psz  : pointer to a NUL-terminated string (CHAR* or WCHAR*)

//  – str  : instance of CString(A/W) class

//

//

//

// Useful Web References:

// ———————-

//

// WideCharToMultiByte Function

// http://msdn.microsoft.com/en-us/library/dd374130.aspx

//

// MultiByteToWideChar Function

// http://msdn.microsoft.com/en-us/library/dd319072.aspx

//

// AtlThrow

// http://msdn.microsoft.com/en-us/library/z325eyx0.aspx

//

//

// Developed on VC9 (Visual Studio 2008 SP1)

//

//

//////////////////////////////////////////////////////////////////////////////

 

 

 

namespace UTF8Util

{

 

 

 

//—————————————————————————-

// FUNCTION: ConvertUTF8ToUTF16

// DESC: Converts Unicode UTF-8 text to Unicode UTF-16 (Windows default).

//—————————————————————————-

CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 )

{

    //

    // Special case of NULL or empty input string

    //

    if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == ) )

    {

        // Return empty string

        return L“”;

    }

 

 

    //

    // Consider CHAR’s count corresponding to total input string length,

    // including end-of-string () character

    //

    const size_t cchUTF8Max = INT_MAX – 1;

    size_t cchUTF8;

    HRESULT hr = ::StringCchLengthA( pszTextUTF8, cchUTF8Max, &cchUTF8 );

    if ( FAILED( hr ) )

    {

        AtlThrow( hr );

    }

   

    // Consider also terminating

    ++cchUTF8;

 

    // Convert to ‘int’ for use with MultiByteToWideChar API

    int cbUTF8 = static_cast<int>( cchUTF8 );

 

 

    //

    // Get size of destination UTF-16 buffer, in WCHAR’s

    //

    int cchUTF16 = ::MultiByteToWideChar(

        CP_UTF8,                // convert from UTF-8

        MB_ERR_INVALID_CHARS,   // error on invalid chars

        pszTextUTF8,            // source UTF-8 string

        cbUTF8,                 // total length of source UTF-8 string,

                                // in CHAR’s (= bytes), including end-of-string

        NULL,                   // unused – no conversion done in this step

        0                       // request size of destination buffer, in WCHAR’s

        );

    ATLASSERT( cchUTF16 != 0 );

    if ( cchUTF16 == 0 )

    {

        AtlThrowLastWin32();

    }

 

 

    //

    // Allocate destination buffer to store UTF-16 string

    //

    CStringW strUTF16;

    WCHAR * pszUTF16 = strUTF16.GetBuffer( cchUTF16 );

 

    //

    // Do the conversion from UTF-8 to UTF-16

    //

    int result = ::MultiByteToWideChar(

        CP_UTF8,                // convert from UTF-8

        MB_ERR_INVALID_CHARS,   // error on invalid chars

        pszTextUTF8,            // source UTF-8 string

        cbUTF8,                 // total length of source UTF-8 string,

                                // in CHAR’s (= bytes), including end-of-string

        pszUTF16,               // destination buffer

        cchUTF16                // size of destination buffer, in WCHAR’s

        );

    ATLASSERT( result != 0 );

    if ( result == 0 )

    {

        AtlThrowLastWin32();

    }

 

    // Release internal CString buffer

    strUTF16.ReleaseBuffer();

 

    // Return resulting UTF16 string

    return strUTF16;

}

 

 

 

//—————————————————————————-

// FUNCTION: ConvertUTF16ToUTF8

// DESC: Converts Unicode UTF-16 (Windows default) text to Unicode UTF-8.

//—————————————————————————-

CStringA ConvertUTF16ToUTF8( __in const WCHAR * pszTextUTF16 )

{

    //

    // Special case of NULL or empty input string

    //

    if ( (pszTextUTF16 == NULL) || (*pszTextUTF16 == L) )

    {

        // Return empty string

        return “”;

    }

 

 

    //

    // Consider WCHAR’s count corresponding to total input string length,

    // including end-of-string (L”) character.

    //

    const size_t cchUTF16Max = INT_MAX – 1;

    size_t cchUTF16;

    HRESULT hr = ::StringCchLengthW( pszTextUTF16, cchUTF16Max, &cchUTF16 );

    if ( FAILED( hr ) )

    {

        AtlThrow( hr );

    }

 

    // Consider also terminating

    ++cchUTF16;

 

 

    //

    // WC_ERR_INVALID_CHARS flag is set to fail if invalid input character

    // is encountered.

    // This flag is supported on Windows Vista and later.

    // Don’t use it on Windows XP and previous.

    //

#if (WINVER >= 0x0600)

    DWORD dwConversionFlags = WC_ERR_INVALID_CHARS;

#else

    DWORD dwConversionFlags = 0;

#endif

 

    //

    // Get size of destination UTF-8 buffer, in CHAR’s (= bytes)

    //

    int cbUTF8 = ::WideCharToMultiByte(

        CP_UTF8,                // convert to UTF-8

        dwConversionFlags,      // specify conversion behavior

        pszTextUTF16,           // source UTF-16 string

        static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR’s,

                                        // including end-of-string

        NULL,                   // unused – no conversion required in this step

        0,                      // request buffer size

        NULL, NULL              // unused

        );

    ATLASSERT( cbUTF8 != 0 );

    if ( cbUTF8 == 0 )

    {

        AtlThrowLastWin32();

    }

 

 

    //

    // Allocate destination buffer for UTF-8 string

    //

    CStringA strUTF8;

    int cchUTF8 = cbUTF8; // sizeof(CHAR) = 1 byte

    CHAR * pszUTF8 = strUTF8.GetBuffer( cchUTF8 );

 

 

    //

    // Do the conversion from UTF-16 to UTF-8

    //

    int result = ::WideCharToMultiByte(

        CP_UTF8,                // convert to UTF-8

        dwConversionFlags,      // specify conversion behavior

        pszTextUTF16,           // source UTF-16 string

        static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR’s,

                                        // including end-of-string

        pszUTF8,                // destination buffer

        cbUTF8,                 // destination buffer size, in bytes

        NULL, NULL              // unused

        ); 

    ATLASSERT( result != 0 );

    if ( result == 0 )

    {

        AtlThrowLastWin32();

    }

 

    // Release internal CString buffer

    strUTF8.ReleaseBuffer();

 

    // Return resulting UTF-8 string

    return strUTF8;

}

 

 

 

} // namespace UTF8Util

 

 

//////////////////////////////////////////////////////////////////////////////