How do you manage unmanaged buffers used for P/Invoke

How do you manage unmanaged buffers used for P/Invoke

This is a solution I posted to the C# forum answering an OP's question

http://social.msdn.microsoft.com/Forums/vstudio/en-US/f569a375-5d5d-458f-a0ed-bff87bd97167/how-do-you-manage-unmanaged-buffers-used-for-pinvoke

The OP needed to know a way to make a wrapper that cleans up after itself for String Tokenizing. 

The problem is that strtok remembers its position in the string used in the previous call to strtok. Because the string used by strtok is unmanaged he needed to to make sure that the unmanaged string is available until strtok finishes tokenizing the entire string, or until strtok receives a new string to tokenize.  In my code I created it in a way he could use a using block and it cleans up itself when scope is exited.


This is his code:

public static void Main(string[] args)
    {
        string a = "abcd-efg,hi///jk|lmnop";
        string b = "    hello      interop";
 
        List<string> tokens = new List<string>();
 
        // get first token of string a using "-" as the delimiter
 
        tokens.Add(Utility.StrTok(a, "-"));
 
        // get next tokens of string a by setting the first argument to null
        // each StrTok call can use a different delimiter
 
        tokens.Add(Utility.StrTok(null, ","));
        tokens.Add(Utility.StrTok(null, "/"));
 
        // start tokenizing string b without finishing string a
 
        tokens.Add(Utility.StrTok(b, " "));
        tokens.Add(Utility.StrTok(null, " "));
        tokens.Add(Utility.StrTok(null, " "));
 
        foreach (string token in tokens) {
            Console.WriteLine("<{0}>", token);
        }
    }
}


/* Output
<abcd>
<efg>
<hi>
<hello>
<interop>
<>
*/

His class:

class Utility
{
    [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr strtok(IntPtr str, string delim);
   
    static IntPtr strtokBuffer = IntPtr.Zero;
   
    public static string StrTok(string str, string delim)
    {
        // if str is null
        // continue tokenizing the same unmanaged string from previous strtok call
        // if str is not null
        // strtok will start tokenizing a new unmanaged string initialized with str
   
        // if the token returned from strtok is IntPtr.Zero
        // strtok has finished tokenizing the unmanaged string
   
        IntPtr token;
   
        if (str == null) {
            token = strtok(IntPtr.Zero, delim);
        } else {
            IntPtr strAnsi = Marshal.StringToHGlobalAnsi(str);
            token = strtok(strAnsi, delim);
            Marshal.FreeHGlobal(strtokBuffer);
            strtokBuffer = strAnsi;
        }
   
        if (token == IntPtr.Zero) {
            Marshal.FreeHGlobal(strtokBuffer);
            strtokBuffer = IntPtr.Zero;
            return null;
        }
   
        return Marshal.PtrToStringAnsi(token);
    }
}


I gave him a class that utilizes IDisposable:

public class Utility : IDisposable
    {
        public void Dispose()
        {
            Marshal.FreeHGlobal(strtokBuffer);
            strtokBuffer = IntPtr.Zero;
        }
        [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern IntPtr strtok(IntPtr str, string delim);
        static IntPtr strtokBuffer = IntPtr.Zero;
        public string StrTok(string str, string delim)
        {
            // if str is null
            // continue tokenizing the same unmanaged string from previous strtok call
            // if str is not null
            // strtok will start tokenizing a new unmanaged string initialized with str
            // if the token returned from strtok is IntPtr.Zero
            // strtok has finished tokenizing the unmanaged string
            IntPtr token;
            if (str == null)
            {
                token = strtok(IntPtr.Zero, delim);
            }
            else
            {
                IntPtr strAnsi = Marshal.StringToHGlobalAnsi(str);
                token = strtok(strAnsi, delim);
                Marshal.FreeHGlobal(strtokBuffer);
                strtokBuffer = strAnsi;
            }
            if (token == IntPtr.Zero)
            {
                Marshal.FreeHGlobal(strtokBuffer);
                strtokBuffer = IntPtr.Zero;
                return null;
            }
            return Marshal.PtrToStringAnsi(token);
        }
    }


To use the class with the IDisposable interface implemented:

public static void Main(string[] args)
{
            List<string> tokens = new List<string>();
            string stringA = "abcd-efg,hi///jk|lmnop";
            string stringB = "    hello      interop";
            using (var util = new Utility())
            {
                // get first token of string a using "-" as the delimiter
  
                tokens.Add(util.StrTok(stringA, "-"));
  
                // get next tokens of string a by setting the first argument to null
                // each StrTok call can use a different delimiter
  
                tokens.Add(util.StrTok(null, ","));
                tokens.Add(util.StrTok(null, "/"));
  
                // start tokenizing string b without finishing string a
  
                tokens.Add(util.StrTok(stringB, " "));
                tokens.Add(util.StrTok(null, " "));
                //tokens.Add(StrTok(null, " "));
            }
            foreach (string token in tokens)
            {
                Console.WriteLine("<{0}>", token);
            }
}

If you are interested in string tokenizing via PowerShell check out:
http://gallery.technet.microsoft.com/Generic-PowerShell-string-e9ccfe73

As a side note to anyone who wants to nicely format their code into a code block like you see above... Instructions to do this is at:
https://encrypted.google.com/#q=Add+code+block+to+Technet+wiki

Sort by: Published Date | Most Recent | Most Useful
Comments
Page 1 of 1 (2 items)