How do I PInvoke RpcMgmtEpEltInqNext? - c#

The search term RpcMgmtEpEltInqNext on both Stackoverflow and PInvoke.net yields zero results, so this should be worth asking. I've been fiddling around on MSDN and looking through Win SDK *.h files all day, and feeling a bit out of my element.
I'm basically attempting to query the MSRPC endpoint mapper with managed code. Here is what I have so far:
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcBindingFromStringBinding(string StringBinding, out IntPtr Binding);
[DllImport("Rpcrt4.dll")]
public static extern int RpcBindingFree(ref IntPtr Binding);
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqBegin(IntPtr EpBinding,
int InquiryType, // 0x00000000 = RPC_C_EP_ALL_ELTS
int IfId,
int VersOption,
string ObjectUuid,
out IntPtr InquiryContext);
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqNext(ref IntPtr InquiryContext,
out int IfId,
out IntPtr Binding,
out string ObjectUuid,
out string Annotation);
public static List<int> QueryEPM(string host)
{
List<int> ports = new List<int>();
int retCode = 0; // RPC_S_OK
IntPtr bindingHandle = IntPtr.Zero;
IntPtr inquiryContext = IntPtr.Zero;
IntPtr elementBindingHandle = IntPtr.Zero;
int elementIfId = 0;
string elementUuid = string.Empty;
string elementAnnotation = string.Empty;
try
{
retCode = RpcBindingFromStringBinding("ncacn_ip_tcp:" + host, out bindingHandle);
if (retCode != 0)
throw new Exception("RpcBindingFromStringBinding: " + retCode);
retCode = RpcMgmtEpEltInqBegin(bindingHandle, 0, 0, 0, string.Empty, out inquiryContext);
if (retCode != 0)
throw new Exception("RpcMgmtEpEltInqBegin: " + retCode);
retCode = RpcMgmtEpEltInqNext (ref inquiryContext, out elementIfId, out elementBindingHandle, out elementUuid, out elementAnnotation);
if (retCode != 0)
throw new Exception("RpcMgmtEpEltInqNext: " + retCode);
}
The above code isn't complete, but it illustrates my point. The code consistently returns:
RpcMgmtEpEltIngNext: 87
87 meaning Parameter is Incorrect according to here.
So I'm basically stumped at this point, and I'm sure it's because of my extremely crappy PInvoke code.

The p/invoke declaration is wrong. It needs to be:
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqNext(
IntPtr InquiryContext,
out RPC_IF_ID IfId,
out IntPtr Binding,
out Guid ObjectUuid,
out IntPtr Annotation
);
The first parameter must be passed by value.
You'll need to translate the RPC_IF_ID struct. It's quite a simple one.
public struct RPC_IF_ID
{
public Guid Uuid;
public ushort VersMajor;
public ushort VersMinor;
}
The two string parameters you used were just wrong. They lead to the marshaller calling CoTaskMemFree on the returned value for Annotation. You don't want that. You will need to use Marshal.PtrToStringAnsi to read the value.
Don't forget to call RpcBindingFree and RpcStringFree as per the docs.

Related

NetFileEnum returns ERROR_MORE_DATA

Attempting to retrieve a large list of open files from a file server returns the ERROR_MORE_DATA value (Error number 234), but works fine when dealing with only a small list of files (seems to return around 84 entries). This code is based upon the example at: http://pinvoke.net/default.aspx/netapi32/NetFileEnum.html
Most examples I have found don't really touch on how to handle a large number of files. From my understanding, this has something to do with the resume_handle, however I'm not sure what needs to be done. Do I need to somehow call this method in a loop?
Code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace OpenFiles
{
class Program
{
public static string computername = "computername";
static void Main(string[] args)
{
List<string> theFileList = NativeMethods.GetFiles(computername);
foreach (string file in theFileList)
{
Console.WriteLine(file);
}
}
}
static class NativeMethods
{
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileEnum(
string servername,
string basepath,
string username,
int level,
ref IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
IntPtr resume_handle
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct FILE_INFO_3
{
public int fi3_id;
public int fi3_permission;
public int fi3_num_locks;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_pathname;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_username;
}
[DllImport("Netapi32.dll", SetLastError = true)]
static extern int NetApiBufferFree(IntPtr Buffer);
public static List<string> GetFiles(string Computername)
{
const int MAX_PREFERRED_LENGTH = -1;
int dwReadEntries;
int dwTotalEntries;
IntPtr pBuffer = IntPtr.Zero;
FILE_INFO_3 pCurrent = new FILE_INFO_3();
List<string> fileList = new List<string>();
int dwStatus = NetFileEnum(Computername, null, null, 3, ref pBuffer, MAX_PREFERRED_LENGTH, out dwReadEntries, out dwTotalEntries, IntPtr.Zero);
if (dwStatus == 0)
{
for (int dwIndex = 0; dwIndex < dwReadEntries; dwIndex++)
{
IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (dwIndex * Marshal.SizeOf(pCurrent)));
pCurrent = (FILE_INFO_3)Marshal.PtrToStructure(iPtr, typeof(FILE_INFO_3));
string fileInfo = pCurrent.fi3_id + "," +
pCurrent.fi3_num_locks + "," +
pCurrent.fi3_pathname + "," +
pCurrent.fi3_permission + "," +
pCurrent.fi3_username;
fileList.Add(fileInfo);
}
NetApiBufferFree(pBuffer);
}
else
{
Console.WriteLine("error: " + dwStatus);
}
return fileList;
}
}
}
In my limited experience a very large number of results can be larger than the max buffer. In this case a more data response is given and instructs us to call again with the provided resume handle. In your example, the resume handle will not be changed because the DllImport signature doesn't define it as an out parameter. Using the resume handle result from the first call (passing in zero means first call) allows you to receive the next batch. Loop until you receive a success response or some other error.
Be sure to fix the issue where the NetFileEnum signature is defined.
The resume handle is not defined with an out so cannot be changed by the called function.
Try the following DLL Import signature instead:
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileEnum(
string servername,
string basepath,
string username,
int level,
ref IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
out IntPtr resume_handle
);
You should be able to re-call NetFileEnum with the resulting resume handle multiple times when you get a more data response.

How to know if two hard links point to the same inode? (C#)

Anyway of checking, in C#, that two files (hard links) point to the same inode? And also get the count of this inode, in case there are more than two... ?
You can get count of hard links pointing to the node using GetFileInformationByHandle function. For example:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetFileInformationByHandle(
SafeFileHandle hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[StructLayout(LayoutKind.Sequential)]
struct BY_HANDLE_FILE_INFORMATION {
public uint FileAttributes;
public FILETIME CreationTime;
public FILETIME LastAccessTime;
public FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
}
// then in another place
using (var fs = File.OpenRead("path to your file")) {
BY_HANDLE_FILE_INFORMATION info;
GetFileInformationByHandle(fs.SafeFileHandle, out info);
var numberOfLinks = info.NumberOfLinks;
}
To get what files they are pointing to, you will need another win api functions: FindFirstFileNameW and FineNextFileNameW. Use them like this:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr FindFirstFileNameW(
string lpFileName,
uint dwFlags,
ref uint stringLength,
StringBuilder fileName);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool FindNextFileNameW(
IntPtr hFindStream,
ref uint stringLength,
StringBuilder fileName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FindClose(IntPtr fFindHandle);
public static string[] GetFileHardLinks(string filePath) {
// first get drive letter
var drive = new DriveInfo(Path.GetPathRoot(filePath));
var result = new List<string>();
// buffer for return value
var sb = new StringBuilder(256);
// length of buffer
uint sbLength = 256;
// third argument contains reference to buffer length (buffer is StringBuilder).
// it's a reference because if it's too small, call returns an error and will put required length there instead
IntPtr findHandle = FindFirstFileNameW(filePath, 0, ref sbLength, sb);
// returns -1 on error
if (findHandle.ToInt64() != -1) {
do {
// combine the result with drive letter (it comes without it)
result.Add(Path.Combine(drive.RootDirectory.FullName, sb.ToString().TrimStart(new [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar})));
sb.Clear();
sbLength = 256;
// and repeat
} while (FindNextFileNameW(findHandle, ref sbLength, sb));
FindClose(findHandle);
return result.ToArray();
}
return null;
}
This code might be not production ready, so take care. But it should at least give you an idea. If you will use it - carefully read what those function return on errors and act accordingly (for example, handle the case when buffer length is not enough, or just use larger buffer than 256).

Pinvoke ONLY code for getting proccess via its name

as i was trying to have a test and learn about native p/invoke functions i was trying to use only pinvoke and then compare the time it takes to get process info with .net simple
Process myProc = Process.GetProcessByName("WinRAR");
though i feel that i need to realy measure that almost 2 pages in length code, using P/invoke just so i could get same results, but this time ONLY with native code, i guess that it should be faster and i want to atleast get to benchmark both ,so please help here .
so it seems that my code is 1) ... ok i guess i could count to 20
"enumerating" all it's issues, but mainly :
it doesn't enumerate all processes for a strange reason i did not see winrar for instance
second it is far from being as short as pinvoke bunche-of-methods needs
(i am using Winforms app, though you could hard code the ProcessName needed in order to "search" for the correct process)
most of comments here is by the author of well, most parts of the code
i only modified it a little to have enum later so you could choose between searching via window title or process name
so this is the code:
main entry - create instance of class :
pinvokers Pi = new pinvokers();
// Find all Internet Explorer instances(i used winrar, as my second task in this project is also test application performance... later on, and again, using only native calls)
Pi.FindWindows(0, pinvokers.SearchWin.ProcName, null, new Regex(TBX_SelectedWinName.Text), new pinvokers.FoundWindowCallback(pinvokers.foundWindowToPrint));
public class pinvokers
{
// Win32 constants.
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.Dll")]
private static extern Boolean EnumChildWindows(int hWndParent, PChildCallBack lpEnumFunc, int lParam);
[DllImport("user32.Dll")]
private static extern int GetWindowText(int hWnd, StringBuilder text, int count);
[DllImport("user32.Dll")]
private static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
[DllImport("user32.Dll")]
private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
[DllImport("user32.Dll")]
private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, int lParam);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetWindowModuleFileName(IntPtr hwnd,
StringBuilder lpszFileName, uint cchFileNameMax);
[DllImport("psapi.dll")]
private static extern uint GetModuleFileNameEx(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize);
// The PChildCallBack delegate that we used with EnumWindows.
private delegate bool PChildCallBack(int hWnd, int lParam);
// This is an event that is run each time a window was found that matches the search criterias. The boolean
// return value of the delegate matches the functionality of the PChildCallBack delegate function.
static event FoundWindowCallback foundWindowCB;
public delegate bool FoundWindowCallback(int hWnd);
int parentHandle;
Regex process;
#region <<=========== not nedded - search by window title. i am looking to search via process name ===========>>
/* <- commented all unsuesd
Regex windowText;
public static bool foundWindowToPrint(int handle)
{
// Print the window info.
printWindowInfo(handle);
// Continue on with next window.
return true;
}
static void printWindowInfo(int handle)
{
// Get the text.
int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0);
StringBuilder sbText = new StringBuilder(txtLength + 1);
SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText);
// Now we can write out the information we have on the window.
MessageBox.Show("Handle: " + handle);
MessageBox.Show("Text : " + sbText);
}
=====>end of un needed search bywindowtitle1
*/
#endregion
// my plan was to use enum instead of if !empty or null value for ither title name or process name so that's how the original code ditermin wich one to execute.
public enum SearchWin
{
Title, ProcName
}
//first method (and that's all i could really tell.. as it is full of callbacks and private extern, and delegates ... so complex
public void FindWindows(int parentHandle, SearchWin By, Regex windowText, Regex process, FoundWindowCallback fwc)
{
this.parentHandle = parentHandle;
//this.windowText = windowText;
this.process = process;
// Add the FounWindowCallback to the foundWindow event.
foundWindowCB = fwc;
// Invoke the EnumChildWindows function.
EnumChildWindows(parentHandle, new PChildCallBack(enumChildWindowsCallback), 0);
}
// This function gets called each time a window is found by the EnumChildWindows function. The foun windows here
// are NOT the final found windows as the only filtering done by EnumChildWindows is on the parent window handle.
private bool enumChildWindowsCallback(int handle, int lParam)
{
#region <<=========== not nedded - search by window title. #2 ===========>>
/* <--here too window title portion of code commented
// If a window text was provided, check to see if it matches the window.
if (windowText != null)
{
int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0);
StringBuilder sbText = new StringBuilder(txtLength + 1);
SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText);
// If it does not match, return true so we can continue on with the next window.
if (!windowText.IsMatch(sbText.ToString()))
return true;
}
*/
#endregion //endr2
// If a process name was provided, check to see if it matches the window.
if (process != null)
{
int processID;
GetWindowThreadProcessId(handle, out processID);
// Now that we have the process ID, we can use the built in .NET function to obtain a process object.
var ProcessName = GetProcNameByID(processID);
// If it does not match, return true so we can continue on with the next window.
if (!process.IsMatch(ProcessName))
return true;
}
// If we get to this point, the window is a match. Now invoke the foundWindow event and based upon
// the return value, whether we should continue to search for windows.
return foundWindowCB(handle);
}
private string GetProcNameByID(int ProcID)
{
IntPtr hProcess = OpenProcess(0x0410, false, ProcID);
StringBuilder text = new StringBuilder(1000);
GetWindowModuleFileName(hProcess, text, (uint)text.Capacity);
//GetModuleFileNameEx(hProcess, IntPtr.Zero, text, text.Capacity);
//CloseHandle(hProcess); here i am trying to catch what enumeration of windows got in its net , all this code does work just copy and paste it .
var t = text.ToString();
if (t.ToLower().Contains("inra"))
MessageBox.Show(t);
return t;
}
}
so could this be a little shorter is a side question
main one is :
Why does it not enumerate all the processes ?
i don't know if it is the best i could get or maybe someone who knows what he is doing with win api, or p/invoke or if i had to try and make unmanagedLand win over .net built in calsses
i might have rolled my sleeves and put some c++ code together (will probbably take another week)
and compile it to a dll to get all functions together in one DLL (should it do some perfomance gain)
and then i might have cut some gap .
(by the way now it is much closer to system diagnostic results thogh i thought it will be much faster and i was wrong)
but still it was only for knowing i am safe to use .net C#
and to trust microsoft for knowing much better than me (: how to make a good proggraming language.
this is the code i was using to make it through all those dllllls import. i should have known that import anything and it costs( here it might be the import tax that is costely)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void But_StartPinvoke_Click(object sender, EventArgs e)
{
var userInputOK = TBX_SelectedProcessName.userInput();
if(!userInputOK)
MessageBox.Show(RApss.mesgs.EmptyTbx);
RApss.Strings.UserInput = TBX_SelectedProcessName.Text;
RApss.Globs.TbxPname = TBX_SelectedProcessName.Text.AddSufixEXE();
doWarmUp();
Stopwatch SwGpbn = Stopwatch.StartNew();
SwGpbn.Start();
//string _netProcName = Process.GetProcessesByName(RApss.Strings.UserInput)[0].ProcessName;
Process p = Process.GetProcessesByName(RApss.Strings.UserInput)[0];
if (p.ProcessName.ResultFetched())
SwGpbn.Stop();
var msElps_Net4 = SwGpbn.ElapsedMilliseconds;
SwGpbn.Reset();
SwGpbn.Start();
EnumProcessesV3.GetProcessByName();
SwGpbn.Stop();
var msElpsNat = SwGpbn.ElapsedMilliseconds;
SwGpbn.Reset();
SwGpbn.Reset();
if (RApss.Globs.Result.ResultFetched()) MessageBox.Show(string.Concat(RApss.Globs.Result, "\r\nWas Fetched In: ", msElpsNat, " Via PinVoke\r\n Was Fetched In: ", msElps_Net4," Via C#.NET !" ));
}
private void doWarmUp()
{
List<string> swarm = new List<string>();
for (int i = 0; i < 50000; i++)
{
swarm.Add((i + 1 *500).ToString());
}
}
}
public class RApss
{
public class Globs
{
public static string TbxPname;
public static string Result = string.Empty;
}
public class Strings
{
public static string intputForProcessName = "Requiered Process Name";
public static string UserInput = string.Empty;
}
public class mesgs
{
public static string EmptyTbx = string.Concat("please fill ", Strings.intputForProcessName, " field");
}
}
public class EnumProcessesV3
{
#region APIS
[DllImport("psapi")]
private static extern bool EnumProcesses(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] IntPtr[] processIds,
UInt32 arraySizeBytes,
[MarshalAs(UnmanagedType.U4)] out UInt32 bytesCopied);
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, IntPtr dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("psapi.dll")]
static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
[DllImport("psapi.dll", SetLastError = true)]
public static extern bool EnumProcessModules(IntPtr hProcess,
[Out] IntPtr lphModule,
uint cb,
[MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);
[DllImport("psapi.dll")]
static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
#endregion
#region ENUMS
[Flags]
enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
#endregion
public static void GetProcessByName()
{
UInt32 arraySize = 120;
UInt32 arrayBytesSize = arraySize * sizeof(UInt32);
IntPtr[] processIds = new IntPtr[arraySize];
UInt32 bytesCopied;
bool success = EnumProcesses(processIds, arrayBytesSize, out bytesCopied);
#region <<=========== some cleanUps ============>>
// trying to check what could have been taking extra mssssnds
//Console.WriteLine("success={0}", success);
//Console.WriteLine("bytesCopied={0}", bytesCopied);
//if (!success)
//{
// MessageBox.Show("Boo!");
// return;
//}
//if (0 == bytesCopied)
//{
// MessageBox.Show("Nobody home!");
// return;
//}
#endregion
UInt32 numIdsCopied = bytesCopied >> 2;
#region <<===========same here commenting anything that might cost nerowing the options ============>>
//if (0 != (bytesCopied & 3))
//{
// UInt32 partialDwordBytes = bytesCopied & 3;
// MessageBox.Show(String.Format("EnumProcesses copied {0} and {1}/4th DWORDS... Please ask it for the other {2}/4th DWORD",
// numIdsCopied, partialDwordBytes, 4 - partialDwordBytes));
// return;
//}
//taking initialisation of SB out of loop was a winning thought but nada no change maybe in nanos
#endregion
for (UInt32 index = numIdsCopied; index> 1 ; index--) // reversing from last process id(chitting) to erlier process id did not help to win the contest
{
StringBuilder szProcessName = new StringBuilder(1000);
int x = szProcessName.Capacity;
string sName = PrintProcessName(processIds[index-1],szProcessName,x);
if (sName.Equals(RApss.Globs.TbxPname)) // tryng hardcoded value instead of reading from a variable.(GlobalsClass)
{
RApss.Globs.Result = sName;
break;
}
////////IntPtr PID = processIds[index];
////////Console.WriteLine("Name '" + sName + "' PID '" + PID + "'");
}
}
static string PrintProcessName(IntPtr processID, StringBuilder sb, int Cpcty)
{
string sName = "";
//bool bFound = false;
IntPtr hProcess = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead, false, processID);
if (hProcess != IntPtr.Zero)
{
IntPtr hMod = IntPtr.Zero;
uint cbNeeded = 0;
EnumProcessModules(hProcess, hMod, (uint)Marshal.SizeOf(typeof(IntPtr)), out cbNeeded);
if (GetModuleBaseName(hProcess, hMod, sb, Cpcty) > 0)
{
sName = sb.ToString();
//bFound = true;
}
// Close the process handle
CloseHandle(hProcess);
}
//if (!bFound)
//{
// sName = "<unknown>";
//}
return sName;
}
}
}
namespace RExt
{
public static class UserInputs
{
public static bool userInput(this TextBox tbxId)
{
return tbxId.Text.Length > 1;
}
}
public static class strExt
{
public static bool ResultFetched(this string StrToCheck)
{
return !string.IsNullOrWhiteSpace(StrToCheck);
}
public static string AddSufixEXE(this string StrToAppendEXE)
{
return string.Concat(StrToAppendEXE, ".exe");
}
}
}
if thats not working, make sure the project is targeting x86 CPU and Rebuild
for some reason i did not check what is needed to make it suit both x64 & x86

