Running cscript.exe from C# .ashx does not execute code in vbscript file
EDIT
I added in some error handling to my .vbs file and it is indeed a permissions issue (I now get a "Permission Denied error"). However, supplying my credentials in the web.config <impersonate>
tag does not seem to have any effect.
Also when trying to supply my credentials to the process via via
p.StartInfo.Password = Misc.CreateSecurityString("password");
p.StartInfo.UserName = "admin";
I get an new error:
cscript.exe - Application error
The application failed to initialize properly (0xc0000142). Click on OK to terminate the application.
Shout it out if you know what's causing this. (Or just type it...)
Thanks for your help so far!
Background
I'm trying to execute a .vbs file from an custom handler (.ashx). The VBScript is setting up a web application in iis 5.1.
So far the following code executes without errors
string sToRun = "C:\CreateIISApplication.vbs"
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cscript";
p.StartInfo.Arguments = sToRun;
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string sOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Problem
My problem is that the vbscript appears to not have run at all. When I check IIS, my application is not created.
When I run the script file direct开发者_如何学JAVAly from the command prompt everything works correctly and my application shows up in IIS.
Trouble Shooting
I decided to add some echo statements into the .vbs file so I could make sure it was running. On the command line, all the statements are outputted correctly. When checking the string sOutput I get the header message, but none of my subsequent messages.
From C# - contents of sOutput
Microsoft (R) Windows Script Host Version 5.7 Copyright (C) Microsoft Corporation. All rights reserved
From the Command Line
Microsoft (R) Windows Script Host Version 5.7 Copyright (C) Microsoft Corporation. All rights reserved
Hello
So I can prove (I think) that the .vbs file is not being evaluated, and cscript is being called. And if I call cscript without referencing a .vbs file, then I get the help documentation. So something is going wrong.
Any ideas? Thanks!
I imagine that a problem (maybe not the entire problem) is that the user that IIS is running under doesn't have the privileges to run scripts on the target machine.
You need to be an admin to create the website. You will need to change your web app to run as an admin user, or alternatively, you can launch the cscript process as an admin user.
Turns out I needed to skip using System.Diagnostics.Process
and use kernel32.dll and advapi32.dll methods.
"This is because in ASP.NET, impersonation is performed at the thread level and not at the process level." source
Also needed make my Anonymous Access account a member of "Replace a process level token" Control Panel -> Administrative Tools -> Local Security Settings. (You'll need to restart for this to take effect.
Here's the adapted code from MSDN (http://support.microsoft.com/kb/889251).
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.IO;
using System.Security.Principal;
namespace UtilityLib
{
public class Win32Process
{
[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);
public static void CreateProcess(string cmdline)
{
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)
{
throw new Exception("DuplicateTokenEx failed with " + Marshal.GetLastWin32Error());
}
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "";
string commandLinePath = cmdline;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
ret = CreateProcessAsUser(DupedToken,null,commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi);
if (ret == false)
{
throw new Exception("CreateProcessAsUser failed with " + Marshal.GetLastWin32Error() + ": if 1314, make sure user is a member 'Replace a process level token' Control Panel -> Administrative Tools -> Local Security Settings.");
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
ret = CloseHandle(DupedToken);
if (ret == false)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}
}
}
}
Using it is simple:
string sToRun = @"cscript C:\CreateIISApplication.vbs"; //OR C:\myfile.bat arguments, or whatever else you want to run.
Win32Process.CreateProcess(sToRun);
精彩评论