昨天,我研究了一下如何用c#编写windows服务,如果有兄弟不知道怎么弄,可以去看一下,因为我昨天晚上研究了它的应用,今天准备和大家分享一下。
昨天的文章在这里:彻底搞懂如何用c#编写windows服务
c#编写windows服务,这个链接第一次用,不太知道怎么弄。
昨天安装了一个啥也没干的windows服务,我就在想,这个东西能做什么呢?最后我决定制作一个守护程序,功能就是监测另一个程序,看它有没有启动,没有的话就启动它。
制作一个简单的程序,我们就来监测它是不是启动了,程序文件名:text.exe。
切换到service的代码视图
整体代码如下,,这里我用LOG文件做了日志:
using System;
using System.IO;
using System.ServiceProcess;
using System.Windows.Forms;
namespace A_Fw
{
public partial class TheService : ServiceBase
{
//定义一个timer
public System.Timers.Timer timer = new System.Timers.Timer();
//运行路径
string thepath = Application.StartupPath;
//日志文件路径
string logfile = "\\log.txt";
//被监控程序的进程名,用来检查它是不是已经运行
string jinchengming = "test";
//被监控程序的文件名
string runfilename = "\\test.exe";
public TheService()
{
InitializeComponent();
//定义timer事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = 1000; //时间间隔
}
//timer启动后,每隔一段时间检测下被监视程序是否运行,如果没有就运行它
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//根据进程名得到被监测程序的进程对象
System.Diagnostics.Process[] myproc = System.Diagnostics.Process.GetProcessesByName(jinchengming);
int isrun = myproc.Length;
//如果没有进程,说明这个程序没有运行
if (isrun == 0)
{
try
{
Log("程序未运行,准备运行...");
//运行被监控程序
System.Diagnostics.Process.Start(thepath + runfilename);
Log("程序已经启动...");
}
catch (Exception ex)
{
Log("启动程序时发现错误:" + ex.ToString());
Application.Exit();
}
}
else
{
Log("程序正在运行");
}
}
protected override void OnStart(string[] args)
{
//服务启动时的事件,timer开始工作
timer.Enabled = true;
timer.Start();
Log("服务启动...");
}
protected override void OnStop()
{
//服务停止时的事件,timer停止工作
timer.Enabled = false;
timer.Stop();
Log("服务停止...");
}
void Log(string logcontent) //记录日志
{
using (FileStream st = new FileStream(thepath + logfile, FileMode.Append))
{
using (StreamWriter wr = new StreamWriter(st))
{
wr.WriteLine(#34;{DateTime.Now.ToLongTimeString()},{logcontent}");
}
}
}
}
}
出现以下情况
是因为我们写的不是windows窗体服务,添加一下system.windows.Forms.dll,
再引用一下 using System.Windows.Forms; 就可以了。
重新生成解决方案,因为我程序中都是用的应该程序根目录,这样方便调试,所以这次需要把3个文件放在同一目录下
G:\x>installUtil.exe A_Fw.exe 安装服务,然后启动服务
运行后,test.exe并未弹出
但查看日志文件没有发现问题
查看进程,发现已经有了test进程,杀死后,还会出现这个进程,说明服务程序没有问题。但需要注意的是,此时test进程为“后台进程”
这是什么原因呢?给服务加上与桌面交互选项也是不行的
查了些资料,确定原因出现在这句:
System.Diagnostics.Process.Start(thepath + runfilename);
我不知道是什么原因,用它启动text.exe就是后台进程,
解决方法如下:新建一个文件(我并不知道为啥,这个是网上抄的)
这个文件直接照抄就行了。
using System;
using System.Runtime.InteropServices;
namespace RFW
{
public class WinAPI_Interop
{
public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
/// <summary>
/// 服务程序执行消息提示,前台MessageBox.Show
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
public static void ShowServiceMessage(string message, string title)
{
int resp = 0;
WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTSGetActiveConsoleSessionId(), title, title.Length, message, message.Length, 0, 0, out resp, false);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSSendMessage(IntPtr hServer, int SessionId, String pTitle, int TitleLength, String pMessage, int MessageLength, int Style, int Timeout, out int pResponse, bool bWait);
#region P/Invoke WTS APIs
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WTS_SESSION_INFO
{
public UInt32 SessionID;
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] UInt32 Reserved,
[MarshalAs(UnmanagedType.U4)] UInt32 Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount
);
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern void WTSFreeMemory(IntPtr pMemory);
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
#endregion
#region P/Invoke CreateProcessAsUser
/// <summary>
/// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser.
/// </summary>
///
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
/// <summary>
/// 以当前登录的windows用户(角色权限)运行指定程序进程
/// </summary>
/// <param name="hToken"></param>
/// <param name="lpApplicationName">指定程序(全路径)</param>
/// <param name="lpCommandLine">参数</param>
/// <param name="lpProcessAttributes">进程属性</param>
/// <param name="lpThreadAttributes">线程属性</param>
/// <param name="bInheritHandles"></param>
/// <param name="dwCreationFlags"></param>
/// <param name="lpEnvironment"></param>
/// <param name="lpCurrentDirectory"></param>
/// <param name="lpStartupInfo">程序启动属性</param>
/// <param name="lpProcessInformation">最后返回的进程信息</param>
/// <returns>是否调用成功</returns>
[DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CloseHandle(IntPtr hHandle);
#endregion
/// <summary>
/// 以当前登录系统的用户角色权限启动指定的进程
/// </summary>
/// <param name="ChildProcName">指定的进程(全路径)</param>
public static void CreateProcess(string ChildProcName)
{
IntPtr ppSessionInfo = IntPtr.Zero;
UInt32 SessionCount = 0;
if (WTSEnumerateSessions(
(IntPtr)WTS_CURRENT_SERVER_HANDLE, // Current RD Session Host Server handle would be zero.
0, // This reserved parameter must be zero.
1, // The version of the enumeration request must be 1.
ref ppSessionInfo, // This would point to an array of session info.
ref SessionCount // This would indicate the length of the above array.
))
{
for (int nCount = 0; nCount < SessionCount; nCount++)
{
WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State)
{
IntPtr hToken = IntPtr.Zero;
if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken))
{
PROCESS_INFORMATION tProcessInfo;
STARTUPINFO tStartUpInfo = new STARTUPINFO();
tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
bool ChildProcStarted = CreateProcessAsUser(
hToken, // Token of the logged-on user.
ChildProcName, // Name of the process to be started.
null, // Any command line arguments to be passed.
IntPtr.Zero, // Default Process' attributes.
IntPtr.Zero, // Default Thread's attributes.
false, // Does NOT inherit parent's handles.
0, // No any specific creation flag.
null, // Default environment path.
null, // Default current directory.
ref tStartUpInfo, // Process Startup Info.
out tProcessInfo // Process information to be returned.
);
if (ChildProcStarted)
{
CloseHandle(tProcessInfo.hThread);
CloseHandle(tProcessInfo.hProcess);
}
else
{
int err = Marshal.GetLastWin32Error();
ShowServiceMessage("CreateProcessAsUser失败:" + err.ToString(), "CreateProcess");
}
CloseHandle(hToken);
break;
}
}
}
WTSFreeMemory(ppSessionInfo);
}
}
}
}
将打开文件那句改成:
try
{
Log("程序未运行,准备运行...");
//运行被监控程序,下面这句不行
//System.Diagnostics.Process.Start(thepath + runfilename);
//用这句才可以打开应用进程
RFW.WinAPI_Interop.CreateProcess(thepath + @runfilename);
Log("程序已经启动...");
}
重新安装服务,运行效果如下:
明天再来研究,用其它程序来控制这个服务的安装与卸载
如果看得上的话,需要源码可以联系我。