What is wrong with this pinvoke?

I have this piece of code that has not been modified but all of a sudden it has stopped working... I could swear that it used to work but can't guarantee it. It throws an exception:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
static void Main(string[] args)
{
ErrorMsg(123);
}
[DllImport("kernel32.dll", EntryPoint = "FormatMessageW", CharSet = CharSet.Auto)]
static extern int FormatMessage(int dwFlags, IntPtr lpSource, long dwMessageId, int dwLanguageId, out IntPtr MsgBuffer, int nSize, IntPtr Arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetThreadLocale();
/// <summary>
/// Gets a Locale specific windows error
/// code specified.
/// </summary>
/// <param name="errorcode">The errorcode.</param>
public static string ErrorMsg(long errorcode)
{
try
{
if (errorcode == 0)
return "No Error";
IntPtr pMessageBuffer;
int dwBufferLength;
string sMsg;
int dwFormatFlags;
//FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
dwFormatFlags = 0x00000100 | 0x00000200 | 0x00001000;
dwBufferLength = FormatMessage(dwFormatFlags, IntPtr.Zero, errorcode, GetThreadLocale(), out pMessageBuffer, 0, IntPtr.Zero);
if (dwBufferLength == 0)
return "An Unknown Error Has occured.";
sMsg = Marshal.PtrToStringUni(pMessageBuffer);
Marshal.FreeHGlobal(pMessageBuffer);
return sMsg;
}
catch (Exception ex)
{
return "An Unknown Error Has occured.";
}
}
What am I doing wrong here, I can't seem to find anything? Thanks!
Your code worked fine when I tested it on my machine. By the way is there any reason you wouldn't prefer the following method which is a little shorter and achieves equivalent goal:
static void Main()
{
var ex = new Win32Exception(123);
Console.WriteLine(ex.Message);
}
Of course under the covers Win32Exception PInvokes into FormatMessage but at least it's the .NET framework that should worry about it, not us.
UPDATE:
Here's how the Win32Exception.GetErrorMessage method is implemented in .NET:
private static string GetErrorMessage(int error)
{
string result = "";
StringBuilder stringBuilder = new StringBuilder(256);
int num = SafeNativeMethods.FormatMessage(12800, NativeMethods.NullHandleRef, error, 0, stringBuilder, stringBuilder.Capacity + 1, IntPtr.Zero);
if (num != 0)
{
int i;
for (i = stringBuilder.Length; i > 0; i--)
{
char c = stringBuilder[i - 1];
if (c > ' ' && c != '.')
{
break;
}
}
result = stringBuilder.ToString(0, i);
}
else
{
result = "Unknown error (0x" + Convert.ToString(error, 16) + ")";
}
return result;
}
where FormatMessage is declared like this:
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr arguments);
Try sMsg = Marshal.PtrToStringUni(pMessageBuffer, dwBufferLength);

