how can i creating window station and windows desktop using c# - c#

i would like create new window station & windows desktop and attach my process to it. how can i do that
i need to know
Creating Window station and attach my desktop
Creating & switching between Desktop effectively
how do i attaching process to winlogon desktop(if it is possible )
Destroy created desktop and return back to windows desktop

There is only one interactive window station per 'session' but you can have multiple sessions.
http://blogs.technet.com/markrussinovich/archive/2010/02/24/3315174.aspx
I'm not aware of an API to directly create a login session but if you are using a Windows Server version you could use Remote Desktop to create a local session, autorun your program there, then logout again once the program has ended (the program you are running in the Remote Desktop Session could logout when finished).
The code below will use the MSTSC ActiveX control to programmatically create an RDP session. You will need to manually generate the ActiveX stubs and add them to your project.
From the Visual Studio Command Prompt type the following: aximp.exe %windir%\system32\mstscax.dll
Copy the generated files (MSTSCLib.dll and AxMSTSCLib.dll) to the project directory.
Add both files to the project references.
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 AxMSTSCLib;
using MSTSCLib;
using System.Runtime.InteropServices;
namespace AutoLogin
{
public partial class Form1 : Form
{
private AxMSTSCLib.AxMsRdpClient5 rdpClient;
public Form1()
{
InitializeComponent();
rdpClient = new AxMSTSCLib.AxMsRdpClient5();
((ISupportInitialize)rdpClient).BeginInit();
rdpClient.Enabled = true;
rdpClient.Location = new System.Drawing.Point(0, 0);
rdpClient.Name = "MsRdpClient";
rdpClient.Size = ClientSize;
rdpClient.TabIndex = 1;
rdpClient.Anchor = (AnchorStyles)
(AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right);
Controls.Add(rdpClient);
((ISupportInitialize)rdpClient).EndInit();
}
void axRemoteDesktop_OnDisconnected
(object sender, IMsTscAxEvents_OnDisconnectedEvent e)
{
Application.Idle += ExitTimerEvent;
}
public void ExitTimerEvent(object source, EventArgs e)
{
Application.Idle -= ExitTimerEvent;
// Attempt to close down the session we just connected to (there
// appears to be no way to get the session id, so we just close all
// disconnected sessions.
if (rdpClient.Connected == 1) {
rdpClient.Disconnect();
}
LogoffDisconnectedSessions();
Close();
}
private Timer logoffTimer;
private void Form1_Load(object sender, EventArgs e)
{
// Close down any existing disconnected sessions, the number of
// available sessions is limited.
LogoffDisconnectedSessions();
String username = "username";
String password = "password";
rdpClient.Server = "localhost";
rdpClient.UserName = username;
rdpClient.AdvancedSettings2.ClearTextPassword = password;
rdpClient.Domain = "";
rdpClient.FullScreen = false;
rdpClient.AdvancedSettings2.RedirectDrives = false;
rdpClient.AdvancedSettings2.RedirectPrinters = false;
rdpClient.AdvancedSettings2.RedirectPorts = false;
rdpClient.AdvancedSettings2.RedirectSmartCards = false;
rdpClient.AdvancedSettings6.RedirectClipboard = false;
rdpClient.AdvancedSettings6.MinutesToIdleTimeout = 1;
rdpClient.OnDisconnected += new
AxMSTSCLib.IMsTscAxEvents_OnDisconnectedEventHandler
(axRemoteDesktop_OnDisconnected);
rdpClient.Connect();
logoffTimer = new Timer();
logoffTimer.Tick += new EventHandler(LogoutTimerEvent);
logoffTimer.Interval = 150000;
logoffTimer.Start();
}
private void Form1_Close(object sender, FormClosedEventArgs e)
{
Application.Idle -= ExitTimerEvent;
if (rdpClient.Connected == 1) {
rdpClient.Disconnect();
}
}
public void LogoutTimerEvent(object source, EventArgs e)
{
logoffTimer.Stop();
rdpClient.Disconnect();
}
enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct WTS_SESSION_INFO
{
public int SessionId;
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("wtsapi32.dll")]
private static extern bool WTSLogoffSession(IntPtr hServer, int SessionId, bool bWait);
private static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto)]
private static extern bool WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)]
int Reserved,
[MarshalAs(UnmanagedType.U4)]
int Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)]
ref int pCount);
[DllImport("wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pMemory);
private void LogoffDisconnectedSessions()
{
IntPtr buffer = IntPtr.Zero;
int count = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
ref buffer, ref count)) {
WTS_SESSION_INFO sessionInfo = new WTS_SESSION_INFO();
for (int index = 0; index < count; index++) {
sessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(
new IntPtr(buffer.ToInt32() +
(Marshal.SizeOf(sessionInfo) * index)),
typeof(WTS_SESSION_INFO));
WTS_CONNECTSTATE_CLASS state = sessionInfo.State;
if (state == WTS_CONNECTSTATE_CLASS.WTSDisconnected)
{
WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE,
sessionInfo.SessionId, true);
}
}
}
WTSFreeMemory(buffer);
}
}
}

