我试图使用CreateProcessAsUser启动服务,但由于某种原因,在调试时会创建EXE的多个(30+)实例。这些进程开始在这一行代码上产生:
ret = CreateProcessAsUser(DupedToken, Path, null, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi);
我使用了这个例子中的代码- http://support.microsoft.com/default.aspx?scid=kb;EN-US;889251。
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
string curFile2 = AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt";
public void createProcessAsUser()
{
IntPtr Token = new IntPtr(0);
IntPtr DupedToken = new IntPtr(0);
bool ret;
//Label2.Text+=WindowsIdentity.GetCurrent().Name.ToString();
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.bInheritHandle = false;
sa.Length = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = (IntPtr)0;
Token = WindowsIdentity.GetCurrent().Token;
const uint GENERIC_ALL = 0x10000000;
const int SecurityImpersonation = 2;
const int TokenType = 1;
ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupedToken);
if (ret == false)
File.AppendAllText(curFile2, "DuplicateTokenEx failed with " + Marshal.GetLastWin32Error());
else
File.AppendAllText(curFile2, "DuplicateTokenEx SUCCESS");
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "";
string Path;
Path = @"C:\myEXEpath";
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
ret = CreateProcessAsUser(DupedToken, Path, null, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi);
if (ret == false)
File.AppendAllText(curFile2, "CreateProcessAsUser failed with " + Marshal.GetLastWin32Error());
else
{
File.AppendAllText(curFile2, "CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
ret = CloseHandle(DupedToken);
if (ret == false)
File.AppendAllText(curFile2, Marshal.GetLastWin32Error().ToString() );
else
File.AppendAllText(curFile2, "CloseHandle SUCCESS");
}

发布于 2014-01-28 03:11:46
上面概述的步骤将在每次执行createProcessAsUser()方法时生成一个进程。现在,此方法不包含任何终止或终止进程的代码,因此重复调用此方法将生成多个进程。当您的代码被显示时,该方法将面对只生成一个进程。
我想真正的答案是你怎么称呼这个方法。正如你在评论中所说的
我试图在用户会话中启动.exe
我只能假设您可能从Session启动、Application_BeginRequest或其他方法调用此进程,这些方法可能会根据应用程序的设计方式多次执行(此方法的调用代码将非常好地用作编辑)。
如前所述,每次调用该方法而不是终止该方法时,都会执行exe。如果您只希望运行应用程序的一个实例,则必须检查流程树以确定流程是否已在运行。现在,如果您应该让每个用户运行一个进程,那么您将需要执行上述操作,但也需要维护一个引用,即在应用程序第一次启动时创建的进程ID。
检查下面的代码以进行更改(简化)
public void createProcessAsUser()
{
//one process per session
object sessionPID = Session["_servicePID"];
if (sessionPID != null && sessionPID is int && Process.GetProcessById((int)sessionPID) != null)
return; //<-- Return process already running for session
else
Session.Remove("_servicePID");
//one process per application
object applicationPID = Application["_applicationPID"];
if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)applicationPID) != null)
return; //<-- Process running for application
else
Application.Remove("_applicationPID");
//omitted starting code
if (ret == false)
// omitted log failed
else
{
// omitted log started
//for one process per session
Session["_servicePID"] = Convert.ToInt32(pi.dwProcessId);
//for one process per application
Application["_applicationPID"] = Convert.ToInt32(pi.dwProcessId);
//close handles
}
// omitted the rest of the method
}这个简单的方法将对为应用程序创建的进程ID的引用保存为每个用户一个进程的Session状态或每个应用程序实例的一个进程的Application状态。
现在,如果这是预期的结果,您还可能希望在应用程序关闭(优雅地)或会话结束时终止进程。这将非常类似于我们的第一次检查,但可以这样做,如下所示。*注意,这没有考虑到工作进程关闭而不调用会话\应用程序结束事件,这些事件也应该在应用程序启动时处理。
//session end
void Session_End(object sender, EventArgs e)
{
object sessionPID = Session["_servicePID"];
if (sessionPID != null && sessionPID is int)
{
Process runningProcess = Process.GetProcessById((int)sessionPID);
if (runningProcess != null)
runningProcess.Kill();
}
}
//application end
void Application_End(object sender, EventArgs e)
{
object applicationPID = Application["_applicationPID"];
if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)applicationPID) != null)
{
Process runningProcess = Process.GetProcessById((int)applicationPID);
if (runningProcess != null)
runningProcess.Kill();
}
}再次,回到原来的问题,您如何停止多个实例。答案是通过检查如何启动实例(即对方法createProcessAsUser()的调用代码)来停止生成多个实例的能力,并相应地调整方法以避免多次调用。
如果这有助于详细说明如何调用createProcessAsUser()方法,请发布编辑。
更新1:
会话\应用程序在上下文中不存在。如果方法createProcessUser()在与ASPX页面不同的类中(如教程中所示),就会发生这种情况。
正因为如此,您将需要更改HttpContext的存在,只需调用
HttpContext.Currrent
我调整了上面的方法,将检查包含到HttpContext中。
public void createProcessAsUser()
{
//find the http context
var ctx = HttpContext.Current;
if (ctx == null)
throw new Exception("No Http Context");
//use the following code for 1 process per user session
object sessionPID = ctx.Session["_servicePID"];
if (sessionPID != null && sessionPID is int && Process.GetProcessById((int)sessionPID) != null)
return; //<-- Return process already running for session
else
ctx.Session.Remove("_servicePID");
//use the following code for 1 process per application instance
object applicationPID = ctx.Application["_applicationPID"];
if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)sessionPID) != null)
return; //<-- Process running for application
else
ctx.Application.Remove("_applicationPID");
// omitted code
if (ret == false)
{
//omitted logging
}
else
{
//omitted logging
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
//for one process per session
ctx.Session["_servicePID"] = Convert.ToInt32(pi.dwProcessId);
//for one process per application
ctx.Application["_applicationPID"] = Convert.ToInt32(pi.dwProcessId);
}
//omitted the rest
}您将不会通过调用HttpContext获得当前using System.Web(必须添加var ctx = HttpContext.Current )的前几行中的更改
接下来,我们只检查ctx变量是否为null。如果它为null,我将抛出一个异常,但是无论如何您都可以处理这个问题。
从那里开始,我没有直接调用Session和Application,而是更改了对ctx.Session...和ctx.Application...的引用
更新2:
这是一个调用上述方法的Windows应用程序。现在,这改变了球类游戏,因为上面的代码实际上是要启动一个进程,作为模拟的windows标识。现在,模拟通常是在WebApplications而不是WinForms中完成的(虽然可以这样做)。
如果您不是在模拟与运行应用程序的用户不同的用户。这意味着登录的用户是运行应用程序的用户。如果是这样的话,那么您的代码就会变得更容易很多。
下面是如何实现这一目标的一个例子。
/// <summary>
/// static process ID value
/// </summary>
static int? processID = null;
public void startProcess()
{
//check if the processID has a value and if the process ID is active
if (processID.HasValue && Process.GetProcessById(processID.Value) != null)
return;
//start a new process
var process = new Process();
var processStartInfo = new ProcessStartInfo(@"C:\myProg.exe");
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
process.StartInfo = processStartInfo;
process.Start();
//set the process id
processID = process.Id;
}同样,由于这是一个Win窗体应用程序,您可以使用Process对象启动进程,因此此windows应用程序将作为运行Windows应用程序的用户运行。在本例中,我们还保存了对processID的静态引用,并检查processID (如果找到)是否已经在运行。
https://stackoverflow.com/questions/21191947
复制相似问题