Sometimes in the future 'ANSI' is really going to be unsupported!

by Michael S. Kaplan, published on 2006/10/24 03:44 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2006/10/24/867880.aspx


The question to the microsoft.public.win32.programmer.international newsgroup was simple enough:

TITLE: RegLoadMUIString Vista P Invoke

Hello,
I'm trying to get an MUI string out of the registry in display friendly format. From what I've read, strings in the following format:
“@[path]\dllname,-strID” are MUI strings and receive special handling via the RegLoadMUIString API call.

This is what I have come up with for the call:

[DllImport("advapi32.dll")]
internal static extern long RegLoadMUIString(IntPtr hKey, string pszValue, StringBuilder pszOutBuf, int cbOutBuf, out int pcbData, uint Flags, string pszDirectory);

I'm calling this function with a valid pointer to an open registry key, passing in the appropriate value key (pszValue) which has the MUI formatted string. When I check the output buffer (pszOutBuf) it's always an empty string.

Has anyone been able to get this call to work? I cannot find any examples on the web.

thanks,
-bp

A few exchanges back and forth where he showed me how he was calling the function showed three problems: one caused by him, one caused by the .NET Framework team, and one caused by the MUI team. I'll explain all three of these problems here....

 The problem that was bp's fault was that several Win32 API functions were being called, but the return value was not being checked. So a functionality error was being reported with an implicit assumption that function calls succeeded when there was really no evidence of success. Luckily this same person put the code up on the MSDN Forums in this post where this problem was fixed, and the failure was now known -- it was returning error 120, which can be found in winerror.h:

//
// MessageId: ERROR_CALL_NOT_IMPLEMENTED
//
// MessageText:
//
// This function is not supported on this system.
//
#define ERROR_CALL_NOT_IMPLEMENTED 120L

Ok, this leads us to the next problem, one that in my opinion is caused by the .NET Framework. For the sake of backward compatibility with VB4, VB5, and VB6, all P-Invoke calls that do not have charset information attached default to use the "A" version of functions. This means that even though the code is running in .NET where all the strings are Unicode on Vista where the registry and everything else is Unicode that everything is being dealt with as if it were a non-Unicode string, and the non-Unicode version of functions is being called.

Remember when I wrote about how The Unicode train is leaving the station and was quite clear that there would no longer be non-Unicode function calls added to the NLS API? And how we'd be recommending to other teams that they do the same, either the way we did with FindNLSString (not even decorating the name with a "W") or the way the Shell team did with StrCmpLogicalW (a "W" decoration), no "A" version is being provided.

Anyway, we now hit the final problem, which in my opinion is the MUI team's fault. They have only provided a Unicode implementation, but have provided both RegLoadMUIStringA and RegLoadMUIStringW, one of which the precompiler will convert RegLoadMUIString to depending on whether UNICODE is defined.

RegLoadMUIStringA is, in fact, not supported.

But if you look at the requirements section in the docs, they clearly claim that the function is "Implemented as RegLoadMUIStringW (Unicode) and RegLoadMUIStringA (ANSI)."

For those who are interested (someone who spends time on the MSDN forums can pass the word if it has not been answered there yet!), the code that will work on Vista and solves all three of these problems is:

using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace RegMUITest {
  class Program {
    [DllImport("advapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true, EntryPoint="RegOpenKeyExW", CallingConvention=CallingConvention.StdCall)]
    public static extern int RegOpenKeyEx(IntPtr hKey, string lpSubKey,int ulOptions,int samDesired, out IntPtr phkResult);

    [DllImport("advapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true, EntryPoint="RegLoadMUIStringW", CallingConvention=CallingConvention.StdCall)]
    internal static extern int RegLoadMUIString(IntPtr hKey, string pszValue, StringBuilder pszOutBuf, int cbOutBuf, out int pcbData, uint Flags, string pszDirectory);

    [DllImport("advapi32.dll", ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
    public static extern int RegCloseKey(IntPtr hKey);

    static void Main(string[] args) {
      try {
        IntPtr localMachine = new IntPtr((long)unchecked((int)0x80000002));
        IntPtr regKey;
        int pcbData, retval;

        //NOTE: Open a device key with KEY_READ access rights.
        retval = RegOpenKeyEx(localMachine, @"SYSTEM\CurrentControlSet\Control\Class\{36FC9E60-C465-11CF-8056-444553540000}", 0, 0x20019, out regKey);
        if(retval != 0) {
          Console.WriteLine("RegOpenKeyEx failed with error {0}.", retval);
        } else {
          //NOTE: Build the output buffer reference
          StringBuilder lptStr = new StringBuilder(1024);
          //NOTE: ClassDesc contains the MUI formatted string
          retval = RegLoadMUIString(regKey, "ClassDesc", lptStr, 1024, out pcbData, 0, null);
          if(retval != 0) {
            Console.WriteLine("RegOpenKeyEx failed with error {0}.", retval);
          } else {
            //NOTE: Output values to console
            Console.WriteLine("Reg key : {0}", regKey);
            Console.WriteLine("LPWSTR : {0}", lptStr.ToString());
            Console.WriteLine("pcbData : {0}", pcbData);
          }

          //NOTE: Close the key
          RegCloseKey(regKey);
        }
      }
      catch (Exception ex) {
        Console.WriteLine("Exception : " + ex.Message);
      }

      Console.ReadLine();
    }
  }
}

 Enjoy!

 

This post brought to you by  (U+a13e, a.k.a. YI SYALLABLE DDAX)


# Ted. on 9 Nov 2006 11:42 AM:

It's unfortunate that this "A" function is exported at all.   Are there any other functions that have "A" versions that return "call not implemented?"   I was hoping that all "Unicode only" functions would not have dummy "A" stubs included as well.  

In a sick way, I can see a need for an MSLA DLL (Microsoft Layer for ANSI), for certain customers.

# Michael S. Kaplan on 10 Nov 2006 6:25 AM:

Yikes! Just say NAY to MSLA! :-)

I did not even know about this one until looking into the reported problem, so I am not sure if there are others....


Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2012/03/26 The Unicode train left the station YEARS ago, in fact! (2012 edition)

2007/02/22 Getting the resource info you want

2006/11/20 Putting the *backward* in backward compatibility

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