Although Windows supports multiple "window stations", the documentation states that:
The interactive window station, Winsta0, is the only window station that can display a user interface or receive user input. It is assigned to the logon session of the interactive user, and contains the keyboard, mouse, and display device. All other window stations are noninteractive, which means they cannot display a user interface or receive user input.
This indicates that the ability to switch between window stations in the way you are proposing is not possible.

Related

Bring process to focus c# (ASP.net)

I have created a web application that will call a window based application using:
#region Process
public static string shell_exec(string path, string args)
{
var p = new ProcessStartInfo();
// Redirect the output stream of the child process.
p.UseShellExecute = false;
p.LoadUserProfile = true;
p.RedirectStandardOutput = true;
p.FileName = path;
p.Arguments = args;
p.WindowStyle = ProcessWindowStyle.Normal;
var pc = Process.Start(p);
WindowHelper.BringProcessToFront(pc);
string output = pc.StandardOutput.ReadToEnd();
pc.WaitForExit();
return output;
}
#endregion
this is published using IIS Express server, the process.start() calls the application and shows in the top most (obviously because i set the property topmost true) but not in the taskbar (didn't activate the load event or not focused)
I also used bringprocesstofront function but didn't do anything:
const int SW_RESTORE = 9;
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
public static void BringProcessToFront(Process process)
{
IntPtr handle = process.MainWindowHandle;
if (IsIconic(handle))
{
ShowWindow(handle, SW_RESTORE);
}
SetForegroundWindow(handle);
}
am i missing something in my code?
The external process im calling is an application that will register a persons fingerprint (using DPFP digital persona, creating it in window based unless there is for web or javascript)

Issues with injection code

So after looking around online, and debugging the code I found out that there are issues with using CreateRemoteThread and CreateRemoteThreadEx on windows 8, 8.1, and 10 (The dll does not inject at all). The code works fine for anyone who is not using windows 8+. I was wondering if anyone could help me debug the code in order for it to work on the newer operating system, and if possible to provide a explanation to why it is not working. This is the first time I looked into c#, I mainly program in Java.
While I was following the stack I know that it is coming from InjectLibrary in Injector.cs
// load dll via call to LoadLibrary using CreateRemoteThread
hThread = Imports.CreateRemoteThread(_handle, IntPtr.Zero, 0, hLoadLib, pLibRemote, 0, IntPtr.Zero);
Program.cs:
using System;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Syringe;
namespace GameLauncherEx
{
class Program
{
// Injector code by adaephon on ownedcore
// www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/265219-c-net-dll-injector.html
static void Main(string[] args)
{
string ip = "127.0.0.1";
int maxTryCount = 5;
int waitWindowSleep = 1;
int failInjectSleep = 500;
string dll = "IPRedirect.dll";
string client = string.Format("{0}\\MapleStory.exe", Environment.CurrentDirectory);
if (!File.Exists(client))
{
MessageBox.Show("Couldn't find MapleStory.exe", "GameLauncherEx");
return;
}
if (!File.Exists(string.Format("{0}\\{1}", Environment.CurrentDirectory, dll)))
{
MessageBox.Show("Couldn't find IPRedirect.dll", "GameLauncherEx");
return;
}
IPAddress ipAddress;
if (args.Length >= 1 && IPAddress.TryParse(args[0], out ipAddress)) {
ip = args[0];
MessageBox.Show(args[0]);
}
using(Process process = Process.Start(client, "GameLaunching"))
{
while (process.MainWindowHandle == IntPtr.Zero && !process.HasExited)
Thread.Sleep(waitWindowSleep);
if (process.HasExited)
return;
for (int i = 0; i < maxTryCount; i++)
{
try
{
using (Injector injector = new Injector(process))
{
injector.EjectOnDispose = false;
injector.InjectLibrary(dll);
if (ip != IPAddress.Loopback.ToString())
injector.CallExport<IPInfo>(dll, "SetIP", new IPInfo(ip));
// Add any additional IPs you want maped here, you can also unmap them with UnMapIP if needed
//injector.CallExport<MapedIPInfo>(dll, "MapIP", new MapedIPInfo("RealGameIP", "YourServerIP"));
//injector.CallExport<MapedIPInfo>(dll, "UnMapIP", new MapedIPInfo("RealGameIP", "YourServerIP"));
return;
}
}
catch (Exception e)
{
Thread.Sleep(failInjectSleep);
MessageBox.Show(e.ToString());
}
}
}
MessageBox.Show("Failed to initialize GameLauncherEx");
}
[StructLayout(LayoutKind.Sequential)]
struct IPInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string IP;
public IPInfo(string ip)
{
IP = ip;
}
}
[StructLayout(LayoutKind.Sequential)]
struct MapedIPInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string DestIP;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string IP;
public MapedIPInfo(string destIP, string ip)
{
DestIP = destIP;
IP = ip;
}
}
}
}
Injector.cs:
http://pastebin.com/QUVXSTHC
Imports.cs
http://pastebin.com/L1CtWYfN
I seemed to have surpassed the character limit, so I posted the code on pastebin.

