GetWindowText hangs on Windows 10 - c#

TL;DR: Did GetWindowText win32 api change behavior on windows 10?
I have some code that loops through all windows on the desktop to find a window where the title contains some text.
When this code hits a window named "" with class "URL Moniker Notification Window" it hangs on GetWindowText.
GetWindowText is trying to send a message, my guess is WM_GETTEXT.
This window is part of the exe "SearchUI.exe", the process is suspended and can't process messages.
When reading: https://blogs.msdn.microsoft.com/oldnewthing/20030821-00/?p=42833/
according to rule 2, this should not happen.
This code has been working fine for years. (win 7, 8, 8.1)
So did GetWindowText change behavior in Windows 10?
Update:
The code in question.
public static int HwndGet(string partialTitle, string klassenavn)
{
partialTitle = partialTitle ?? "";
var cTitleTemp = new StringBuilder(255);
var hWndTemp = FindWindowEx((IntPtr)0, (IntPtr)0, null, null);
var nypartialTitle = partialTitle.ToUpper();
while (hWndTemp != (IntPtr)0)
{
GetWindowText(hWndTemp, cTitleTemp, cTitleTemp.Capacity);
string sTitleTemp = cTitleTemp.ToString();
sTitleTemp = sTitleTemp.ToUpper();
if (sTitleTemp.StartsWith(nypartialTitle, StringComparison.CurrentCultureIgnoreCase))
{
var className = new StringBuilder(255);
GetClassName(hWndTemp, className, 255);
//sTitleTemp: " + sTitleTemp + " ClassName: " + ClassName);
if (className.ToString().StartsWith(klassenavn, StringComparison.CurrentCultureIgnoreCase))
{
return (int)hWndTemp;
}
}
hWndTemp = GetWindow(hWndTemp, GwHwndnext);
}
return 0; // does not find the window
}
Stack trace:

The code you are using isn't "safe". There is no guarantee that the order of the windows won't change between calls to FindWindowsEx and GetWindow(GwHwndnext). For this reason there is another API, EnumWindows, that is "safe". You could try with it.
Here there is a sample program (based on the one found here).
public static class WndSearcher
{
public static IntPtr SearchForWindow(string wndclass, string title)
{
var sd = new SearchData { Wndclass = wndclass, Title = title };
EnumWindows(sd.EnumWindowsProc, IntPtr.Zero);
return sd.hWndFound;
}
private class SearchData
{
// You can put any dicks or Doms in here...
public string Wndclass;
public string Title;
public IntPtr hWndFound;
public bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
// Check classname and title
var sb = new StringBuilder(1024);
int res = GetClassName(hWnd, sb, sb.Capacity);
if (res == 0)
{
throw new Win32Exception();
}
if (sb.ToString().StartsWith(Wndclass, StringComparison.CurrentCultureIgnoreCase))
{
sb.Clear();
res = GetWindowText(hWnd, sb, sb.Capacity);
if (res == 0)
{
int error = Marshal.GetLastWin32Error();
if (error != 0)
{
throw new Win32Exception(error);
}
}
if (sb.ToString().StartsWith(Title, StringComparison.CurrentCultureIgnoreCase))
{
hWndFound = hWnd;
// Found the wnd, halt enumeration
return false;
}
}
return true;
}
}
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
}
and then use it like
IntPtr ptr = WndSearcher.SearchForWindow("classname", "windowname");

Related

Get real path from symlink C#