How to call NetUserModalsGet() from C#.NET?

EDIT: followup at NetUserModalsGet() returns strings incorrectly for C#.NET
I'm struggling with the DLL declarations for this function:
NET_API_STATUS NetUserModalsGet(
__in LPCWSTR servername,
__in DWORD level,
__out LPBYTE *bufptr
);
(Reference: http://msdn.microsoft.com/en-us/library/aa370656%28VS.85%29.aspx)
I tried this:
private string BArrayToString(byte[] myArray)
{
string retVal = "";
if (myArray == null)
retVal = "Null";
else
{
foreach (byte myByte in myArray)
{
retVal += myByte.ToString("X2");
}
}
return retVal;
}
...
[DllImport("netapi32.dll")]
public static extern int NetUserModalsGet(
string servername,
int level,
out byte[] bufptr
);
[DllImport("netapi32.dll")]
public static extern int NetApiBufferFree(
byte[] bufptr
);
...
int retVal;
byte[] myBuf;
retVal = NetUserModalsGet("\\\\" + tbHost.Text, 0, out myBuf);
myResults.Text += String.Format("retVal={0}\nBuffer={1}\n", retVal, BArrayToString(myBuf));
retVal = NetApiBufferFree(myBuf);
I get a return value of 1231 (Network Location cannot be reached) no matter if I use an IP address or a NetBIOS name of a machine that's undoubtedly online, or even my own. On edit: this happens even if I don't put a "\\" in front of the hostname.
I'm doing things wrong, I know, and let's not even get started on how to declare that blasted return buffer (which can have a number of different lengths, ewww).
From pinvoke.net (they also have some sample code on how to use it):
[DllImport("netapi32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
static extern uint NetUserModalsGet(
string server,
int level,
out IntPtr BufPtr);
It would be better to use System.Text.Encoding.ASCII.GetBytes(somestring_param) and System.Text.Encoding.ASCII.GetString(byte_array) instead of your own routine BArrayToString.
Hope this helps.

Categories

Resources