How to close all file handles on the file and delete it, which is being copied and given to printer for printing job

I have a C# application in which i am sending sending some string variable to my local printer. So what i wanted to do is, i want to delete a particular file from folder which is
copied to the folder prior to my print command.
Its like this.. Copy the file to the //debug folder, after printing is finished delete the file. I am bit confused how to understand my printing job is done.
Below are my codes.
private void print_Click(object sender, EventArgs e)
{
string s = image_print() + Print_image();
PrintFactory.sendTextToLPT1(s);
/*string Filename = img_path.Text;
MessageBox.Show("file", Filename);
// if (Filename.ToCharArray().Intersect(Path.GetInvalidFileNameChars()).Any())
// return;
File.Delete(Path.Combine(#"E:\Debug", Filename));*/
}
private string image_print()
{
OpenFileDialog ofd = new OpenFileDialog();
string path = "";
string full_path = "";
string filename_noext = "";
ofd.InitialDirectory = #"C:\ZTOOLS\FONTS";
ofd.Filter = "GRF files (*.grf)|*.grf";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
filename_noext = System.IO.Path.GetFileName(ofd.FileName);
path = Path.GetFullPath(ofd.FileName);
img_path.Text = filename_noext;
//MessageBox.Show(filename_noext, "Filename");
// MessageBox.Show(full_path, "path");
//move file from location to debug
string replacepath = #"E:\Debug";
string fileName = System.IO.Path.GetFileName(path);
string newpath = System.IO.Path.Combine(replacepath, fileName);
if (!System.IO.File.Exists(filename_noext))
System.IO.File.Copy(path, newpath);
}
StreamReader test2 = new StreamReader(img_path.Text);
string s = test2.ReadToEnd();
return s;
}
private string Print_image()
{
//passing some commands and returns its as a sting "S"
return s;
}
And in my separate class function i have instructions to deal with the printer. SO below is the codes that goes for my print.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using System.IO;
namespace DVZebraPrint
{
public class Print
{
public const short FILE_ATTRIBUTE_NORMAL = 0x80;
public const short INVALID_HANDLE_VALUE = -1;
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint CREATE_NEW = 1;
public const uint CREATE_ALWAYS = 2;
public const uint OPEN_EXISTING = 3;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
public static void sendTextToLPT1(String receiptText)
{
IntPtr ptr = CreateFile("LPT1", GENERIC_WRITE, 0,
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
/* Is bad handle? INVALID_HANDLE_VALUE */
if (ptr.ToInt32() == -1)
{
/* ask the framework to marshall the win32 error code to an exception */
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
else
{
FileStream lpt = new FileStream(ptr, FileAccess.ReadWrite);
Byte[] buffer = new Byte[2048];
//Check to see if your printer support ASCII encoding or Unicode.
//If unicode is supported, use the following:
//buffer = System.Text.Encoding.Unicode.GetBytes(Temp);
buffer = System.Text.Encoding.ASCII.GetBytes(receiptText);
lpt.Write(buffer, 0, buffer.Length);
lpt.Close();
}
}
}
}
you should return the file path only from image_print() function. then after printing the string delete that file.
private string image_print()
{
...Your code
string newpath = string.Empty;
if (!System.IO.File.Exists(filename_noext))
System.IO.File.Copy(path, newpath);
return newpath;
}
And read the string from that file in your print button click event
private void print_Click(object sender, EventArgs e)
{
string filePath = image_print();
StreamReader test2 = new StreamReader(filePath);
string s = test2.ReadToEnd();
test2.Close();
s += Print_image();
PrintFactory.sendTextToLPT1(s);
System.IO.File.Delete(filePath); //Now delete the file which you have copied in image_print() method.
}

unable to refresh windows after setting the global proxy settings

I'm trying to enable/disable the windows global proxy( in Internet option ) using windows registry. If I set the value, I don't have any problems but refreshing the settings. After searching I found a question on SO that provided the code for that. But now the problem is that In each session of the app it works only once. i.e. it works the first time and if you want it to work again, you have to rerun the app. Any ideas what could be the problem??? here's the code
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 Microsoft.Win32;
using System.Runtime.InteropServices;
namespace SystemProxyToggle
{
public partial class Form1 : Form
{
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;
static bool settingsReturn, refreshReturn;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
queryStatus();
}
private void btnToggle_Click(object sender, EventArgs e)
{
toggleStatus();
queryStatus();
}
private void queryStatus()
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
int status = (int)registry.GetValue("ProxyEnable");
if (status == 0)
{
lblStatus.Text = "Proxy Is Disabled";
lblStatus.ForeColor = Color.Maroon;
}
else
{
lblStatus.Text = "Proxy Is Enabled";
lblStatus.ForeColor = Color.Green;
}
//I added this after I encountered the problem
//though I don't that this is the problem
registry.Close();
}
private void toggleStatus()
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
int status = (int)registry.GetValue("ProxyEnable");
if (status == 1)
{
registry.SetValue("ProxyEnable", 0);
}
else
{
registry.SetValue("ProxyEnable", 1);
}
//Refresh System Settings
settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
//I added this after I encountered the problem
registry.Close();
}
}
}
I've tested this code in Windows 7 (Internet Explorer 11) and had no problems, but when I tried in a second system with Windows 8, I had the same result as you did.
After digging for a while I found out that you should call InternetSetOption differently, basically like this:
INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, IntPtr.Zero, 0);
I tried it again on both systems and it worked just fine.
Cheers
For anybody else stuck on this issue, something odd seemed to do the trick for me.
I simply removed/commented all calls to InternetSetOption()
//settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
//refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
Restarted the program and this time the registry changes took effect in both directions as long as I wanted and not just the first time.

how to delay shutdown and run a process in window service

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

Categories