Does anybody know how can get real path from symlink file or folder? Thank you!
Hello guys after my research I found this solution for how to get real path of a Symlink. If you have a created symlink and want to check where is the real pointer of this file or folder. If someone have better way to write it please share.
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int GetFinalPathNameByHandle([In] SafeFileHandle hFile, [Out] StringBuilder lpszFilePath, [In] int cchFilePath, [In] int dwFlags);
private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
public static string GetRealPath(string path)
{
if (!Directory.Exists(path) && !File.Exists(path))
{
throw new IOException("Path not found");
}
SafeFileHandle directoryHandle = CreateFile(path, 0, 2, IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); //Handle file / folder
if (directoryHandle.IsInvalid)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
StringBuilder result = new StringBuilder(512);
int mResult = GetFinalPathNameByHandle(directoryHandle, result, result.Capacity, 0);
if (mResult < 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
if (result.Length >= 4 && result[0] == '\\' && result[1] == '\\' && result[2] == '?' && result[3] == '\\')
{
return result.ToString().Substring(4); // "\\?\" remove
}
return result.ToString();
}

SendInput not working after attaching thread input to a target process

I'm making an application that needs to work with the UI of a program which doesn't seem to implement UI Automation elements (Inspect.Exe only shows the main pane and no children).
So I researched about what the best ways to implement the features I need were, and found SendInput(), which apparently is a newer version of keybd_event() and mouse_event().
However, since it requires keyboard focus and since I can't afford to set the target window to foreground (to avoid bothering the user while it runs), I kept searching until I found this answer. I did what Skurmedel said, and joined my application's thread to the target's window thread. But now, even if I SetFocus() to the target and then SendInput(), the target window won't be affected.
My question either is "Why doesn't this work?" or "What am I doing wrong?", but I guess a code example will help sorting this out:
ThreadHandler class
class ThreadHandler
{
#region P/Invoking and constants definition
const uint WM_GETTEXT = 0x000D;
[DllImport("user32.dll")]
static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, uint lpdwProcessId = 0);
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
IntPtr lParam);
[DllImport("user32.dll")]
static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, StringBuilder lParam);
#endregion
public readonly string ProcessName, WindowName;
protected readonly int TargetThreadID, CurrentThreadID;
protected readonly IntPtr TargetWindowHandle;
public ThreadHandler(string processName, string windowName)
{
CurrentThreadID = GetCurrentThreadId();
ProcessName = processName;
WindowName = windowName;
object[] objs = GetWindowThread(processName, windowName);
if (objs == null)
{
throw new ArgumentException("Could not find the specified process/window.");
}
TargetThreadID = (int)objs[0];
TargetWindowHandle = (IntPtr)objs[1];
}
public ThreadHandler(string processName)
{
CurrentThreadID = GetCurrentThreadId();
ProcessName = processName;
var processes = Process.GetProcessesByName(ProcessName);
if (processes.Length == 0)
{
throw new ArgumentException("Could not find the specified process.");
}
var appProc = processes[0];
WindowName = appProc.MainWindowTitle;
TargetThreadID = GetWindowThreadProcessId(appProc.MainWindowHandle);
TargetWindowHandle = appProc.MainWindowHandle;
}
public bool AttachThreadInput()
{
return AttachThreadInput(CurrentThreadID, TargetThreadID, true);
}
public bool DetachThreadInput()
{
return AttachThreadInput(CurrentThreadID, TargetThreadID, false);
}
public void SetFocus()
{
SetFocus(TargetWindowHandle);
}
static object[] GetWindowThread(string processName, string windowName)
{
var processes = Process.GetProcessesByName(processName);
if (processes.Length > 0)
{
//Fill a list of handles
var handles = new List<IntPtr>();
foreach (ProcessThread thread in processes[0].Threads)
EnumThreadWindows(thread.Id,
(hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
//Create a stringbuilder to function as storage unit
StringBuilder nameBuffer = new StringBuilder(64);
foreach (var hWnd in handles)
{
//And finally compare the caption of the window with the requested name
nameBuffer.Clear();
SendMessage(hWnd, WM_GETTEXT, nameBuffer.Capacity, nameBuffer);
if (nameBuffer.ToString() == windowName)
{
return new object[2] { GetWindowThreadProcessId(hWnd), hWnd };
}
}
}
return null;
}
}
Main method of the application
static void Main(string[] args)
{
Console.WriteLine("Please input the name of the process to hook: ");
string pName = Console.ReadLine();
Console.WriteLine("Input the name of a specific window, or leave blank: ");
string pWnd = Console.ReadLine();
ThreadHandler threadHandler;
try
{
if(!String.IsNullOrWhiteSpace(pWnd))
threadHandler = new ThreadHandler(pName, pWnd);
else
threadHandler = new ThreadHandler(pName);
}
catch
{
Console.WriteLine("Error: " + pName +" does not seem to be running.");
Console.ReadKey();
return;
}
if (!threadHandler.AttachThreadInput())
{
Console.WriteLine("Error: The application tried to attach its Input Processing Mechanism to " + threadHandler.ProcessName + ", but failed.");
Console.ReadKey();
return;
}
Console.WriteLine("Input Processing Mechanism correctly attached to " + threadHandler.ProcessName + ".");
threadHandler.SetFocus();
InputSimulator.SimulateTextEntry("test"); //InputSimulator is a seemingly famous SendInput wrapper. Replacing this line with the code for a keystroke also doesn't work.
Console.ReadLine();
Console.WriteLine("Detaching Input Processing Mechanism.");
threadHandler.DetachThreadInput();
}
Thanks in advance if you can elucidate me on the arcane arts of SendInput().
Make sure the specific control you are sending the keystrokes to is properly focused.
You should be able to use SetFocus to give focus to the control you are sending the keystrokes to.
SendMessage and PostMessage can also be used to send keystrokes, but it's BAD PRACTICE and should be avoided.
Check out System.Windows.Forms.SendKeys for information on sending keystrokes though the Forms class in .NET.
In a lot of cases, if you don't need the keystrokes themselves, you can just change the text on a window using SendMessage with WM_SETTEXT if this is what you're looking to do.

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);

I successfully called advapi32's LsaEnumerateAccountRights() from C#. Now how do I unmarshal the array of LSA_UNICODE_STRING it returns?

