tags:

views:

42

answers:

1

I'm using C# for WinForms app in VS2010 and I needed to create a directory in which the path was too large for the .NET methods (248 char limit, I believe) and ran across suggestions from google to use the Unicode Win32 CreateDirectory(). I had initially tried calling it using Unicode and passed parameters but after several failed attempts, I've reduced the code and am using EXACTLY the code found here:

http://www.pinvoke.net/default.aspx/Structures/SECURITY_ATTRIBUTES.html

I am still getting the same error:

System.AccessViolationException was caught Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Admittedly, I don't know anything about calling the Win32 functions, I'm really just pulling what I can find online and trying to learn. Can anyone tell me what I'm doing wrong? Removing non-essential code for the question, I have:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Text;

namespace RFCGenerator
{

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }

    public class RFC
    {

        [DllImport("kernel32.dll")]
        static extern bool CreateDirectory(string lpPathName, SECURITY_ATTRIBUTES lpSecurityAttributes);   

        protected void CopyDirectory(Uri Source, Uri Destination)
        {

            SECURITY_ATTRIBUTES lpSecurityAttributes = new SECURITY_ATTRIBUTES();
            DirectorySecurity security = new DirectorySecurity();
            lpSecurityAttributes.nLength = Marshal.SizeOf(lpSecurityAttributes);
            byte[] src = security.GetSecurityDescriptorBinaryForm();
            IntPtr dest = Marshal.AllocHGlobal(src.Length);
            Marshal.Copy(src, 0, dest, src.Length);
            lpSecurityAttributes.lpSecurityDescriptor = dest;
            string path = @"C:\Test";
            CreateDirectory(path, lpSecurityAttributes);
        }
    }
}

UPDATE: using Hans' suggestion, I did get this to work locally. However, when I attempt to create a directory using a UNC address, such as passing in:

 path = @"\\mydomain.com\foo\bar\newfolder"

I now get:

System.ComponentModel.Win32Exception was caught Message=The filename, directory name, or volume label syntax is incorrect

I have verified that \\mydomain.com\foo\bar\ does exist.

SOLUTION:

Using Hans' code and a minor modification to check if it's UNC path (reference: http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx, under "Maximum Path Length Limitation"):

string UnicodePath = (path.StartsWith(@"\\")) ? @"\\?\UNC\" + (path.Remove(0, 2)) : @"\\?\" + path;
if (!CreateDirectory(UnicodePath, IntPtr.Zero))
    throw new System.ComponentModel.Win32Exception();
+2  A: 

You are not using the Unicode version, that requires CharSet = CharSet.Unicode in the [DllImport] declaration. Furthermore, creating directories with long names has nothing to do with the security attribute. You have to prefix the name with @"\\?\". Thus:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes); 
...
if (!CreateDirectory(@"\\?\" + path, IntPtr.Zero))
    throw new Win32Exception(); 
Hans Passant
Fantastic -- thank you very much, Hans. I did set the Unicode charset and use the "\\?\" originally but removed them to simplify my testing. It was the security attribute that was apparently giving me the error. However, I am now experiencing a new problem. I will add to my original post.
heath
Your question looks the same. Start a new thread for a new question.
Hans Passant
Sorry, it took me a moment to update.
heath
A network share, that's iffy. Make sure you don't get three backslashes after the ?. Very iffy.
Hans Passant
When I examine the variable at runtime (with escapes), it is: "\\\\?\\\\\\mydomain.com\\foo\\bar\\
heath
http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx. This shows promise. It says...The "\\?\" prefix can also be used with paths constructed according to the universal naming convention (UNC). To specify such a path using UNC, use the "\\?\UNC\" prefix. I am testing now...
heath