I have created a program that can also be ran as a service and it will allow me to debug it as well using the following in the Program.cs startup file.
using System;
using System.Linq;
using System.Windows.Forms;
using System.ServiceProcess;
using System.Reflection;
using System.Threading;
using crs.Includes;
using crs.Service;
using System.IO;
namespace crs
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//Convert all arguments to lower
args = Array.ConvertAll(args, e => e.ToLower());
//Create the container object for the settings to be stored
Settings.Bag = new SettingsBag();
//Check if we want to run this as a service
bool runAsService = args.Contains("-service");
//Check if debugging
bool debug = Environment.UserInteractive;
//Catch all unhandled exceptions as well
if (!debug || debug)
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
if (runAsService)
{
//Create service array
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new CRSService()
};
//Run services in interactive mode if needed
if (debug)
RunInteractive(ServicesToRun);
else
ServiceBase.Run(ServicesToRun);
}
else
{
//Start the main gui
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainGUI());
}
}
#region Functions
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
string stackTrace = ex.Message + "/n";
while (ex.InnerException != null)
{
ex = ex.InnerException;
stackTrace += ex.Message + "/n";
}
stackTrace = stackTrace.Substring(0, stackTrace.Length - 2);
string msg = "UNHANDLED EXCEPTION!/n/n" + stackTrace;
//Write all log messages to a debug log
try
{
string currentDate = DateTime.Now.ToString("yyyy-MM-dd");
string debugFilePath = AppDomain.CurrentDomain.BaseDirectory + #"debugLogs\";
string debugFilename = Application.ProductName + "-debug-" + currentDate + ".log";
if (!Directory.Exists(debugFilePath))
{
//Create the debug log files directory
Directory.CreateDirectory(debugFilePath);
}
if (!File.Exists(debugFilePath + debugFilename))
{
//Create the new file
using (StreamWriter w = File.CreateText(debugFilePath + debugFilename))
{
w.WriteLine("Debug log file for " + Application.ProductName + ".");
w.WriteLine("Created on " + currentDate + ".");
w.WriteLine("");
}
}
//Write the log message to the file
using (StreamWriter w = File.AppendText(debugFilePath + debugFilename))
{
w.WriteLine(DateTime.Now.ToString() + " :: " + msg);
}
}
catch
{ }
}
private static void RunInteractive(ServiceBase[] servicesToRun)
{
Console.WriteLine("Services running in interactive mode.");
Console.WriteLine();
MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Starting {0}...", service.ServiceName);
onStartMethod.Invoke(service, new object[] { new string[] { } });
Console.Write("Started");
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Press any key to stop the services and end the process...");
Console.ReadKey();
Console.WriteLine();
MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Stopping {0}...", service.ServiceName);
onStopMethod.Invoke(service, null);
Console.WriteLine("Stopped");
}
//Keep the console alive for a second to allow the user to see the message.
Console.WriteLine("All services stopped.");
Thread.Sleep(1000);
}
#endregion
}
}
Everything works as expected except for the line Console.ReadKey(); under the RunInteractive() method. If I was to try and run this service manually in a console window I would have no issues what so ever, it runs great and waits for me to hit enter to start the service stopping process. However, when running it in the IDE it's spitting everything out to the DEBUG window and there is nothing for it to grab a ReadKey on.
How can I go about getting around this when debugging in the IDE? Is it possible to somehow force it to run in a command window when debugging in the IDE?
After some digging around I came up with a new class to suit my needs. Thanks for the post by Pavlo I was able to actually get text to read and write to the new console window I needed to create when one was not present.
My altered RunInteractive function from my original question:
private static void RunInteractive(ServiceBase[] servicesToRun)
{
//Account for running this application without a console window (debugging in IDE)
if (!ConsoleWindow.Exists() && !ConsoleWindow.Create())
return;
Console.WriteLine("Services running in interactive mode.");
Console.WriteLine();
MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Starting {0}...", service.ServiceName);
onStartMethod.Invoke(service, new object[] { new string[] { } });
Console.Write("Started");
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Press any key to stop the services and end the process...");
Console.ReadKey();
Console.WriteLine();
MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Stopping {0}...", service.ServiceName);
onStopMethod.Invoke(service, null);
Console.WriteLine("Stopped");
}
//Keep the console alive for a second to allow the user to see the message.
Console.WriteLine("All services stopped.");
Thread.Sleep(1000);
}
Note: The only thing that was added here was this little bit at the top of the function.
//Account for running this application without a console window (debugging in IDE)
if (!ConsoleWindow.Exists() && !ConsoleWindow.Create())
return;
My new ConsoleWindow class:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace crs.Includes
{
public class ConsoleWindow
{
#region Constants
private const UInt32 GENERIC_WRITE = 0x40000000;
private const UInt32 GENERIC_READ = 0x80000000;
private const UInt32 FILE_SHARE_READ = 0x00000001;
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
private const UInt32 OPEN_EXISTING = 0x00000003;
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
#endregion
#region WinAPI external functions
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport(
"kernel32.dll",
SetLastError = true
)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeConsole();
[DllImport(
"kernel32.dll",
SetLastError = true
)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AllocConsole();
[DllImport(
"kernel32.dll",
EntryPoint = "CreateFileW",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern IntPtr CreateFileW(
string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFil
);
#endregion
#region Public class methods
public static bool Exists()
{
if (GetConsoleWindow() == IntPtr.Zero)
return false;
else
return true;
}
public static bool Create()
{
try
{
if (!AllocConsole())
throw new Exception("Error! Could not get a lock on a console window and could not create one.");
InitializeOutStream();
InitializeInStream();
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
#endregion
#region Functions
private static void InitializeOutStream()
{
FileStream fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
if (fs != null)
{
StreamWriter writer = new StreamWriter(fs) { AutoFlush = true };
Console.SetOut(writer);
Console.SetError(writer);
}
}
private static void InitializeInStream()
{
FileStream fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
if (fs != null)
Console.SetIn(new StreamReader(fs));
}
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, FileAccess dotNetFileAccess)
{
SafeFileHandle file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
if (!file.IsInvalid)
{
FileStream fs = new FileStream(file, dotNetFileAccess);
return fs;
}
return null;
}
#endregion
}
}
Instead of console.write, you can create a log file in which you can log the status of the program. I recommend using the log4net nuget package.
Related
**EDIT: When I put a breakpoint in my MinimizePlayer() method, and step through it, it minimizes the wmplayer process. But it does not without stepping through. I don't know why. **
I had a similar script on my Linux machine where I automated some tasks that I use every time I boot up. I was able to start Rhythmbox, begin playing my playlist, and minimize the process.
Well, I'm trying the same thing on my new Windows 10 machine, and I don't know how to minimize the wmplayer.exe process from my script.
This is what I have so far. It works fine, I just want wmplayer to be minimized:
using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
namespace Playlist
{
class Program
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
private enum WindowShowStyle:uint
{
Hide = 0,
ShowMinimized = 2,
Minimize = 6
}
static void Main(string[] args)
{
Run();
}
public static void Run()
{
String username = Environment.UserName;
username = char.ToUpper(username[0]) + username.Substring(1);
Console.WriteLine("Hello " + username);
Thread.Sleep(2000);
Console.WriteLine("Opening Playlist...");
Thread.Sleep(2000);
Process.Start("wmplayer.exe", "C:\\Users\\" + username + "\\Music\\A_ChillstepMix.mp3");
//Thread.Sleep(2000);
//Console.WriteLine("Opening your IDE...");
//Thread.Sleep(2000);
//Process.Start("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\IDE\\devenv.exe");
//Thread.Sleep(2000);
MinimizePlayer();
Thread.Sleep(2000);
Console.WriteLine("Goodbye...");
Thread.Sleep(2000);
System.Environment.Exit(0);
}
public static void MinimizePlayer()
{
Process[] ps = Process.GetProcesses();
foreach(Process p in ps)
{
if(p.ProcessName.Contains("wmplayer"))
{
IntPtr h = p.MainWindowHandle;
ShowWindow(h, WindowShowStyle.Minimize);
}
}
}
}
}
You can specify the WindowStyle of the process you are starting if you use the ProcessStartInfo object:
var psi = new System.Diagnostics.ProcessStartInfo();
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized;
psi.FileName = "wmplayer.exe";
System.Diagnostics.Process.Start(psi);
Ok, I got it. I needed to add Thread.Sleep() before the MinimizePlayer() method. Working code is as follows (I commented out the Visual Studio stuff while debugging to speed things up):
using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
namespace Playlist
{
class Program
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
private enum WindowShowStyle:uint
{
Hide = 0,
ShowMinimized = 2,
Minimize = 6
}
static void Main(string[] args)
{
Run();
}
public static void Run()
{
String username = Environment.UserName;
username = char.ToUpper(username[0]) + username.Substring(1);
Console.WriteLine("Hello " + username);
Thread.Sleep(2000);
Console.WriteLine("Opening Playlist...");
Thread.Sleep(2000);
Process.Start("wmplayer.exe", "C:\\Users\\" + username + "\\Music\\A_ChillstepMix.mp3");
//Thread.Sleep(2000);
//Console.WriteLine("Opening your IDE...");
//Thread.Sleep(2000);
//Process.Start("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\IDE\\devenv.exe");
Thread.Sleep(2000);
Console.WriteLine("Minimizing Player...");
Thread.Sleep(2000);
MinimizePlayer();
Thread.Sleep(2000);
Console.WriteLine("Goodbye...");
Thread.Sleep(5000);
System.Environment.Exit(0);
}
public static void MinimizePlayer()
{
Process[] ps = Process.GetProcesses();
foreach(Process p in ps)
{
if(p.ProcessName.Contains("wmplayer"))
{
IntPtr h = p.MainWindowHandle;
ShowWindow(h, WindowShowStyle.Minimize);
}
}
}
}
}
There is a web site: http://site.domain.com which prompts for credentials with "windows security" dialog box. So I managed to use WebBrowser control to navigate to the page and send keystrokes to enter the password - I could not find another way around.
Now I came to the point where the website generates a link to a file I want to download, it looks like: http://site.domain.com/operations/reporting/csv/Report720_2553217.csv
I tried to use WebClient to download the file but it does nothing (br is my WebBrowser control):
WebClient wb = new WebClient();
wb.Headers.Add( br.Document.Cookie);
wb.DownloadFile(link, #"report.csv");
I have been trying to find a working solution to no avail. I know the web client is not authenticated so tried to use web browser's cookie but it does not work. The cookie looks as follows:
TLTUID=61FE48D8F9B910F9E930F42D6A03EAA6; TLTSID=0B2B8EE82688102641B7E768807FA8B2; s_cc=true; s_sq=%5B%5BB%5D%5D; ASPSESSIONIDQQSTRDQS=FNPJCODCEMGFIDHFLKDBEMHO
So I have two questions:
How to allow web client to download a file accessible from web browser's session. What am I doing wrong in the above code sample?
Is there an easy way to use WebBrowser solely to download and save that file to the path and filename of my choice? Or how to do it all using WebClient or something else?
Not possible, AFAIK. WebClient and WebBrowser use different layers to access web. WebClient uses WinHTTP, WebBrowser uses UrlMon. Thus, they would have separate sessions (including the authentication cache).
That's possible, just use any of UrlMon APIs to download the file, e.g. URLDownloadToFile or URLDownloadToCacheFile. I've just answered a similar question.
On a side note, you don't have to feed in keystrokes to provide authentication credentials. You can implement IAuthenticateEx on WebBrowser site object for this. Here is more info.
So this is the working solution which meets the requirements given in my question. It automatically navigates to the specified website, authenticates and downloads a generated report. I used this Q/A as an outline.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Web;
using System.Security.Authentication;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Win32;
using System.Runtime.CompilerServices;
using System.Security.Policy;
namespace margot_report
{
[ComImport,
Guid("00000112-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleObject
{
void SetClientSite(IOleClientSite pClientSite);
}
[ComImport,
Guid("00000118-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleClientSite
{
void SaveObject();
void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk);
void GetContainer(object ppContainer);
void ShowObject();
void OnShowWindow(bool fShow);
void RequestNewObjectLayout();
}
[ComImport,
GuidAttribute("6d5140c1-7436-11ce-8034-00aa006009fa"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
ComVisible(false)]
public interface IServiceProvider
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryService(ref Guid guidService, ref Guid riid, out IntPtr
ppvObject);
}
[ComImport]
[Guid("79EAC9D0-BAF9-11CE-8C82-00AA004BA90B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAuthenticate
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Authenticate(IntPtr phwnd,
[MarshalAs(UnmanagedType.LPWStr)] ref string pszUsername,
[MarshalAs(UnmanagedType.LPWStr)] ref string pszPassword);
}
class SelfAuthenticatingWebBrowser : WebBrowser, IOleClientSite, IAuthenticate, IServiceProvider
{
public static Guid IID_IAuthenticate = new Guid("79eac9d0-baf9-11ce-8c82-00aa004ba90b");
public static Guid SID_IAuthenticate = new Guid("79eac9d0-baf9-11ce-8c82-00aa004ba90b");
public const int INET_E_DEFAULT_ACTION = unchecked((int)0x800C0011);
public const int S_OK = unchecked((int)0x00000000);
public void Authenticate(IntPtr phwnd, ref string pszUsername, ref string pszPassword)
{
Console.WriteLine("Authenticate");
pszUsername = Program.username; //
pszPassword = Program.password; //
//return S_OK;
}
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
//Console.WriteLine("QueryService");
int nRet = guidService.CompareTo(IID_IAuthenticate); // Zero returned if the compared objects are equal
if (nRet == 0)
{
nRet = riid.CompareTo(IID_IAuthenticate); // Zero returned if the compared objects are equal
if (nRet == 0)
{
ppvObject = Marshal.GetComInterfaceForObject(this,
typeof(IAuthenticate));
return S_OK;
}
}
ppvObject = new IntPtr();
return INET_E_DEFAULT_ACTION;
}
public void SaveObject()
{
throw new NotImplementedException();
}
public void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk)
{
throw new NotImplementedException();
}
public void GetContainer(object ppContainer)
{
throw new NotImplementedException();
}
public void ShowObject()
{
throw new NotImplementedException();
}
public void OnShowWindow(bool fShow)
{
throw new NotImplementedException();
}
public void RequestNewObjectLayout()
{
throw new NotImplementedException();
}
public IComponent Component
{
get { throw new NotImplementedException(); }
}
public IContainer Container
{
get { throw new NotImplementedException(); }
}
public bool DesignMode
{
get { throw new NotImplementedException(); }
}
public string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
class Program
{
static bool send = true, verbose=false, clicked=false, submitted=false;
static int c = 0, t=0;
public static string filename, report, username, password;
/// <summary>
/// The URLMON library contains this function, URLDownloadToFile, which is a way
/// to download files without user prompts. The ExecWB( _SAVEAS ) function always
/// prompts the user, even if _DONTPROMPTUSER parameter is specified, for "internet
/// security reasons". This function gets around those reasons.
/// </summary>
/// <param name="pCaller">Pointer to caller object (AX).</param>
/// <param name="szURL">String of the URL.</param>
/// <param name="szFileName">String of the destination filename/path.</param>
/// <param name="dwReserved">[reserved].</param>
/// <param name="lpfnCB">A callback function to monitor progress or abort.</param>
/// <returns>0 for okay.</returns>
[DllImport("URLMON.DLL", EntryPoint = "URLDownloadToFileW", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern int URLDownloadToFile(int pCaller, string srcURL,
string dstFile, int Reserved, int CallBack);
static void Main(string[] args)
{
// HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("http://site.domain.com/operations/reporting/report-run.asp?reportid=720");
////webRequest.Proxy = webProxy;
string appname = Environment.GetCommandLineArgs()[0];
if (args.Count() < 1)
{
Console.WriteLine("Type: \"{0}\" -h for help on usage.\n", appname);
return;
}
if (args.Count() > 0)
foreach (var s in args)
if (s.Contains("-h") || s.Contains("/h") || s.Contains("-?") || s.Contains("/?"))
{
Console.WriteLine("\nUsage: {0} [-rfupv] <values>\n", appname);
Console.WriteLine("\n -r report_link ");
Console.WriteLine(" -f output_file ");
Console.WriteLine(" -u username ");
Console.WriteLine(" -p password ");
Console.WriteLine(" -t time_delay - seconds to wait before sending key strokes");
Console.WriteLine(" -v verbose output to console (for debugging)");
Console.WriteLine(" -h|? Display this info and exit.");
return;
}
try
{
if (args.Any(x => x.Contains("-f")))
{
int i = 0;
while (!args[i].Contains("-f")) i++;
filename =args[i + 1];
}
else filename = "report.csv";
if (args.Any(x => x.Contains("-r")))
{
int i = 0;
while (!args[i].Contains("-r")) i++;
report = args[i + 1];
}
else report = "http://site.domain.com/operations/reporting/report-run.asp?reportid=720";
if (args.Any(x => x.Contains("-u")))
{
int i = 0;
while (!args[i].Contains("-u")) i++;
username = args[i + 1];
}
else username = "";
if (args.Any(x => x.Contains("-p")))
{
int i = 0;
while (!args[i].Contains("-p")) i++;
password = args[i + 1];
}
else password = "";
if (args.Any(x => x.Contains("-t")))
{
int i = 0;
while (!args[i].Contains("-t")) i++;
t = int.Parse(args[i + 1]);
}
else t = 5;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null) Console.WriteLine(ex.InnerException.Message);
return;
}
///////////////////////////////
//////////////////////////////////////////////////////
Console.WriteLine("start");
var th = new Thread(() => {
//WebBrowser
var wb = new SelfAuthenticatingWebBrowser(); // WebBrowser();
wb.Show(); Console.WriteLine(wb.DocumentText);
Console.WriteLine(wb.DocumentText);
wb.DocumentCompleted += browser_DocumentCompleted;
wb.NewWindow += browser_newWindow;
wb.ControlAdded += browser_ControlAdded ;
wb.LostFocus += browser_LostFocus;
wb.Navigating += browser_Navigating;
wb.FileDownload += browser_FileDownload;
//wb.Site = new MySitex();
//Console.WriteLine(wb.Site.GetService(typeof(IAuthenticateEx)));//wb.Site.Name
Console.WriteLine(wb.AllowNavigation);
wb.Navigate("about:blank");
object obj = wb.ActiveXInstance;
IOleObject oc = obj as IOleObject;
oc.SetClientSite(wb as IOleClientSite);
//this.Site = this as ISite;
System.IntPtr ppvServiceProvider;
IServiceProvider sp = wb as IServiceProvider;
sp.QueryService(ref SelfAuthenticatingWebBrowser.SID_IAuthenticate, ref SelfAuthenticatingWebBrowser.IID_IAuthenticate, out ppvServiceProvider);
wb.Navigate(report);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
Console.WriteLine("end");
}
static void browser_Navigating(object sender, EventArgs e)
{
Console.WriteLine("navigating..." );
var s = sender as WebBrowser;
Console.WriteLine(s.DocumentTitle + s.Name);
//foreach (var x in s.Controls)
// Console.WriteLine(x.ToString());
//foreach(Form x in Application.OpenForms)
// Console.WriteLine(x.Name);
if (false)//(send)
{
send = false;
var thekeys = new Thread(() =>
{
Thread.Sleep(t*1000);
if (username != "")
{
Console.WriteLine("sending username key strokes");
SendKeys.SendWait(username);
SendKeys.SendWait("{TAB}");
Console.WriteLine("sent");
}
if (password != "")
{
Console.WriteLine("sending password key strokes");
SendKeys.SendWait(password);
SendKeys.SendWait("{ENTER}");
Console.WriteLine("sent");
}
});
thekeys.Start();
}
}
static void browser_FileDownload(object sender, EventArgs e)
{
if(verbose) Console.WriteLine("FileDownload : " + e.ToString());
var s = sender as WebBrowser;
if (verbose) Console.WriteLine(s.DocumentTitle + s.Name + s.Url); //Console.ReadKey();
}
static void browser_LostFocus(object sender, EventArgs e)
{
Console.WriteLine("lost focus : " + e.ToString());
var s = sender as WebBrowser;
Console.WriteLine(s.DocumentTitle + s.Name);
}
static void browser_ControlAdded(object sender, ControlEventArgs e)
{
Console.WriteLine("control added : " + e.ToString());
var s = sender as WebBrowser;
Console.WriteLine(s.DocumentTitle + s.Name);
}
static void browser_newWindow(object sender, CancelEventArgs e)
{
Console.WriteLine("new : " + e.ToString());
var s = sender as WebBrowser;
Console.WriteLine(s.DocumentTitle + s.Name);
}
static void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
if (e.Url.ToString()=="about:blank") return;
if (br.Url == e.Url)
{
Console.WriteLine("Navigated to {0}", e.Url);
if (e.Url.ToString() == "http://site.domain.com/operations/reporting/report-generate.asp") submitted = true;
if(verbose) Console.WriteLine(br.DocumentText);
//Console.WriteLine(br.Document.Cookie);
if(!clicked)
foreach (HtmlElement x in br.Document.All)
{
// if (x.All == null)
//Console.WriteLine("|{0} {1} {2}| [{3}]\n", x.Id, x.Name, ".",x.GetAttribute("value"));
if (x.Name == "format" && x.GetAttribute("value") == "C") { Console.WriteLine("\n C L I C K !\n"); x.InvokeMember("click"); Thread.Sleep(1000); clicked = true; }
//else foreach (HtmlElement y in x.Document.All)
// Console.WriteLine("|{0} {1} {2}| [{3}]\n", y.Id, y.Name, y.OuterHtml, x.InnerText);
}
if (clicked && !submitted)
{
Console.WriteLine("submitting the report");
if (verbose) Console.WriteLine(" function at: {0}", br.DocumentText.IndexOf("function runReport()"));
//var newtext = br.DocumentText.Replace("value=\"R\" checked", "value=\"R\" ").Replace("value=\"C\"", "value=\"C\" checked");
//Console.WriteLine(" function at: {0}", br.DocumentText.IndexOf("function runReport()"));
br.Document.InvokeScript("runReport");
Console.WriteLine("done");
//Console.WriteLine(br.DocumentText);
}
//if (c == 1)
{
//Console.WriteLine(br.DocumentText);
if (verbose) Console.WriteLine(br.DocumentText.IndexOf("http://"));
if (verbose) Console.WriteLine(br.DocumentText.IndexOf(".csv"));
}
if (verbose) Console.WriteLine("c = {0}", c);
//http://site.domain.com/operations/reporting/csv/Report714_4045373.csv
if (submitted)
{
int start = br.DocumentText.IndexOf("csv/Report");
int end = 0; if (start >= 0) end = br.DocumentText.IndexOf(".csv", start);
if (start > 0 && end > start)
{
if (verbose) Console.WriteLine(br.DocumentText.Substring(start, end - start));
string link = "http://site.domain.com/operations/reporting/" + br.DocumentText.Substring(start, end - start) + ".csv";
Console.WriteLine(link);
//Console.WriteLine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
//Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
if (URLDownloadToFile(0, link, filename, 0, 0) == 0)
Console.WriteLine("The report has been saved as {0}", filename);
else
Console.WriteLine("There was a problem with downloading/saving the report {0}", report);
Application.ExitThread();
}
}
if (verbose) Console.WriteLine("cookies ? {0}, {1}, {2}", br.Document.Cookie == null, br.Document.Cookie == "", br.Document.Cookie);
c++;
if(c>4) Application.ExitThread(); // Stops the thread
}
}
}
}
I have made an application to log all websites visited by current PC user. This is not for a malicious use. I am making this feature for my employee monitoring software which will be licensed under proper laws.
Coming on main point, Whenever I am fetching URL from any browser such as IE. I am only getting its URL for all opened tabs. I am unable to get any tab handle for IE7+ , because of which I am unable to maintain a list of tabs for which I have already logged URL's for same tab.
Below is my code (Take a Look on Commented Code First):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace WebsiteLoggerConsole
{
public class WebLogger
{
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
System.Threading.Timer log;
public void StartLoggin()
{
try
{
TimerCallback logcallback = new TimerCallback(LogTick);
log = new System.Threading.Timer(logcallback, null, 0, 2000);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
}
}
public void StopLogging()
{
try
{
log.Dispose();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
}
}
public void LogTick(Object stateInfo)
{
CreateLog();
}
void CreateLog()
{
try
{
SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindows();
string filename;
foreach (SHDocVw.InternetExplorer ie in shellWindows)
{
filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
if (filename.Equals("iexplore"))
{
int val = ie.HWND;
IntPtr hwnd = new IntPtr(val);
IntPtr uihwnd = GetDirectUIHWND(hwnd);
string ddd = (ie.LocationURL) + " :::: " + (uihwnd.ToString());
Console.WriteLine(ddd);
}
}
//SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindows();
//string filename;
//foreach (SHDocVw.InternetExplorer ie in shellWindows)
//{
// filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
// if (filename.Equals("iexplore"))
// {
// int val = ie.HWND;
// IntPtr hwnd = new IntPtr(val);
// IntPtr uihwnd = GetDirectUIHWND(hwnd);
// IntPtr tabhwnd = GetDirectUIHWND(uihwnd);
// string ddd = (ie.LocationURL) + " :::: " + (tabhwnd.ToString());
// Console.WriteLine(ddd);
// }
//}
//Process[] processlist = Process.GetProcesses();
//foreach (Process theprocess in processlist)
//{
// if (theprocess.ProcessName == "iexplore")
// {
// Console.WriteLine("Process: {0}, ID: {1}, Handle: {3}, Window name: {2}",
// theprocess.ProcessName, theprocess.Id, theprocess.MainWindowTitle, theprocess.SessionId.ToString()
// );
// }
//}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
}
}
private static IntPtr GetDirectUIHWND(IntPtr ieFrame)
{
// try IE 9 first:
IntPtr intptr = FindWindowEx(ieFrame, IntPtr.Zero, "WorkerW", null);
if (intptr == IntPtr.Zero)
{
// IE8 and IE7
intptr = FindWindowEx(ieFrame, IntPtr.Zero, "CommandBarClass", null);
}
intptr = FindWindowEx(intptr, IntPtr.Zero, "ReBarWindow32", null);
//intptr = FindWindowEx(intptr, IntPtr.Zero, "TabBandClass", null);
//intptr = FindWindowEx(intptr, IntPtr.Zero, "DirectUIHWND", null);
return intptr;
}
}
}
I provided a solution on this topic: How to write a standalone URL logger for Windows?
There I used Java, but you can do the same by using C#. I am almost sure there is a lot of good libpcap wrappers for C#.
There is a project called pcapDotNet and you can use it.
Adjusting one example:
using System;
using System.Collections.Generic;
using PcapDotNet.Core;
using PcapDotNet.Packets;
using PcapDotNet.Packets.IpV4;
using PcapDotNet.Packets.Transport;
namespace InterpretingThePackets
{
class Program
{
static void Main(string[] args)
{
// Retrieve the device list from the local machine
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
if (allDevices.Count == 0)
{
Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
return;
}
// Print the list
for (int i = 0; i != allDevices.Count; ++i)
{
LivePacketDevice device = allDevices[i];
Console.Write((i + 1) + ". " + device.Name);
if (device.Description != null)
Console.WriteLine(" (" + device.Description + ")");
else
Console.WriteLine(" (No description available)");
}
int deviceIndex = 0;
do
{
Console.WriteLine("Enter the interface number (1-" + allDevices.Count + "):");
string deviceIndexString = Console.ReadLine();
if (!int.TryParse(deviceIndexString, out deviceIndex) ||
deviceIndex < 1 || deviceIndex > allDevices.Count)
{
deviceIndex = 0;
}
} while (deviceIndex == 0);
// Take the selected adapter
PacketDevice selectedDevice = allDevices[deviceIndex - 1];
// Open the device
using (PacketCommunicator communicator =
selectedDevice.Open(65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
1000)) // read timeout
{
// Check the link layer. We support only Ethernet for simplicity.
if (communicator.DataLink.Kind != DataLinkKind.Ethernet)
{
Console.WriteLine("This program works only on Ethernet networks.");
return;
}
// Compile the filter
using (BerkeleyPacketFilter filter = communicator.CreateFilter("ip and tcp"))
{
// Set the filter
communicator.SetFilter(filter);
}
Console.WriteLine("Listening on " + selectedDevice.Description + "...");
// start the capture
communicator.ReceivePackets(0, PacketHandler);
}
}
// Callback function invoked by libpcap for every incoming packet
private static void PacketHandler(Packet packet)
{
// print timestamp and length of the packet
Console.WriteLine(packet.Timestamp.ToString("yyyy-MM-dd hh:mm:ss.fff") + " length:" + packet.Length);
IpV4Datagram ip = packet.Ethernet.IpV4;
//Do your magic here using HttpRequestDatagram
}
}
}
Why Register a DLL?
I have a lot of basic doubts on DLLs and i have tried to put them in a listed Questions form as below:
Why do we need to register a DLL?
What happens when we register a DLL?
When i use a "LoadLibrary" in my C# code, i do not do any registration.
Whats the connection/difference between the two?
(Registering a DLL and Loading a DLL)
Can i register all the DLLs? or are there some DLLs which cannot be registered and why?
If anyone can recommend some online articles for clarifying my doubts it would be big help!
otherwise snippet code.
im using this code but it doesnt give exactly what i need, thats why i asked here....
its working fine in 32-bit machine but it gives denied path error in 64 bit machine
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security;
using System.Management;
using System.IO;
using CCC_DLLRegistar.LoadLibrary;
using System.Security.Principal;
using System.Diagnostics;
namespace CCC_DLLRegistar
{
public partial class Form1 : Form
{
#region Is64BitOperatingSystem (IsWow64Process)
public static bool Is64BitOperatingSystem()
{
if (IntPtr.Size == 8) // 64-bit programs run only on Win64
{
return true;
}
else // 32-bit programs run on both 32-bit and 64-bit Windows
{
// Detect whether the current process is a 32-bit process
// running on a 64-bit system.
bool flag;
return ((DoesWin32MethodExist("kernel32.dll", "IsWow64Process") &&
IsWow64Process(GetCurrentProcess(), out flag)) && flag);
}
}
static bool DoesWin32MethodExist(string moduleName, string methodName)
{
IntPtr moduleHandle = GetModuleHandle(moduleName);
if (moduleHandle == IntPtr.Zero)
{
return false;
}
return (GetProcAddress(moduleHandle, methodName) != IntPtr.Zero);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule,
[MarshalAs(UnmanagedType.LPStr)]string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
#endregion
#region Is64BitOperatingSystem (WMI)
public static bool Is64BitOperatingSystem(string machineName,
string domain, string userName, string password)
{
ConnectionOptions options = null;
if (!string.IsNullOrEmpty(userName))
{
options = new ConnectionOptions();
options.Username = userName;
options.Password = password;
options.Authority = "NTLMDOMAIN:" + domain;
}
// Else the connection will use the currently logged-on user
// Make a connection to the target computer.
ManagementScope scope = new ManagementScope("\\\\" +
(string.IsNullOrEmpty(machineName) ? "." : machineName) +
"\\root\\cimv2", options);
scope.Connect();
ObjectQuery query = new ObjectQuery(
"SELECT AddressWidth FROM Win32_Processor");
// Perform the query and get the result.
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject queryObj in queryCollection)
{
if (queryObj["AddressWidth"].ToString() == "64")
{
return true;
}
}
return false;
}
#endregion
public Form1()
{
InitializeComponent();
}
private string _RootPath = string.Empty;
private string _path = string.Empty;
private List<string> _Regfiles;
DLLHelper obj;
public string path
{
get { return _path; }
set { _path = value; }
}
public string RootPath
{
get { return _RootPath; }
set { _RootPath = value; }
}
private void Form1_Load(object sender, EventArgs e)
{
// Solution 1. Is64BitOperatingSystem (IsWow64Process)
// Determine whether the current operating system is a 64 bit
// operating system.
bool f64bitOS = Is64BitOperatingSystem();
//Console.WriteLine("The current operating system {0} 64-bit.",
// f64bitOS ? "is" : "is not");
// Solution 2. Is64BitOperatingSystem (WMI)
// Determine whether the current operating system is a 64 bit
// operating system through WMI. The function is also able to
// query the bitness of OS on a remote machine.
try
{
f64bitOS = Is64BitOperatingSystem(".", null, null, null);
//Console.WriteLine("The current operating system {0} 64-bit.",
// f64bitOS ? "is" : "is not");
if (f64bitOS == true)
{
RootPath = "C:\\windows\\SysWow64\\";
}
else
{
RootPath = "C:\\Windows\\System32\\";
}
bool isAdmin = new WindowsPrincipal(WindowsIdentity.GetCurrent())
.IsInRole(WindowsBuiltInRole.Administrator) ? true : false;
if (isAdmin)
{
MessageBox.Show("you are an administrator");
}
else
{
MessageBox.Show("You are not an administrator");
}
path = Application.StartupPath + "\\CCC DLL\\";
List<string> regsvr = new List<string>();
foreach (string s in Directory.GetFiles(path, "*.*").Select(Path.GetFileName))
{
regsvr.Add(Application.StartupPath + "\\CCC DLL\\" + s);
}
foreach (string filepath in regsvr)
{
Registar_Dlls(filepath);
}
_Regfiles=new List<string>();
foreach (string s in Directory.GetFiles(path, "*.dll").Select(Path.GetFileName))
{
_Regfiles.Add(Application.StartupPath + "\\CCC DLL\\" + s);
if (DLLHelper.UnmanagedDllIs64Bit(Application.StartupPath + "\\CCC DLL\\" + s) == true)
{
MessageBox.Show("62 bit");
}
else
{
MessageBox.Show("32 bit");
}
}
foreach (string s in Directory.GetFiles(path, "*.ocx").Select(Path.GetFileName))
_Regfiles.Add(Application.StartupPath + "\\CCC DLL\\" + s);
foreach (string filepath in _Regfiles)
{
obj = new DLLHelper(filepath);
obj.DumpToFile(RootPath + obj.GetDLLName());
}
MessageBox.Show("Register Colmpleted");
}
catch (Exception ex)
{
//Console.WriteLine("Is64BitOperatingSystem throws the exception: {0}",
// ex.Message);
MessageBox.Show(ex.Message.ToString());
}
}
public void Registar_Dlls(string filePath)
{
try
{
//'/s' : Specifies regsvr32 to run silently and to not display any message boxes.
//string arg_fileinfo = "/s" + " " + "\"" + filePath + "\"";
string arg_fileinfo = RootPath +"\regsvr32.exe"+" "+ "/s" + " " + "\"" + filePath;
Process reg = new Process();
//This file registers .dll files as command components in the registry.
reg.StartInfo.FileName = "cmd.exe"; //"regsvr32.exe";
reg.StartInfo.Arguments = arg_fileinfo;
reg.StartInfo.UseShellExecute = false;
reg.StartInfo.CreateNoWindow = true;
reg.StartInfo.RedirectStandardOutput = true;
if (System.Environment.OSVersion.Version.Major >= 6)
{
reg.StartInfo.Verb = "runas";
}
reg.Start();
reg.WaitForExit();
reg.Close();
// MessageBox.Show("Successfully Registered your Ocx files");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
I have to run a process ie a application on windows shutdown, is there any method to delay the windows shutdown and run the application in windows service...
protected override void OnShutdown()
{
// Add your save code here
// Add your save code here
StreamWriter str = new StreamWriter("D:\\Log.txt", true);
str.WriteLine("Service stoped due to on" + DateTime.Now.ToString());
str.Close();
base.OnShutdown();
}
I have used function above which overrides the shutdown and i was able to write a log entry to a text file but i was not able to run an application after that On searching i found that the delay was below only some seconds after user fires shutdown
this.RequestAdditionalTime(250000);
this gives an addition time delay of 25 seconds on shutdown event but i was not able to run the application. Can anyone suggest method or ideas to run application on shutdown.
The ability of applications to block a pending system shutdown was severely restricted in Windows Vista. The details are summarized in two handy articles on MSDN: Shutdown Changes for Windows Vista and Application Shutdown Changes in Windows Vista.
As that page indicates, you shouldn't rely on the ability to block shutdown for any longer than 5 seconds. If you wish to attempt to block a pending shutdown event, your application should use the new ShutdownBlockReasonCreate function, which allows you to register a string that explains to the user the reason why you think the shutdown should be blocked. The user reserves the ability to heed your advice and cancel the shutdown, or throw caution to the wind and cancel anyway.
As soon as your application finishes doing whatever it is that should not be interrupted by a shutdown, you should call the corresponding ShutdownBlockReasonDestroy function, which frees the reason string and indicates that the system can now be shut down.
Also remember that Windows Services now run in an isolated session and are prohibited from interacting with the user. My answer here also provides more details, as well as a pretty diagram.
Basically, this is impossible. Windows is going to fight you tooth and nail over starting up a separate process from your Service, as well as any attempt you make to block a pending shutdown. Ultimately, the user has the power to override anything you try to pull. This sounds like something you should solve using security policies, rather than an application—ask questions about that on Server Fault.
On Windows Vista SP1 and higher, the new SERVICE_CONTROL_PRESHUTDOWN is available. Unfortunately it is not supported by .NET framework yet, but here is workaround using reflection. Just inherit your service class from ServicePreshutdownBase, override OnStop and periodically call RequestAdditionalTime(). Note that CanShutdown should be set to false.
public class ServicePreshutdownBase : ServiceBase
{
public bool Preshutdown { get; private set; }
public ServicePreshutdownBase()
{
Version versionWinVistaSp1 = new Version(6, 0, 6001);
if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= versionWinVistaSp1)
{
var acceptedCommandsField = typeof (ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
if (acceptedCommandsField == null)
throw new InvalidOperationException("Private field acceptedCommands not found on ServiceBase");
int acceptedCommands = (int) acceptedCommandsField.GetValue(this);
acceptedCommands |= 0x00000100; //SERVICE_ACCEPT_PRESHUTDOWN;
acceptedCommandsField.SetValue(this, acceptedCommands);
}
}
protected override void OnCustomCommand(int command)
{
// command is SERVICE_CONTROL_PRESHUTDOWN
if (command == 0x0000000F)
{
var baseCallback = typeof(ServiceBase).GetMethod("ServiceCommandCallback", BindingFlags.Instance | BindingFlags.NonPublic);
if (baseCallback == null)
throw new InvalidOperationException("Private method ServiceCommandCallback not found on ServiceBase");
try
{
Preshutdown = true;
//now pretend stop was called 0x00000001
baseCallback.Invoke(this, new object[] {0x00000001});
}
finally
{
Preshutdown = false;
}
}
}
}
Here is example usage:
public partial class Service1 : ServicePreshutdownBase
{
public Service1()
{
InitializeComponent();
this.CanShutdown = false;
}
protected override void OnStop()
{
WriteLog(Preshutdown ? "Service OnPreshutdown" : "Service OnStop");
for (int i = 0; i < 180; i++)
{
Thread.Sleep(1000);
WriteLog("Service stop in progress...");
RequestAdditionalTime(2000);
}
WriteLog(Preshutdown ? "Service preshutdown completed" : "Service stop completed");
}
}
This will work for 3min 20s, if you need more time, then you need to configure service. The best place to do so is during installation. Just use the ServicePreshutdownInstaller instead of ServiceInstaller and set the PreshutdownTimeout to maximum time you will ever need.
public class ServicePreshutdownInstaller : ServiceInstaller
{
private int _preshutdownTimeout = 200000;
/// <summary>
/// Gets or sets the preshutdown timeout for the service.
/// </summary>
///
/// <returns>
/// The preshutdown timeout of the service. The default is 200000ms (200s).
/// </returns>
[DefaultValue(200000)]
[ServiceProcessDescription("ServiceInstallerPreshutdownTimeout")]
public int PreshutdownTimeout
{
get
{
return _preshutdownTimeout;
}
set
{
_preshutdownTimeout = value;
}
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
Version versionWinVistaSp1 = new Version(6, 0, 6001);
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version < versionWinVistaSp1)
{
//Preshutdown is not supported
return;
}
Context.LogMessage(string.Format("Setting preshutdown timeout {0}ms to service {1}", PreshutdownTimeout, ServiceName));
IntPtr service = IntPtr.Zero;
IntPtr sCManager = IntPtr.Zero;
try
{
// Open the service control manager
sCManager = OpenSCManager(null, null, ServiceControlAccessRights.SC_MANAGER_CONNECT);
if (sCManager == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to open Service Control Manager.");
// Open the service
service = OpenService(sCManager, ServiceName, ServiceAccessRights.SERVICE_CHANGE_CONFIG);
if (service == IntPtr.Zero) throw new Win32Exception();
// Set up the preshutdown timeout structure
SERVICE_PRESHUTDOWN_INFO preshutdownInfo = new SERVICE_PRESHUTDOWN_INFO();
preshutdownInfo.dwPreshutdownTimeout = (uint)_preshutdownTimeout;
// Make the change
int changeResult = ChangeServiceConfig2(
service,
ServiceConfig2InfoLevel.SERVICE_CONFIG_PRESHUTDOWN_INFO,
ref preshutdownInfo);
// Check that the change occurred
if (changeResult == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration.");
}
Context.LogMessage(string.Format("Preshutdown timeout {0}ms set to service {1}", PreshutdownTimeout, ServiceName));
}
finally
{
// Clean up
if (service != IntPtr.Zero)CloseServiceHandle(service);
if (sCManager != IntPtr.Zero)Marshal.FreeHGlobal(sCManager);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_PRESHUTDOWN_INFO
{
public UInt32 dwPreshutdownTimeout;
}
[Flags]
public enum ServiceControlAccessRights : int
{
SC_MANAGER_CONNECT = 0x0001, // Required to connect to the service control manager.
SC_MANAGER_CREATE_SERVICE = 0x0002, // Required to call the CreateService function to create a service object and add it to the database.
SC_MANAGER_ENUMERATE_SERVICE = 0x0004, // Required to call the EnumServicesStatusEx function to list the services that are in the database.
SC_MANAGER_LOCK = 0x0008, // Required to call the LockServiceDatabase function to acquire a lock on the database.
SC_MANAGER_QUERY_LOCK_STATUS = 0x0010, // Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020, // Required to call the NotifyBootConfigStatus function.
SC_MANAGER_ALL_ACCESS = 0xF003F // Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table.
}
[Flags]
public enum ServiceAccessRights : int
{
SERVICE_QUERY_CONFIG = 0x0001, // Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration.
SERVICE_CHANGE_CONFIG = 0x0002, // Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators.
SERVICE_QUERY_STATUS = 0x0004, // Required to call the QueryServiceStatusEx function to ask the service control manager about the status of the service.
SERVICE_ENUMERATE_DEPENDENTS = 0x0008, // Required to call the EnumDependentServices function to enumerate all the services dependent on the service.
SERVICE_START = 0x0010, // Required to call the StartService function to start the service.
SERVICE_STOP = 0x0020, // Required to call the ControlService function to stop the service.
SERVICE_PAUSE_CONTINUE = 0x0040, // Required to call the ControlService function to pause or continue the service.
SERVICE_INTERROGATE = 0x0080, // Required to call the ControlService function to ask the service to report its status immediately.
SERVICE_USER_DEFINED_CONTROL = 0x0100, // Required to call the ControlService function to specify a user-defined control code.
SERVICE_ALL_ACCESS = 0xF01FF // Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table.
}
public enum ServiceConfig2InfoLevel : int
{
SERVICE_CONFIG_DESCRIPTION = 0x00000001, // The lpBuffer parameter is a pointer to a SERVICE_DESCRIPTION structure.
SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002, // The lpBuffer parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure.
SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007 // The lpBuffer parameter is a pointer to a SERVICE_PRESHUTDOWN_INFO structure.
}
[DllImport("advapi32.dll", EntryPoint = "OpenSCManager")]
public static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
ServiceControlAccessRights desiredAccess);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
public static extern int CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll", EntryPoint = "OpenService")]
public static extern IntPtr OpenService(
IntPtr hSCManager,
string serviceName,
ServiceAccessRights desiredAccess);
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
public static extern int ChangeServiceConfig2(
IntPtr hService,
ServiceConfig2InfoLevel dwInfoLevel,
ref SERVICE_PRESHUTDOWN_INFO lpInfo);
}
I have a similar problem, and there is one trick that might work in your case. You can start application in question before shutdown is initiated with CREATE_SUSPENDED flag (see this). This will ensure that the process will be created, but never run. On shutdown you can ResumeThread that process, and it will go on with execution.
Note, that it might be possible that the process will not be able to initialize and run anyway, since during shutdown some OS functions will fail.
Another implication is: the process which is supposed to run on shutdown will show in task manager. It would be possible to kill that process.
Here is an article on the Shutdown Event Tracker. You can activate it in Windows XP. It prompts the user for a reason for shutdown.
namespace WindowsService1
{
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public int serviceType;
public int currentState;
public int controlsAccepted;
public int win32ExitCode;
public int serviceSpecificExitCode;
public int checkPoint;
public int waitHint;
}
public enum SERVICE_STATE : uint
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007
}
public enum ControlsAccepted
{
ACCEPT_STOP = 1,
ACCEPT_PAUSE_CONTINUE = 2,
ACCEPT_SHUTDOWN = 4,
ACCEPT_PRESHUTDOWN = 0xf,
ACCEPT_POWER_EVENT = 64,
ACCEPT_SESSION_CHANGE = 128
}
[Flags]
public enum SERVICE_CONTROL : uint
{
STOP = 0x00000001,
PAUSE = 0x00000002,
CONTINUE = 0x00000003,
INTERROGATE = 0x00000004,
SHUTDOWN = 0x00000005,
PARAMCHANGE = 0x00000006,
NETBINDADD = 0x00000007,
NETBINDREMOVE = 0x00000008,
NETBINDENABLE = 0x00000009,
NETBINDDISABLE = 0x0000000A,
DEVICEEVENT = 0x0000000B,
HARDWAREPROFILECHANGE = 0x0000000C,
POWEREVENT = 0x0000000D,
SESSIONCHANGE = 0x0000000E
}
public enum INFO_LEVEL : uint
{
SERVICE_CONFIG_DESCRIPTION = 0x00000001,
SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002,
SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003,
SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x00000004,
SERVICE_CONFIG_SERVICE_SID_INFO = 0x00000005,
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006,
SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007,
SERVICE_CONFIG_TRIGGER_INFO = 0x00000008,
SERVICE_CONFIG_PREFERRED_NODE = 0x00000009
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_PRESHUTDOWN_INFO
{
public UInt32 dwPreshutdownTimeout;
}
[Flags]
public enum SERVICE_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SERVICE_QUERY_CONFIG = 0x00001,
SERVICE_CHANGE_CONFIG = 0x00002,
SERVICE_QUERY_STATUS = 0x00004,
SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
SERVICE_START = 0x00010,
SERVICE_STOP = 0x00020,
SERVICE_PAUSE_CONTINUE = 0x00040,
SERVICE_INTERROGATE = 0x00080,
SERVICE_USER_DEFINED_CONTROL = 0x00100,
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START |
SERVICE_STOP |
SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE |
SERVICE_USER_DEFINED_CONTROL)
}
[Flags]
public enum SCM_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SC_MANAGER_CONNECT = 0x00001,
SC_MANAGER_CREATE_SERVICE = 0x00002,
SC_MANAGER_ENUMERATE_SERVICE = 0x00004,
SC_MANAGER_LOCK = 0x00008,
SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,
SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE |
SC_MANAGER_LOCK |
SC_MANAGER_QUERY_LOCK_STATUS |
SC_MANAGER_MODIFY_BOOT_CONFIG
}
public partial class Service1 : ServiceBase
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
internal static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100;
const int SERVICE_CONTROL_PRESHUTDOWN = 0xf;
public Service1()
{
InitializeComponent();
CanShutdown = true;
tim = new Timer();
tim.Interval = 5000;
tim.Elapsed += tim_Elapsed;
FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
int value = (int)acceptedCommandsFieldInfo.GetValue(this);
acceptedCommandsFieldInfo.SetValue(this, value | SERVICE_ACCEPT_PRESHUTDOWN);
StreamWriter writer = new StreamWriter("D:\\LogConst.txt", true);
try
{
IntPtr hMngr = OpenSCManager("localhost", null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
IntPtr hSvc = OpenService(hMngr, "WindowsService1", (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
SERVICE_PRESHUTDOWN_INFO spi = new SERVICE_PRESHUTDOWN_INFO();
spi.dwPreshutdownTimeout = 5000;
IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(spi));
if (lpInfo == IntPtr.Zero)
{
writer.WriteLine(String.Format("Unable to allocate memory for service action, error was: 0x{0:X} -- {1}", Marshal.GetLastWin32Error(), DateTime.Now.ToLongTimeString()));
}
Marshal.StructureToPtr(spi, lpInfo, false);
// apply the new timeout value
if (!ChangeServiceConfig2(hSvc, (int)INFO_LEVEL.SERVICE_CONFIG_PRESHUTDOWN_INFO, lpInfo))
writer.WriteLine(DateTime.Now.ToLongTimeString() + " Failed to change service timeout");
else
writer.WriteLine(DateTime.Now.ToLongTimeString() + " change service timeout : " + spi.dwPreshutdownTimeout);
}
catch (Exception ex)
{
writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message);
}
writer.Close();
}
void tim_Elapsed(object sender, ElapsedEventArgs e)
{
result = false;
StreamWriter writer = new StreamWriter("D:\\hede.txt", true);
writer.WriteLine(DateTime.Now.ToLongTimeString());
//System.Threading.Thread.Sleep(5000);
writer.Close();
result = true;
tim.Stop();
}
Timer tim;
bool result = false;
protected override void OnStart(string[] args)
{
RequestAdditionalTime(1000);
tim.Start();
}
protected override void OnStop()
{
}
protected override void OnCustomCommand(int command)
{
StreamWriter writer = new StreamWriter("D:\\Log.txt", true);
try
{
if (command == SERVICE_CONTROL_PRESHUTDOWN)
{
int checkpoint = 1;
writer.WriteLine(DateTime.Now.ToLongTimeString());
while (!result)
{
SERVICE_STATUS myServiceStatus = new SERVICE_STATUS();
myServiceStatus.currentState = (int)SERVICE_STATE.SERVICE_STOP_PENDING;
myServiceStatus.serviceType = 16;
myServiceStatus.serviceSpecificExitCode = 0;
myServiceStatus.checkPoint = checkpoint;
SetServiceStatus(this.ServiceHandle, ref myServiceStatus);
checkpoint++;
}
writer.WriteLine(DateTime.Now.ToLongTimeString());
}
}
catch (Exception ex)
{
writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message);
}
writer.Close();
base.OnCustomCommand(command);
}
}
}