views:

2404

answers:

3

Multistrings (double null-terminated string of null-separated strings) are common in the Windows API. What's a good method for converting a multistring returned from an API to a C# string collection and vice versa?

I'm especially interested in proper handling of character encoding (Windows XP an later).

The following method seems to be okay for creating a multistring, but I don't have an example of decoding a multistring.

static string StringArrayToMultiString(
    ICollection<string> stringArray
    )
{
    StringBuilder multiString = new StringBuilder();


    if (stringArray != null)
    {
        foreach (string s in stringArray)
        {
            multiString.Append(s);
            multiString.Append('\0');
        }
    }

    return multiString.ToString();
}
+7  A: 

This might be naïve, but how about:

static string[] MultiStringToArray(string multiString)
{
    return multiString.TrimEnd('\0').Split('\0');
}

Also - aren't you missing the final \0 (you state double-null-terminated) in StringArrayToMultiString? And it might be easier to call if the array was a params string[] array - something like:

    static string StringArrayToMultiString(params string[] values)
{
    if (values == null) throw new ArgumentNullException("values");
    StringBuilder multiString = new StringBuilder();

    foreach (string s in values)
    {
        multiString.Append(s);
        multiString.Append('\0');
    }
    return multiString.ToString();
}

[edited after clarification about final \0]

Marc Gravell
I think a \0 is already there in the string when it is sent through COM, meaning he would need one \0 but not the other.
configurator
Yeah, that's correct -- when a string is passed to a Win32 API, it is null-terminated. So there's no need to add the last \0.A multi-string cannot contain empty strings. That means that a regular empty string "" is a legit multistring of no entries.
k...m
OK, updated then
Marc Gravell
The MultiStringToArray() method work nicely if the multiString value contains the full multistring value, but the issue is how to get that value back from the API call.
k...m
hmmm... tricky to look into without a concrete example...
Marc Gravell
The TrimEnd here could be omitted with `multiString.Split('\0', StringSplitOptions.RemoveEmptyEntries)`, since empty strings cannot be stored in a double-null terminated list.
configurator
+1  A: 

I've tested the StringArrayToMultiString method, using the ChangeServiceConfig() function to change the dependencies of a Windows service, and it works nicely for zero, one and many strings.

In the meantime, I've worked out a solution for decoding a multistring received from an API call. For example, the SCardListReaders() function returns a multistring of PC/SC reader names. I declared this as:

[DllImport("winscard.dll", CharSet = CharSet.Auto)]
static extern int SCardListReaders(
    IntPtr context,
    string groups,
    char[] readers,
    ref uint readersLen
    );

Note that the readers parameter, which returns the multistring, is declared as char[]. The return value is easily parsed and converted into a collection of strings:

static string[] MultiStringToArray(
    char[] multistring
    )
{
    List<string> stringList = new List<string>();
    int i = 0;
    while (i < multistring.Length)
    {
        int j = i;
        if (multistring[j++] == '\0') break;
        while (j < multistring.Length)
        {
            if (multistring[j++] == '\0')
            {
                stringList.Add(new string(multistring, i, j - i - 1));
                i = j;
                break;
            }
        }
    }

    return stringList.ToArray();
}
k...m
+1  A: 

In the MSDN documentation for RegistryKey.SetValue() and RegistryKey.GetValue(), the examples simply pass in, or cast to, string[], respectively. Is there something wrong with that?

David