views:

642

answers:

2

I am attemping to use NetUseAdd to add a share that is needed by an application. My code looks like this.

[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint NetUseAdd(
     string UncServerName,
     uint Level,
     IntPtr Buf,
     out uint ParmError);

...

USE_INFO_2 info = new USE_INFO_2();
info.ui2_local = null;
info.ui2_asg_type = 0xFFFFFFFF;
info.ui2_remote = remoteUNC;
info.ui2_username = username;
info.ui2_password = Marshal.StringToHGlobalAuto(password);
info.ui2_domainname = domainName;

IntPtr buf = Marshal.AllocHGlobal(Marshal.SizeOf(info));

try
{
    Marshal.StructureToPtr(info, buf, true);

    uint paramErrorIndex;
    uint returnCode = NetUseAdd(null, 2, buf, out paramErrorIndex);

    if (returnCode != 0)
    {
        throw new Win32Exception((int)returnCode);
    }
}
finally
{
    Marshal.FreeHGlobal(buf);
}

This works fine on our server 2003 boxes. But in attempting to move over to Server 2008 and IIS7 this doesnt work any more. Through liberal logging i have found that it hangs on the line Marshal.StructureToPtr(info, buf, true);

I have absolutely no idea why this is can anyone shed any light on it for tell me where i might look for more information?

A: 

What kind of layout do you have for your struct? I believe the USE_INFO_2 needs to be [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)].

Also, have you tried just passing the structure by ref rather than needing to Marshal.StructureToPtr?

Joshua
A: 

The reason is:

You pulled the p/invoke signature off pinvoke.net and you didn't verify it. The fool who initially wrote this p/invoke sample code had no idea what he was doing and created one that "works" on 32-bit systems but does not work on 64-bit systems. He somehow turned a very simple p/invoke signature into some amazingly complicated mess and it spread like wildfire on the net.

The correct signature is:

    [DllImport( "NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
    public static extern uint NetUseAdd(
         string UncServerName,
         UInt32 Level,
         USE_INFO_2 Buf,
         out UInt32 ParmError
        );

    [StructLayout( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
    public struct USE_INFO_2
    {
        public string ui2_local;
        public string ui2_remote;
        public string ui2_password;
        public UInt32 ui2_status;
        public UInt32 ui2_asg_type;
        public UInt32 ui2_refcount;
        public UInt32 ui2_usecount;
        public string ui2_username;
        public string ui2_domainname;
    }

And your code should read:

USE_INFO_2 info = new USE_INFO_2();   
info.ui2_local = null;   
info.ui2_asg_type = 0xFFFFFFFF;   
info.ui2_remote = remoteUNC;   
info.ui2_username = username;   
info.ui2_password = password;
info.ui2_domainname = domainName;      

uint paramErrorIndex;   
uint returnCode = NetUseAdd(null, 2, info, out paramErrorIndex);   

if (returnCode != 0)   
{   
    throw new Win32Exception((int)returnCode);   
}

Hope this was of some help. I just spent half a day knee deep remote debugging someone elses trash code trying to figure out what was going on and it was this.

Karl Strings