I want to execute NetUseEnum from C#. My objective is to list network shares available to the local computer. My preferences regarding parameters to pass are:
UncServerName
= null
; (the local computer is used)LevelFlags
= 0; (USE_INFO_0
)What is the correct method signature?
[DllImport("C:\\dev\\netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint NetUseEnum(
UnmanagedType uncServerName,
UInt32 level,
ref IntPtr buf,
UInt32 preferedMaximumSize,
out int entriesRead,
out int totalEntries,
IntPtr resumeHandle
);
What is the correct method call?
static void Main(string[] args) {
string UncServerName;
IntPtr lBuffer = IntPtr.Zero;
int lRead;
int lTotal;
IntPtr lHandle = IntPtr.Zero;
NetUseEnum(null,
0,
ref lBuffer,
0xffffffff,
out lRead,
out lTotal,
lHandle);
//iterate through lBuffer
NetApiBufferFree(lBuffer);
}
The code above will not work as posted because the string-handling code needs to be modified.
In particular, the incompatibility occurs between the WinAPI (which prefers working with pointers to Unicode strings, i.e., LPWStr) and C# (which prefers working with strings/Strings).
NetUseEnum documentation.
You're setting CharSet.Unicode
and the UncServerName
parameter is an Unicode string if _WIN32_WINNT
or FORCE_UNICODE
are defined. You can declare UncServerName
as string
.
You cannot declare it as UnmanagedType
, it doesn't make sense. You could specify how to marshal with [MarshalAs(UnmanagedType.LPWStr)]
, but it's not required.
The declaration of the other members is ~OK.
You're not doing anything with the Buffer after that, though.
This buffer pointer is set when the function is first called: it points to the first structure of the Type specified by LevelFlags
USE_INFO_0
, USE_INFO_1
or USE_INFO_2
.
You can iterate EntriesRead
times and move the pointer by the size of structure level specified to read all the entries. For example:
// Local Machine, USE_INFO_2 info level
var connections1 = GetNetConnections<USE_INFO_2>(null, 2);
// A Remote Machine, USE_INFO_0 info level
var connections2 = GetNetConnections<USE_INFO_0>("\\\\REMOTE-SERVER", 0);
// [...]
public List<T> GetNetConnections<T>(string computerName, uint infoLevel) where T : struct
{
if (typeof(T) != typeof(USE_INFO_0) && typeof(T) != typeof(USE_INFO_2)) {
throw new ArgumentException("Wrong Type");
}
IntPtr buffer = IntPtr.Zero;
try {
NetUseEnum(computerName, infoLevel, ref buffer,
MAX_PREFERRED_LENGTH, out int entries, out int total, IntPtr.Zero);
var connections = new List<T>(entries);
for (int i = 0; i < entries; i++) {
T info = Marshal.PtrToStructure<T>(IntPtr.Add(buffer, Marshal.SizeOf<T>() * i));
connections.Add(info);
}
return connections;
}
finally {
NetApiBufferFree(buffer);
}
}
Declarations:
internal const uint MAX_PREFERRED_LENGTH = 0xFFFFFFFF;
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint NetUseEnum(
string UncServerName,
uint LevelFlags,
ref IntPtr BufPtr,
uint PreferedMaximumSize,
out int EntriesRead,
out int TotalEntries,
IntPtr ResumeHandle
);
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint NetApiBufferFree(IntPtr buffer);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct USE_INFO_0
{
string ui0_local;
string ui0_remote;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USE_INFO_2
{
public string ui2_local;
public string ui2_remote;
public string ui2_password;
public uint ui2_status;
public uint ui2_asg_type;
public uint ui2_refcount;
public uint ui2_usecount;
public string ui2_username;
public string ui2_domainname;
}
User contributions licensed under CC BY-SA 3.0