It's a pointer to an array of LSA_UNICODE_STRING structures. I found some code that does the inverse, i.e., create a LSA_UNICODE_STRING from a C# string. You can see that in the helper code section below.
What I have up to and including the call to LsaEnumerateAccountRights() seems to work just fine. Sensible values are returned for the array pointer and for the count.
I am at a loss as to how to get at those blasted strings. Help please? Pretty please?
UPDATE: nobugz's helper function in his answer below is ALMOST right, you only have to divide the length by UnicodeEncoding.CharSize. Thanks to him, I can now see the FIRST string in the array. See the updates at the end of both code sections below.
Now, how the netherworld do I do pointer arithmetic?
UPDATE 2.5: See answer for the functioning code. I lost the old, "wrong" code.
Found it! In this blog post. Now the amended code below works fully. It's even 64-bit safe!
The main code:
IntPtr sid = IntPtr.Zero;
int sidSize = 0;
StringBuilder domainName = new StringBuilder();
int nameSize = 0;
int accountType = 0;
LookupAccountName("\\\\" + tbHost.Text, tbUsername.Text, sid, ref sidSize,
domainName, ref nameSize, ref accountType);
domainName = new StringBuilder(nameSize);
sid = Marshal.AllocHGlobal(sidSize);
bool result = LookupAccountName("\\\\" + tbHost.Text, tbUsername.Text, sid, ref sidSize,
domainName, ref nameSize, ref accountType);
myResults.Text += String.Format("LookupAccountName(): Result {0}, SID {1}\n", result, sid);
LSA_UNICODE_STRING systemName = string2LSAUS("\\\\" + tbHost.Text);
IntPtr policyHandle = IntPtr.Zero;
LSA_OBJECT_ATTRIBUTES objAttrs = new LSA_OBJECT_ATTRIBUTES();
uint retVal = LsaOpenPolicy(ref systemName, ref objAttrs,
POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, out policyHandle);
myResults.Text += String.Format("LsaOpenPolicy(): Result {0}, Policy Handle {1}\n", retVal, policyHandle);
IntPtr rightsArray = IntPtr.Zero;
ulong rightsCount = 0;
long lretVal = LsaEnumerateAccountRights(policyHandle, sid, out rightsArray, out rightsCount);
retVal = LsaNtStatusToWinError(lretVal);
if (retVal != 0)
throw new System.ComponentModel.Win32Exception((int)retVal);
myResults.Text += String.Format("LsaEnumerateAccountRights(): Result {0}, RightsArray {1}, Count {2}\n",
retVal, rightsArray, rightsCount);
LSA_UNICODE_STRING myLsaus = new LSA_UNICODE_STRING();
for (ulong i = 0; i < rightsCount; i++)
{
IntPtr itemAddr = new IntPtr(rightsArray.ToInt64() + (long)(i * (ulong) Marshal.SizeOf(myLsaus)));
myLsaus = (WinNetUtils.LSA_UNICODE_STRING)Marshal.PtrToStructure(itemAddr, myLsaus.GetType());
string thisRight = WinNetUtils.LSAUS2string(myLsaus);
NonBlockingPrint(wmiResults, "Right #{0}: {1}\n", i+1, thisRight);
}
LsaClose(policyHandle);
The helper functions, imports etc:
public const int POLICY_VIEW_LOCAL_INFORMATION = 0x1;
public const int POLICY_LOOKUP_NAMES = 0x00000800;
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, PreserveSig = true)]
public static extern UInt32 LsaNtStatusToWinError(
long Status);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = true)]
public static extern bool ConvertStringSidToSid(
string StringSid, out IntPtr pSid);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = true)]
public static extern bool LookupAccountName(
string lpSystemName, string lpAccountName,
IntPtr psid, ref int cbsid,
StringBuilder domainName, ref int cbdomainLength,
ref int use );
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, PreserveSig = true)]
public static extern UInt32 LsaOpenPolicy(
ref LSA_UNICODE_STRING SystemName,
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
Int32 DesiredAccess,
out IntPtr PolicyHandle );
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern long LsaEnumerateAccountRights(
IntPtr PolicyHandle, IntPtr AccountSid,
out /* LSA_UNICODE_STRING[] */ IntPtr UserRights,
out ulong CountOfRights);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern long LsaClose(
IntPtr PolicyHandle);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LSA_UNICODE_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LSA_OBJECT_ATTRIBUTES
{
public IntPtr RootDirectory;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
public LSA_UNICODE_STRING ObjectName;
public UInt32 Attributes;
public UInt32 Length;
}
public static LSA_UNICODE_STRING string2LSAUS(string myString)
{
LSA_UNICODE_STRING retStr = new LSA_UNICODE_STRING();
retStr.Buffer = Marshal.StringToHGlobalUni(myString);
retStr.Length = (UInt16)(myString.Length * UnicodeEncoding.CharSize);
retStr.MaximumLength = (UInt16)((myString.Length + 1) * UnicodeEncoding.CharSize);
return retStr;
}
public static string LSAUS2string(LSA_UNICODE_STRING lsaus)
{
char[] cvt = new char[lsaus.Length / UnicodeEncoding.CharSize];
Marshal.Copy(lsaus.Buffer, cvt, 0, lsaus.Length / UnicodeEncoding.CharSize);
return new string(cvt);
}
This ought to work for you:
private static string LSAUS2String(LSA_UNICODE_STRING lsa) {
char[] cvt = new char[lsa.Length];
Marshal.Copy(lsa.Buffer, cvt, 0, lsa.Length);
return new string(cvt);
}

Categories

Resources