Converting a Project to Unicode: Part 4 (It's /Delightful, it's /Delicious, it's /DUnicode!)

by Michael S. Kaplan, published on 2006/12/31 06:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2006/12/31/1386486.aspx


Previous posts in this series (including today's!):

(If you are just tuning in and want to start now you can grab the current source from here.) 

Ok, let's take the gloves off.

This project was not really build to be compiled as Unicode, so we have to do it the hard way.

Just clean up with the following line (if you have compiled already):

nmake clean

And then to do the Unicode compile, you can try:

nmake cflags="$(cflags) /DUNICODE /D_UNICODE"

(We won't keep doing this, for a whole bunch of reasons!) 

Now it is going to fail -- we still have some work to do, after all. The first error is easy enough:

Aha, let's look up strtoul and look for the TCHAR-version here. Scroll down a bit, and Aha! We need that to be _tcstoul, instead.

And to make this all easier let's add this line to the makefile:

cflags = $(cflags) /DUNICODE /D_UNICODE

rather than passing it on the command line (note that this is the usuasl way that Unicode versions of makefiles are done in MSDN samples, in sepasrate files entitled makefile.uni that include the old makefile!). Here goes: 

Now you may have already fixed this yesterday when you noticed yourself fixing string constants that  were being used with CharNextA. Well, we are not going to follow the advice here because using a reinterpret_cast, a C-style cast, and a function-style cast would all be inappropriate. Let's just change them all to CharNext rather than CharNextW,  so that we can compile either with Unicode or not and keep working.... then we'll run nmake again:

 Ok, the first problem is easy, just turn CharPrevA into CharPrev.

The second problem requires a bi more thought, though. The code is  in the LoadResourceString function in util.cpp, and the code loads up the resource as Unicode already (this is one of those spots in Part 1 that I suggested we think about later).

In this case, we need to add some #ifdef UNICODE specific code, to make sure that under Unicode we copy the loaded string to the caller-supplied buffer under Unicode, and that we leave the code as it otherwise. The new function will be something like this (new code marked in red):

/////////////////////////////////////////////////////////////////////////////
// LoadResourceString
//
UINT LoadResourceString(HINSTANCE hInst, LPCTSTR lpType, LPCTSTR lpName, LPTSTR lpBuf, DWORD *pdwBufSize)
{
    HRSRC   hRsrc   = 0;
    HGLOBAL hGlobal = 0;
    WCHAR   *pch    = 0;

    if ((hRsrc = WIN::FindResource(hInst, lpName, lpType)) != 0
        && (hGlobal = WIN::LoadResource(hInst, hRsrc)) != 0)
    {
        // resource exists
        if ((pch = (WCHAR*)LockResource(hGlobal)) != 0)
        {
            unsigned int cch;
#ifdef UNICODE
            cch = lstrlen(pch);
            if(FAILED(StringCchCopy(lpBuf, *pdwBufSize, pch)))
            {
                *pdwBufSize = cch;
                return ERROR_FUNCTION_FAILED;
            }
#else
            cch = WideCharToMultiByte(CP_ACP, 0, pch, -1, NULL, 0, NULL, NULL);
            if (cch > *pdwBufSize)
            {
                *pdwBufSize = cch;
                return ERROR_MORE_DATA;
            }

            if (0 == WideCharToMultiByte(CP_ACP, 0, pch, -1, lpBuf, *pdwBufSize, NULL, NULL))
                return ERROR_FUNCTION_FAILED;
#endif // UNICODE
            *pdwBufSize = cch;
        }
        else
        {
            if (1 > *pdwBufSize)
            {
                *pdwBufSize = 1;
                return ERROR_MORE_DATA;
            }

            *pdwBufSize = 1;
            *lpBuf = 0;
        }
       
        DebugMsg(TEXT("[Resource] lpName = %s, lpBuf = %s\n"), lpName, lpBuf);

        return ERROR_SUCCESS;
    }

    // resource does not exist
    DebugMsg(TEXT("[Resource] lpName = %s NOT FOUND\n"), lpName);

    return ERROR_RESOURCE_NAME_NOT_FOUND;
}

Ok, let's try compiling again with this error fixed too. We'll try a clean compile and just see how far we get:

Ah, here is another such problem (these vertrust.cpp cases are the ones where there are WCHAR variables defined). In this case, there are two pairs of calls in vertrust.cpp, both in the IsPackageTrusted function. The function basically  converts the szPackage and the szSetupExe to Unicode and then calls the IsFileTrusted function on each one.

Of course the biggest problem is that (in my opinion) we really do not have the same situation as with the previous resource loading situation -- we should ideally just pass through the string as is and not allocate or copy anything. The minimal change function I came up with, you can take a look at tomorrow (or you can try out writing your own if you like!). I mainly tried to avoid the extra allocates and copies.

Once this function is fixed up:

Success!

Hey, does that mean we're done?

Well, not quite. Anyone want to guess at what else we need to do before we can get into all the advanced stuff like MSLU integration and so on?

Or does anyone want to try to figure out the best way to write that IsPackageTrusted?

:-)

These last two issues were mercifully few, and they point to the two types of issues that require special handling -- the times when Unicode was involved before the conversion, and what to do with them.

(Coming up -- tomorrow will center on answering the "what's next" question a little bit, but mostly it will be about a discussion about the different experience you will have if you don't (didn't?) do the things I suggested in Parts 2 and 3 and instead skipped right to Part 4 -- because it leads to a very different conversion experience!)

 

This post brought to you by  (U+0c19, a.k.a. TELUGU LETTER NGA)


# Ben Bryant on 31 Dec 2006 9:51 AM:

I was in the habit of only defining _UNICODE with the underscore in the project defines until I recently discovered that I needed to define UNICODE without the underscore for a project that included a file without MFC. Somewhere in one of the MFC Afx headers it defines UNICODE for you if you haven't already.

# Michael S. Kaplan on 31 Dec 2006 12:15 PM:

I didn't know that, though it makes sense -- MFC has to call the Win32 API in many cases, and the Unicode version expects to call the Unicode Win32 API....


referenced by

2007/12/24 VS just got served!, aka The ??? Shift, aka 'Converting a project to Unicode???' No, it's 'Converting a project??? ToUnicode!!!'

2007/01/05 Converting a project to Unicode: Part 9 (The project's postpartum postmortem)

2007/01/04 Converting a project to Unicode: Part 8 (Fitting MSLU into the mix)

2007/01/03 Converting a Project to Unicode: Part 7 (What does it mean to fit things to a 'T', anyway?)

2007/01/02 Converting a Project to Unicode: Part 6 (Upon the road not traveled)

2007/01/01 Converting a Project to Unicode: Part 5 (Are we there yet? Well, not *just* yet)

go to newer or older post, or back to index or month or day