7

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();
}
efotinis
  • 14,565
  • 6
  • 31
  • 36
k...m
  • 131
  • 1
  • 6

3 Answers3

9

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
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    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 Nov 06 '08 at 15:09
  • 1
    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 Nov 06 '08 at 15:24
  • 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 Nov 06 '08 at 16:51
  • 2
    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 Nov 21 '09 at 00:45
3

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
  • 131
  • 1
  • 6
1

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
  • 149
  • 1
  • 2