Related
What's the best way to shut down the computer from a C# program?
I've found a few methods that work - I'll post them below - but none of them are very elegant. I'm looking for something that's simpler and natively .net.
Works starting with windows XP, not available in win 2000 or lower:
This is the quickest way to do it:
Process.Start("shutdown","/s /t 0");
Otherwise use P/Invoke or WMI like others have said.
Edit: how to avoid creating a window
var psi = new ProcessStartInfo("shutdown","/s /t 0");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi);
Taken from: a Geekpedia post
This method uses WMI to shutdown windows.
You'll need to add a reference to System.Management to your project to use this.
using System.Management;
void Shutdown()
{
ManagementBaseObject mboShutdown = null;
ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
mcWin32.Get();
// You can't shutdown without security privileges
mcWin32.Scope.Options.EnablePrivileges = true;
ManagementBaseObject mboShutdownParams =
mcWin32.GetMethodParameters("Win32Shutdown");
// Flag 1 means we want to shut down the system. Use "2" to reboot.
mboShutdownParams["Flags"] = "1";
mboShutdownParams["Reserved"] = "0";
foreach (ManagementObject manObj in mcWin32.GetInstances())
{
mboShutdown = manObj.InvokeMethod("Win32Shutdown",
mboShutdownParams, null);
}
}
This thread provides the code necessary: http://bytes.com/forum/thread251367.html
but here's the relevant code:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
[DllImport("kernel32.dll", ExactSpelling=true) ]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true) ]
internal static extern bool OpenProcessToken( IntPtr h, int acc, ref IntPtr
phtok );
[DllImport("advapi32.dll", SetLastError=true) ]
internal static extern bool LookupPrivilegeValue( string host, string name,
ref long pluid );
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true) ]
internal static extern bool AdjustTokenPrivileges( IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen );
[DllImport("user32.dll", ExactSpelling=true, SetLastError=true) ]
internal static extern bool ExitWindowsEx( int flg, int rea );
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
internal const int EWX_LOGOFF = 0x00000000;
internal const int EWX_SHUTDOWN = 0x00000001;
internal const int EWX_REBOOT = 0x00000002;
internal const int EWX_FORCE = 0x00000004;
internal const int EWX_POWEROFF = 0x00000008;
internal const int EWX_FORCEIFHUNG = 0x00000010;
private void DoExitWin( int flg )
{
bool ok;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
ok = OpenProcessToken( hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok );
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
ok = LookupPrivilegeValue( null, SE_SHUTDOWN_NAME, ref tp.Luid );
ok = AdjustTokenPrivileges( htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero );
ok = ExitWindowsEx( flg, 0 );
}
Usage:
DoExitWin( EWX_SHUTDOWN );
or
DoExitWin( EWX_REBOOT );
Different methods:
A. System.Diagnostics.Process.Start("Shutdown", "-s -t 10");
B. Windows Management Instrumentation (WMI)
http://www.csharpfriends.com/Forums/ShowPost.aspx?PostID=36953
http://www.dreamincode.net/forums/showtopic33948.htm
C. System.Runtime.InteropServices Pinvoke
http://bytes.com/groups/net-c/251367-shutdown-my-computer-using-c
D. System Management
http://www.geekpedia.com/code36_Shut-down-system-using-Csharp.html
After I submit, I have seen so many others also have posted...
Short and sweet. Call an external program:
using System.Diagnostics;
void Shutdown()
{
Process.Start("shutdown.exe", "-s -t 00");
}
Note: This calls Windows' Shutdown.exe program, so it'll only work if that program is available.
You might have problems on Windows 2000 (where shutdown.exe is only available in the resource kit) or XP Embedded.
The old-school ugly method. Use the ExitWindowsEx function from the Win32 API.
using System.Runtime.InteropServices;
void Shutdown2()
{
const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
const short SE_PRIVILEGE_ENABLED = 2;
const uint EWX_SHUTDOWN = 1;
const short TOKEN_ADJUST_PRIVILEGES = 32;
const short TOKEN_QUERY = 8;
IntPtr hToken;
TOKEN_PRIVILEGES tkp;
// Get shutdown privileges...
OpenProcessToken(Process.GetCurrentProcess().Handle,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken);
tkp.PrivilegeCount = 1;
tkp.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
LookupPrivilegeValue("", SE_SHUTDOWN_NAME, out tkp.Privileges.pLuid);
AdjustTokenPrivileges(hToken, false, ref tkp, 0U, IntPtr.Zero,
IntPtr.Zero);
// Now we have the privileges, shutdown Windows
ExitWindowsEx(EWX_SHUTDOWN, 0);
}
// Structures needed for the API calls
private struct LUID
{
public int LowPart;
public int HighPart;
}
private struct LUID_AND_ATTRIBUTES
{
public LUID pLuid;
public int Attributes;
}
private struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
public LUID_AND_ATTRIBUTES Privileges;
}
[DllImport("advapi32.dll")]
static extern int OpenProcessToken(IntPtr ProcessHandle,
int DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
UInt32 BufferLength,
IntPtr PreviousState,
IntPtr ReturnLength);
[DllImport("advapi32.dll")]
static extern int LookupPrivilegeValue(string lpSystemName,
string lpName, out LUID lpLuid);
[DllImport("user32.dll", SetLastError = true)]
static extern int ExitWindowsEx(uint uFlags, uint dwReason);
In production code you should be checking the return values of the API calls, but I left that out to make the example clearer.
Just to add to Pop Catalin's answer, here's a one liner which shuts down the computer without displaying any windows:
Process.Start(new ProcessStartInfo("shutdown", "/s /t 0") {
CreateNoWindow = true, UseShellExecute = false
});
System.Diagnostics.Process.Start("shutdown", "/s /t 0")
Should work.
For restart, it's /r
This will restart the PC box directly and cleanly, with NO dialogs.
Note that shutdown.exe is just a wrapper around InitiateSystemShutdownEx, which provides some niceties missing in ExitWindowsEx
You can launch the shutdown process:
shutdown -s -t 0 - Shutdown
shutdown -r -t 0 - Restart
I had trouble trying to use the WMI method accepted above because i always got privilige not held exceptions despite running the program as an administrator.
The solution was for the process to request the privilege for itself. I found the answer at http://www.dotnet247.com/247reference/msgs/58/292150.aspx written by a guy called Richard Hill.
I've pasted my basic use of his solution below in case that link gets old.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;
namespace PowerControl
{
public class PowerControl_Main
{
public void Shutdown()
{
ManagementBaseObject mboShutdown = null;
ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
mcWin32.Get();
if (!TokenAdjuster.EnablePrivilege("SeShutdownPrivilege", true))
{
Console.WriteLine("Could not enable SeShutdownPrivilege");
}
else
{
Console.WriteLine("Enabled SeShutdownPrivilege");
}
// You can't shutdown without security privileges
mcWin32.Scope.Options.EnablePrivileges = true;
ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");
// Flag 1 means we want to shut down the system
mboShutdownParams["Flags"] = "1";
mboShutdownParams["Reserved"] = "0";
foreach (ManagementObject manObj in mcWin32.GetInstances())
{
try
{
mboShutdown = manObj.InvokeMethod("Win32Shutdown",
mboShutdownParams, null);
}
catch (ManagementException mex)
{
Console.WriteLine(mex.ToString());
Console.ReadKey();
}
}
}
}
public sealed class TokenAdjuster
{
// PInvoke stuff required to set/enable security privileges
[DllImport("advapi32", SetLastError = true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int OpenProcessToken(
System.IntPtr ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle // handle to open access token
);
[DllImport("kernel32", SetLastError = true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int AdjustTokenPrivileges(
IntPtr TokenHandle,
int DisableAllPrivileges,
IntPtr NewState,
int BufferLength,
IntPtr PreviousState,
ref int ReturnLength);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
ref LUID lpLuid);
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
internal int LowPart;
internal int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID_AND_ATTRIBUTES
{
LUID Luid;
int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct _PRIVILEGE_SET
{
int PrivilegeCount;
int Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // ANYSIZE_ARRAY = 1
LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_PRIVILEGES
{
internal int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
internal int[] Privileges;
}
const int SE_PRIVILEGE_ENABLED = 0x00000002;
const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
const int TOKEN_QUERY = 0X00000008;
const int TOKEN_ALL_ACCESS = 0X001f01ff;
const int PROCESS_QUERY_INFORMATION = 0X00000400;
public static bool EnablePrivilege(string lpszPrivilege, bool
bEnablePrivilege)
{
bool retval = false;
int ltkpOld = 0;
IntPtr hToken = IntPtr.Zero;
TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
tkp.Privileges = new int[3];
TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
tkpOld.Privileges = new int[3];
LUID tLUID = new LUID();
tkp.PrivilegeCount = 1;
if (bEnablePrivilege)
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
tkp.Privileges[2] = 0;
if (LookupPrivilegeValue(null, lpszPrivilege, ref tLUID))
{
Process proc = Process.GetCurrentProcess();
if (proc.Handle != IntPtr.Zero)
{
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
ref hToken) != 0)
{
tkp.PrivilegeCount = 1;
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1] = tLUID.HighPart;
tkp.Privileges[0] = tLUID.LowPart;
const int bufLength = 256;
IntPtr tu = Marshal.AllocHGlobal(bufLength);
Marshal.StructureToPtr(tkp, tu, true);
if (AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref ltkpOld) != 0)
{
// successful AdjustTokenPrivileges doesn't mean privilege could be changed
if (Marshal.GetLastWin32Error() == 0)
{
retval = true; // Token changed
}
}
TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(tu,
typeof(TOKEN_PRIVILEGES));
Marshal.FreeHGlobal(tu);
}
}
}
if (hToken != IntPtr.Zero)
{
CloseHandle(hToken);
}
return retval;
}
}
}
I tried roomaroo's WMI method to shutdown Windows 2003 Server, but it would not work until I added `[STAThread]' (i.e. "Single Threaded Apartment" threading model) to the Main() declaration:
[STAThread]
public static void Main(string[] args) {
Shutdown();
}
I then tried to shutdown from a thread, and to get that to work I had to set the "Apartment State" of the thread to STA as well:
using System.Management;
using System.Threading;
public static class Program {
[STAThread]
public static void Main(string[] args) {
Thread t = new Thread(new ThreadStart(Program.Shutdown));
t.SetApartmentState(ApartmentState.STA);
t.Start();
...
}
public static void Shutdown() {
// roomaroo's code
}
}
I'm a C# noob, so I'm not entirely sure of the significance of STA threads in terms of shutting down the system (even after reading the link I posted above). Perhaps someone else can elaborate...?
**Elaborated Answer...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Remember to add a reference to the System.Management assembly
using System.Management;
using System.Diagnostics;
namespace ShutDown
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnShutDown_Click(object sender, EventArgs e)
{
ManagementBaseObject mboShutdown = null;
ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
mcWin32.Get();
// You can't shutdown without security privileges
mcWin32.Scope.Options.EnablePrivileges = true;
ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");
// Flag 1 means we want to shut down the system
mboShutdownParams["Flags"] = "1";
mboShutdownParams["Reserved"] = "0";
foreach (ManagementObject manObj in mcWin32.GetInstances())
{
mboShutdown = manObj.InvokeMethod("Win32Shutdown", mboShutdownParams, null);
}
}
}
}
Use shutdown.exe. To avoid problem with passing args, complex execution, execution from WindowForms use PowerShell execute script:
using System.Management.Automation;
...
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddScript("shutdown -a; shutdown -r -t 100;");
// invoke execution on the pipeline (collecting output)
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
}
System.Management.Automation.dll should be installed on OS and available in GAC.
Sorry for My english.
For Windows 10, I needed to add /f option in order to shutdown the pc without any question and wait time.
//This did not work for me
Process.Start("shutdown", "/s /t 0");
//But this worked
Process.Start("shutdown", "/s /f /t 0");
There is no .net native method for shutting off the computer. You need to P/Invoke the ExitWindows or ExitWindowsEx API call.
If you want to shut down computer remotely then you can use
Using System.Diagnostics;
on any button click
{
Process.Start("Shutdown","-i");
}
I am trying to traverse the controls of a WinForms form (Windows CE) and retrieve/match their textual contents and names (that is, the Name property assigned to the WinForms Control object).
I do this from within a different process, thus WinAPI.
For traversing and for retrieving the text I wrote the following code. TextPresent uses DescendantControls for the traversing and Text to retrieve the text. I am fairly confident that the traversing code works correctly already - if not, please let me know. However, all of the controls that are returned have an empty text.
The code can be used as follows:
var foregroundWindow = new WinCEControl(GetForegroundWindow()); // working properly
Assert.That("The Window Title", foregroundWindow.Text); // working properly
Assert.That(manorMenu.TextPresent("Text to find")); // not working although /
// "Text to find" is a value within the control tree of the window.
As for the name, I do not even know how to retrieve it at all. So far my searching was unsuccessful.
public class WinCEControl
{
[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("coredll.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
private const int WM_GETTEXT = 0x000D;
private const uint GW_CHILD = 5;
private const uint GW_HWNDLAST = 1;
private const uint GW_HWNDNEXT = 2;
public WinCEControl(IntPtr handle)
{
this.Handle = handle;
}
public IntPtr Handle { get; private set; }
public string Text
{
get
{
var stringBuilder = new StringBuilder(256);
SendMessage(this.Handle, WM_GETTEXT, stringBuilder.Capacity, stringBuilder);
return stringBuilder.ToString();
}
}
public IEnumerable<WinCEControl> Children()
{
var childHandle = GetWindow(Handle, GW_CHILD);
if (childHandle == IntPtr.Zero)
{
yield break;
}
var lastHandle = GetWindow(childHandle, GW_HWNDLAST);
while (childHandle != lastHandle)
{
yield return new WinCEWindow(childHandle);
childHandle = GetWindow(childHandle, GW_HWNDNEXT);
}
yield return new WinCEWindow(lastHandle);
}
public IEnumerable<WinCEControl> DescendantControls()
{
var nodes = new Queue<WinCEControl>(new[] { this });
while (nodes.Any())
{
var node = nodes.Dequeue();
if (node != this)
{
yield return node;
}
foreach (var n in node.Children()) nodes.Enqueue(n);
}
}
public bool TextPresent(string text)
{
foreach (var descendantControl in DescendantControls())
{
if (descendantControl.Text == text)
{
return true;
}
}
return false;
}
}
My goal is to be able to perform UI tests against a running application on the CE device. If there is a solid C# library to achieve the same which works on Windows CE I am very hear.
Update: As mentioned in my comment I have failed to find a library or framework for automating foreign WinForms processes on the Win CE. I tried the "simple-wince-gui-automation" and it fails to retrieve the texts, too.
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
I'm using the WebBrowser control in .Net to execute some 3rd party affiliate marketing conversions.
I have a queue table in a database with all the scripts/images to execute. I loop through all these in a WinForms app with the WebBrowser control. After I've executed a script/image I Dispose the WebBrowser control, set it to null, and renews it with a new WebBrowser control instance.
Consider this URL: http://renderserver/RenderScript.aspx?id=1
RenderScript.aspx displays an Image with a URL of e.g.: http://3rdparty/img.ashx?id=9343
I use Fiddler to see all requests and responses, and when the same URL is executed twice, it uses some kind of cache. That cache exists underneath the WebBrowser control itself.
This cache means that the img.ashx is not called.
I tried using Internet Explorer to request the URL: http://renderserver/RenderScript.aspx?id=1 and hit F5. Then it is requested perfectly.
But if I click the address bar and hits Enter to navigate to the same URL again - it is not requested. When I use Firefox is will request the page and image everytime no matter if I use F5 or navigate from the address bar.
I found some Win32 API calls (http://support.microsoft.com/kb/326201) that was able to clear the cache. It worked on my local machine. Then the app was deployed to a server running Windows Server 2003 Standard x64 (my own machine is Vista x86).
And now the API calls to clear the cache doesn't work.
Any ideas on why the API calls doesn't work on Windows Server, but works on Vista? Both machines running IE8.
I had the same problem (quite) a while back. Microsoft has a page that was very helpful with this:
http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q326/2/01.asp&NoWebContent=1
I created a class out of Microsoft's sample, however I also had to add a couple if statements to stop processing when there are no more items; it's been a while now, but I'm pretty sure it would throw an error (see ERROR_NO_MORE_ITEMS in the code below).
I hope its helpful!
using System;
using System.Runtime.InteropServices;
// copied from: http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q326/2/01.asp&NoWebContent=1
namespace PowerCode
{
public class IECache
{
// For PInvoke: Contains information about an entry in the Internet cache
[StructLayout(LayoutKind.Explicit, Size = 80)]
public struct INTERNET_CACHE_ENTRY_INFOA
{
[FieldOffset(0)]
public uint dwStructSize;
[FieldOffset(4)]
public IntPtr lpszSourceUrlName;
[FieldOffset(8)]
public IntPtr lpszLocalFileName;
[FieldOffset(12)]
public uint CacheEntryType;
[FieldOffset(16)]
public uint dwUseCount;
[FieldOffset(20)]
public uint dwHitRate;
[FieldOffset(24)]
public uint dwSizeLow;
[FieldOffset(28)]
public uint dwSizeHigh;
[FieldOffset(32)]
public FILETIME LastModifiedTime;
[FieldOffset(40)]
public FILETIME ExpireTime;
[FieldOffset(48)]
public FILETIME LastAccessTime;
[FieldOffset(56)]
public FILETIME LastSyncTime;
[FieldOffset(64)]
public IntPtr lpHeaderInfo;
[FieldOffset(68)]
public uint dwHeaderInfoSize;
[FieldOffset(72)]
public IntPtr lpszFileExtension;
[FieldOffset(76)]
public uint dwReserved;
[FieldOffset(76)]
public uint dwExemptDelta;
}
// For PInvoke: Initiates the enumeration of the cache groups in the Internet cache
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindFirstUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr FindFirstUrlCacheGroup( int dwFlags, int dwFilter, IntPtr lpSearchCondition, int dwSearchCondition, ref long lpGroupId, IntPtr lpReserved );
// For PInvoke: Retrieves the next cache group in a cache group enumeration
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindNextUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
public static extern bool FindNextUrlCacheGroup( IntPtr hFind, ref long lpGroupId, IntPtr lpReserved );
// For PInvoke: Releases the specified GROUPID and any associated state in the cache index file
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "DeleteUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
public static extern bool DeleteUrlCacheGroup( long GroupId, int dwFlags, IntPtr lpReserved );
// For PInvoke: Begins the enumeration of the Internet cache
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindFirstUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr FindFirstUrlCacheEntry( [MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern, IntPtr lpFirstCacheEntryInfo, ref int lpdwFirstCacheEntryInfoBufferSize );
// For PInvoke: Retrieves the next entry in the Internet cache
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindNextUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
public static extern bool FindNextUrlCacheEntry( IntPtr hFind, IntPtr lpNextCacheEntryInfo, ref int lpdwNextCacheEntryInfoBufferSize );
// For PInvoke: Removes the file that is associated with the source name from the cache, if the file exists
[DllImport(#"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "DeleteUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
public static extern bool DeleteUrlCacheEntry( IntPtr lpszUrlName );
public static void ClearCache()
{
// Indicates that all of the cache groups in the user's system should be enumerated
const int CACHEGROUP_SEARCH_ALL = 0x0;
// Indicates that all the cache entries that are associated with the cache group
// should be deleted, unless the entry belongs to another cache group.
const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
// File not found.
const int ERROR_FILE_NOT_FOUND = 0x2;
// No more items have been found.
const int ERROR_NO_MORE_ITEMS = 259;
// Pointer to a GROUPID variable
long groupId = 0;
// Local variables
int cacheEntryInfoBufferSizeInitial = 0;
int cacheEntryInfoBufferSize = 0;
IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
IntPtr enumHandle = IntPtr.Zero;
bool returnValue = false;
// Delete the groups first.
// Groups may not always exist on the system.
// For more information, visit the following Microsoft Web site:
// http://msdn.microsoft.com/library/?url=/workshop/networking/wininet/overview/cache.asp
// By default, a URL does not belong to any group. Therefore, that cache may become
// empty even when the CacheGroup APIs are not used because the existing URL does not belong to any group.
enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
// If there are no items in the Cache, you are finished.
if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
return;
}
// Loop through Cache Group, and then delete entries.
while (true) {
if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()) {
break;
}
// Delete a particular Cache Group.
returnValue = DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
if (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()) {
returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
}
if (!returnValue && (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())) {
break;
}
}
// Start to delete URLs that do not belong to any group.
enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
return;
}
cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
while (true) {
internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));
if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
break;
}
cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
if (!returnValue) {
returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
}
if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
break;
}
if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize) {
cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr)cacheEntryInfoBufferSize);
returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
}
}
Marshal.FreeHGlobal(cacheEntryInfoBuffer);
}
}
}
To use it in your code, just call:
IECache.ClearCache()
before calling the navigate methods.
Fiddler uses fundamentally the same code as in the KB article to clear the WinINET cache, and I use it on Win2k3 every day.
Rather than wiping the user's entire cache, the proper fix is to set the proper HTTP response header to forbid caching. You can learn more about WinINET caching here: http://www.enhanceie.com/redir/?id=httpperf
(Alternatively, you could simply add a randomized query-string parameter; that way, each time the control encounters a request for the resource, the URL is different and the cache is thus automatically bypassed.)
Try this...
[DllImport("wininet.dll", SetLastError = true)]
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int lpdwBufferLength);
private const int INTERNET_OPTION_END_BROWSER_SESSION = 42;
private void clearCache()
{
try
{
Utilities.Web.WebBrowserHelper.WebBrowserHelper.ClearCache();
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);
}
catch (Exception exception)
{
//throw;
}
}
That kb article everyone links to has numerous errors in it (where the selected answer's source code came from), and I've wasted ~2 days trying to get it to work in all necessary settings. It is copy pasted across the internet, and there are numerous bugs reported based on OS and IE version.
Fiddler was originally written by a Microsoft employee, and is powered by FiddlerCore.dll. Telerik (the current owners/maintainers/sellers) of Fiddler still update, maintain and give away FiddlerCore for free. If you don't want to add a reference to FiddlerCore, you can disassemble the dll, and it shows the CORRECT way to call all of these horribly documented WinINet functions, but I think posting it here would be a disservice to Telerik / plagarism.
Currently, Fiddlercore is hosted here: http://www.telerik.com/fiddler/fiddlercore
The original code from
https://support.microsoft.com/en-us/kb/326201
seems buggy
checking MSDN documentation and also the VB version here:
https://support.microsoft.com/en-us/kb/262110
I modified the code like this and now for it's working for me (the issue was on execution of FindNextUrlCacheGroup and FindNextUrlCacheEntry):
using System;
using System.Runtime.InteropServices;
namespace Q326201CS
{
// Class for deleting the cache.
public class DeleteIECache
{
// For PInvoke: Contains information about an entry in the Internet cache
[StructLayout(LayoutKind.Explicit, Size=80)]
public struct INTERNET_CACHE_ENTRY_INFOA
{
[FieldOffset(0)] public uint dwStructSize;
[FieldOffset(4)] public IntPtr lpszSourceUrlName;
[FieldOffset(8)] public IntPtr lpszLocalFileName;
[FieldOffset(12)] public uint CacheEntryType;
[FieldOffset(16)] public uint dwUseCount;
[FieldOffset(20)] public uint dwHitRate;
[FieldOffset(24)] public uint dwSizeLow;
[FieldOffset(28)] public uint dwSizeHigh;
[FieldOffset(32)] public FILETIME LastModifiedTime;
[FieldOffset(40)] public FILETIME ExpireTime;
[FieldOffset(48)] public FILETIME LastAccessTime;
[FieldOffset(56)] public FILETIME LastSyncTime;
[FieldOffset(64)] public IntPtr lpHeaderInfo;
[FieldOffset(68)] public uint dwHeaderInfoSize;
[FieldOffset(72)] public IntPtr lpszFileExtension;
[FieldOffset(76)] public uint dwReserved;
[FieldOffset(76)] public uint dwExemptDelta;
}
// For PInvoke: Initiates the enumeration of the cache groups in the Internet cache
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="FindFirstUrlCacheGroup",
CallingConvention=CallingConvention.StdCall)]
public static extern IntPtr FindFirstUrlCacheGroup(
int dwFlags,
int dwFilter,
IntPtr lpSearchCondition,
int dwSearchCondition,
ref long lpGroupId,
IntPtr lpReserved);
// For PInvoke: Retrieves the next cache group in a cache group enumeration
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="FindNextUrlCacheGroup",
CallingConvention=CallingConvention.StdCall)]
public static extern bool FindNextUrlCacheGroup(
IntPtr hFind,
ref long lpGroupId,
IntPtr lpReserved);
// For PInvoke: Releases the specified GROUPID and any associated state in the cache index file
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="DeleteUrlCacheGroup",
CallingConvention=CallingConvention.StdCall)]
public static extern bool DeleteUrlCacheGroup(
long GroupId,
int dwFlags,
IntPtr lpReserved);
// For PInvoke: Begins the enumeration of the Internet cache
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="FindFirstUrlCacheEntryA",
CallingConvention=CallingConvention.StdCall)]
public static extern IntPtr FindFirstUrlCacheEntry(
[MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern,
IntPtr lpFirstCacheEntryInfo,
ref int lpdwFirstCacheEntryInfoBufferSize);
// For PInvoke: Retrieves the next entry in the Internet cache
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="FindNextUrlCacheEntryA",
CallingConvention=CallingConvention.StdCall)]
public static extern bool FindNextUrlCacheEntry(
IntPtr hFind,
IntPtr lpNextCacheEntryInfo,
ref int lpdwNextCacheEntryInfoBufferSize);
// For PInvoke: Removes the file that is associated with the source name from the cache, if the file exists
[DllImport(#"wininet",
SetLastError=true,
CharSet=CharSet.Auto,
EntryPoint="DeleteUrlCacheEntryA",
CallingConvention=CallingConvention.StdCall)]
public static extern bool DeleteUrlCacheEntry(
IntPtr lpszUrlName);
public static void doDelete()
{
// Indicates that all of the cache groups in the user's system should be enumerated
const int CACHEGROUP_SEARCH_ALL = 0x0;
// Indicates that all the cache entries that are associated with the cache group
// should be deleted, unless the entry belongs to another cache group.
const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
// File not found.
const int ERROR_FILE_NOT_FOUND = 0x2;
// No more items have been found.
const int ERROR_NO_MORE_ITEMS = 259;
// Pointer to a GROUPID variable
long groupId = 0;
// Local variables
int cacheEntryInfoBufferSizeInitial = 0;
int cacheEntryInfoBufferSize = 0;
IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
IntPtr enumHandle = IntPtr.Zero;
bool returnValue = false;
// Delete the groups first.
// Groups may not always exist on the system.
// For more information, visit the following Microsoft Web site:
// http://msdn.microsoft.com/library/?url=/workshop/networking/wininet/overview/cache.asp
// By default, a URL does not belong to any group. Therefore, that cache may become
// empty even when the CacheGroup APIs are not used because the existing URL does not belong to any group.
enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
// If there are no items in the Cache, you are finished.
if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
return;
// Loop through Cache Group, and then delete entries.
while(true)
{
if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())
{
break;
}
// Delete a particular Cache Group.
returnValue = DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
//if (returnValue || (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()))
//{
returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
//}
if (!returnValue && (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()))
break;
}
// Start to delete URLs that do not belong to any group.
enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
if (enumHandle == IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
return;
cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
while(true)
{
internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));
if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
{
break;
}
cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
//if (!returnValue)
//{
returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
//}
if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
{
break;
}
if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize)
{
cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr) cacheEntryInfoBufferSize);
returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
}
}
Marshal.FreeHGlobal(cacheEntryInfoBuffer);
}
}
}
This should do the trick:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
I have a progaram that can be ran both as a winform, or from command line. If it is invoked from a command line I call AttachConsole(-1) to attach to parent console.
However, after my program ends, the user must hit enter to get back the standard command prompt ("c:\>"). is there a way to avoid that need?
Thanks.
I could wrap it in a cmd file to avoid that issue, but I would like to do it from my exe.
Try adding this line just before your exe exits...
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
Bit of a hack, but best I could find when I encountered that problem.
Here is the safest hack that solves the Enter key problem regardless of whether the console window is in the foreground, background, or minimized. You can even run it in multiple console windows.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace WindowsAndConsoleApp
{
static class Program
{
const uint WM_CHAR = 0x0102;
const int VK_ENTER = 0x0D;
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// Do this first.
AttachConsole(ATTACH_PARENT_PROCESS);
Console.Title = "Console Window - Enter Key Test";
Console.WriteLine("Getting the handle of the currently executing console window...");
IntPtr cw = GetConsoleWindow();
Console.WriteLine($"Console handle: {cw.ToInt32()}");
Console.WriteLine("\nPut some windows in from of this one...");
Thread.Sleep(5000);
Console.WriteLine("Take your time...");
Thread.Sleep(5000);
Console.WriteLine("Sending the Enter key now...");
// Send the Enter key to the console window no matter where it is.
SendMessage(cw, WM_CHAR, (IntPtr)VK_ENTER, IntPtr.Zero);
// Do this last.
FreeConsole();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
}
Rob L's approach is somewhat dangerous as it will send an Enter to the active window. A better approach is to actual send the Enter to the correct process (console).
here is how
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
internal static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
internal static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
internal const int VK_RETURN = 0x0D;
internal const int WM_KEYDOWN = 0x100;
}
--snip--
bool attached = false;
// Get uppermost window process
IntPtr ptr = NativeMethods.GetForegroundWindow();
int u;
NativeMethods.GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
if (string.Compare(process.ProcessName, "cmd", StringComparison.InvariantCultureIgnoreCase) == 0)
{
// attach to the current active console
NativeMethods.AttachConsole(process.Id);
attached = true;
}
else
{
// create new console
NativeMethods.AllocConsole();
}
Console.Write("your output");
NativeMethods.FreeConsole();
if (attached)
{
var hWnd = process.MainWindowHandle;
NativeMethods.PostMessage(hWnd, NativeMethods.WM_KEYDOWN, NativeMethods.VK_RETURN, 0);
}
This solution is build upon the code that is found here:
http://www.jankowskimichal.pl/en/2011/12/wpf-hybrid-application-with-parameters/
It's late to the party and there have been many suggestions over the years, but as I recently just solved this issue myself by stitching together a bunch of information from various posts, I thought I'd post the solution here since it has the most relevant title.
This solution works without using the Enter key or simulating a key press. The only thing I couldn't completely solve is intercepting the Enter from the parent console when your application starts. I think this is impossible because it happens before you get a chance to intercept it; however, there is a reasonable quasi-workaround.
Before diving into the code, here's the sequence of things we need to do:
Attach to the parent console
Capture the text of the current prompt output by the parent console
Clear the parent console's prompt by overwriting it with spaces (not sure it's possible to otherwise prevent this from happening)
Interact with the console as normal
Restore parent console's previous prompt by writing what we captured in #2
This is what it would look like in use:
using System;
using System.Windows.Forms;
public static void Main(string[] args)
{
if (args.Length > 0)
{
using (new ConsoleScope())
{
Console.WriteLine("I now own the console");
Console.WriteLine("MUA HA HA HA HA HA!!!");
}
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
... and now for the code. It's more than I'd like, but this is as succinct as I could make it for a post. May this help others attempting the same thing. Enjoy!
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
public sealed class ConsoleScope : IDisposable
{
const int ATTACH_PARENT_PROCESS = -1;
const int STD_OUTPUT_HANDLE = -11;
readonly bool createdNewConsole;
readonly string prompt;
bool disposed;
public ConsoleScope()
{
if (AttachParentConsole())
{
prompt = CaptureParentConsoleCurrentPrompt();
}
else
{
AllocConsole();
createdNewConsole = true;
}
}
~ConsoleScope() => CleanUp();
public void Dispose()
{
CleanUp();
GC.SuppressFinalize(this);
}
static string CaptureParentConsoleCurrentPrompt()
{
var line = (short)Console.CursorTop;
var length = (short)Console.CursorLeft;
var noPrompt = line == 0 && length == 0;
if (noPrompt)
{
return default;
}
return ReadCurrentLineFromParentConsoleBuffer(line, length);
}
static string ReadCurrentLineFromParentConsoleBuffer(short line, short length)
{
var itemSize = Marshal.SizeOf(typeof(CHAR_INFO));
var buffer = Marshal.AllocHGlobal(length * itemSize);
var encoding = Console.OutputEncoding;
var text = new StringBuilder(capacity: length + 1);
var coordinates = default(COORD);
var textRegion = new SMALL_RECT
{
Left = 0,
Top = line,
Right = (short)(length - 1),
Bottom = line,
};
var bufferSize = new COORD
{
X = length,
Y = 1,
};
try
{
if (!ReadConsoleOutput(GetStdOutputHandle(), buffer, bufferSize, coordinates, ref textRegion))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
var array = buffer;
for (var i = 0; i < length; i++)
{
var info = Marshal.PtrToStructure<CHAR_INFO>(array);
var chars = encoding.GetChars(info.CharData);
text.Append(chars[0]);
array += itemSize;
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
// now that we've captured the current prompt, overwrite it with spaces
// so that things start where the parent left off at
Console.SetCursorPosition(0, line);
Console.Write(new string(' ', length));
Console.SetCursorPosition(0, line - 1);
return text.ToString();
}
void CleanUp()
{
if (disposed)
{
return;
}
disposed = true;
RestoreParentConsolePrompt();
if (createdNewConsole)
{
FreeConsole();
}
}
void RestoreParentConsolePrompt()
{
var text = prompt;
if (!string.IsNullOrEmpty(text))
{
// this assumes the last output from your application used
// Console.WriteLine or otherwise output a CRLF. if it didn't,
// you may need to add an extra line here
Console.Write(text);
}
}
[StructLayout(LayoutKind.Sequential)]
struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] CharData;
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
// REF: https://learn.microsoft.com/en-us/windows/console/allocconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
// REF: https://learn.microsoft.com/en-us/windows/console/attachconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
// REF: https://learn.microsoft.com/en-us/windows/console/freeconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
static bool AttachParentConsole() => AttachConsole(ATTACH_PARENT_PROCESS);
// REF: https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
static IntPtr GetStdOutputHandle() => GetStdHandle(STD_OUTPUT_HANDLE);
}
Ok, I don't have the solution, but it seems to be because the cmd.exe is not waiting on the started process, whereas with a normal console application cmd.exe waits until the the application exits. I don't know what makes cmd.exe decide to wait or not on an application, normal Windows Forms applications are just started and cmd.exe doesn't wait for it to exit. Maybe this hint triggers somebody! I will dig a bit deeper in the mean while.
Try calling the FreeConsole function prior to exiting your executable.
This one has been the easiest solution for me:
myapp.exe [params] | ECHO.
I attempted my own Qt cpp version of Chris Martinez's C# answer:
https://github.com/NightVsKnight/QtGuiConsoleApp/blob/main/QtGuiConsoleApp/main.cpp
#include <QApplication>
#include <QMessageBox>
#ifdef Q_OS_WIN
// Solution posted to https://stackoverflow.com/a/73942013/252308
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
QString consolePromptClear()
{
QString prompt = nullptr;
auto bSuccess = AttachConsole(ATTACH_PARENT_PROCESS);
if (bSuccess)
{
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (bSuccess)
{
auto dwConsoleColumnWidth = (DWORD)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd != 0 || yEnd != 0)
{
DWORD dwNumberOfChars;
SHORT yBegin = yEnd;
{
// Walk backwards to find first all blank line
auto pBuffer = (LPWSTR)LocalAlloc(LPTR, dwConsoleColumnWidth * sizeof(WCHAR));
while (yBegin)
{
COORD dwReadCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, pBuffer, dwConsoleColumnWidth, dwReadCoord, &dwNumberOfChars);
if (!bSuccess) break;
DWORD i;
for (i=0; i < dwNumberOfChars; ++i)
{
WCHAR wchar = pBuffer[i];
if (wchar != L' ')
{
--yBegin;
break;
}
}
if (i == dwNumberOfChars)
{
// Found all blank line; we want the *next* [non-blank] line
yBegin++;
break;
}
}
LocalFree(pBuffer);
}
auto promptLength = (yEnd - yBegin) * dwConsoleColumnWidth + xEnd;
auto lpPromptBuffer = (LPWSTR)LocalAlloc(LPTR, promptLength * sizeof(WCHAR));
COORD dwPromptCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, lpPromptBuffer, promptLength, dwPromptCoord, &dwNumberOfChars);
if (bSuccess)
{
Q_ASSERT(promptLength == dwNumberOfChars);
prompt = QString::fromWCharArray(lpPromptBuffer, dwNumberOfChars);
bSuccess = SetConsoleCursorPosition(hStdOut, dwPromptCoord);
if (bSuccess)
{
FillConsoleOutputCharacterW(hStdOut, L' ', promptLength, dwPromptCoord, &dwNumberOfChars);
}
}
LocalFree(lpPromptBuffer);
}
}
}
}
if (prompt.isEmpty())
{
FreeConsole();
return nullptr;
}
else
{
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
return prompt;
}
}
void consolePromptRestore(const QString& prompt)
{
if (prompt.isEmpty()) return;
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut == INVALID_HANDLE_VALUE) return;
CONSOLE_SCREEN_BUFFER_INFO csbi;
BOOL bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (!bSuccess) return;
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd == 0 && yEnd == 0) return;
auto buffer = prompt.toStdWString();
auto lpBuffer = buffer.data();
auto nLength = (DWORD)buffer.length();
COORD dwWriteCoord = { 0, (SHORT)(yEnd + 1) };
DWORD dwNumberOfCharsWritten;
WriteConsoleOutputCharacterW(hStdOut, lpBuffer, nLength, dwWriteCoord, &dwNumberOfCharsWritten);
dwWriteCoord = { (SHORT)dwNumberOfCharsWritten, (SHORT)(yEnd + 1) };
SetConsoleCursorPosition(hStdOut, dwWriteCoord);
}
#else
// Non-Windows impl...
#endif
int main(int argc, char *argv[])
{
// NOTE: Any console output before call to consolePromptClear() may get cleared.
// NOTE: Console vs GUI mode has **NOTHING** to do with being passed arguments; You can easily pass arguments to GUI apps.
int returnCode;
auto prompt = consolePromptClear();
if (prompt.isEmpty())
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
QMessageBox msgBox(nullptr);
msgBox.setWindowTitle(a.applicationName());
msgBox.setTextFormat(Qt::RichText);
msgBox.setText("App is detected to be running as a GUI");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.show();
returnCode = a.exec();
}
else
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
qout << "App is detected to be running as a Console" << Qt::endl;
returnCode = 0;
consolePromptRestore(prompt);
}
return returnCode;
}