Is it possible to get a list of the running processes along with their corresponding app domains when running a program? I am aware mscoree.dll allows me to retrieve all App Domains of the current process using the ICorRuntimeHost.EnumDomains method. Is there a way to get this information without using an external API and just pure C# code? I understand mdbg has some functions that may help but I am not sure how to use this debugger. I am really looking for a solution using just C#.
Thanks
EDIT:
The goal is to show every process running along with their corresponding app domains on an html page. Ideally there would be a function that iterates through all running processes and retrieves this information.
Code that retrieves all app domains for current process:
private static List<AppDomainInf> GetAppDomains()
{
IList<AppDomain> mAppDomainsList = new List<AppDomain>();
List<AppDomainInf> mAppDomainInfos = new List<AppDomainInf>();
IntPtr menumHandle = IntPtr.Zero;
ICorRuntimeHost host = new CorRuntimeHost();
try
{
host.EnumDomains(out menumHandle);
object mTempDomain = null;
//add all the current app domains running
while (true)
{
host.NextDomain(menumHandle, out mTempDomain);
if (mTempDomain == null) break;
AppDomain tempDomain = mTempDomain as AppDomain;
mAppDomainsList.Add((tempDomain));
}
//retrieve every app domains detailed information
foreach (var appDomain in mAppDomainsList)
{
AppDomainInf domainInf = new AppDomainInf();
domainInf.Assemblies = GetAppDomainAssemblies(appDomain);
domainInf.AppDomainName = appDomain.FriendlyName;
mAppDomainInfos.Add(domainInf);
}
return mAppDomainInfos;
}
catch (Exception)
{
throw; //rethrow
}
finally
{
host.CloseEnum(menumHandle);
Marshal.ReleaseComObject(host);
}
}
using MdbgCore.dll located inside C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\MdbgCore.dll.:
CorPublish cp = new CorPublish();
foreach (CorPublishProcess process in cp.EnumProcesses())
{
foreach (CorPublishAppDomain appDomain in process.EnumAppDomains())
{
}
}
Related
I have developed a launcher that updates/run our software in other companies enviroment. Most of the time the company approves the launcher but forgets about the actual application.
Currently im using Process.Start(procStart) to start the application, but that silently fails if applocker blocks the application.
So i was wondering if someone reliable way of detecting if applocker is active, and when it blocks my application( So i can provide a proper error message).
When the error happens my application seems idle to the user, a memory dump shows this:
Code note:
There is no exception handling or suppression of exceptions. If the launcher crashes i would expect to see it in the eventlog.
Added code:
private void StartzzzDesktop(int value)
{
var rel = Settings.zzzDesktopStore.GetReleaseInfo(Settings.ConnectionDetails.zzzDesktopID);
var proc = CreateProccess(rel);
if (proc == null)
{
Settings.LastError = zzzLauncherError.FatelErrorStartzzz;
Settings.EventManager.TriggerEvent(zzzDesktopStatus.FatalError);
return;
}
Logger.Log(EventLogEntryType.Information, $"Started zzz desktop and got PID {proc.Id} from {rel.GenerateExtrationPath()}");
Settings.EventManager.TriggerEventSync(zzzDesktopStatus.DeleteOldReleases);
Settings.EventManager.TriggerEvent(zzzDesktopStatus.ReleaseBackgroundWorkers);
GC.Collect();
var remoteStatus = new GetRemotezzzWebStatus();
while (!proc.HasExited)
{
Thread.Sleep(1000);
if(!remoteStatus.IsRemoteVersionCompatible())
{
proc.Kill();
Logger.Log(EventLogEntryType.Information, $"Detected that the remote website is no longer compatible with current runnign version, and we are killing desktop.");
}
}
if(proc.ExitCode != 0)
{
Settings.zzzDesktopStore.Delete(rel);
Logger.Log(EventLogEntryType.Warning, $"zzz exited with a none zero exit code ({proc.ExitCode}), the local cached installation will be deleted");
}
else
Logger.Log(EventLogEntryType.Information, $"zzz exited in a normal way with exitcode {proc.ExitCode}, running for {(DateTime.Now - proc.StartTime).ToString()} ");
CloseDown();
}
internal Process CreateProccess(zzzDesktopInfo release)
{
release = GetReleaseInfo(release.ID);
string pathzzzExe = Path.Combine(release.GenerateExtrationPath(), "zzz.exe");
var verifyStatus = UtilsVerifyFile.Verify(pathzzzExe);
if ( !File.Exists(pathzzzExe) || !verifyStatus.Verified)
{
Logger.Log(EventLogEntryType.Error, "Found zzz.exe in temp folder, but the certificate did not pass verification");
foreach (var logentry in verifyStatus.Logs)
Logger.Log(EventLogEntryType.Error, "Certificate verification log: " + logentry);
MarkDatabaseForPurge();
return null;
}
// Removed enterprise spesific code.
var procStart = new ProcessStartInfo();
procStart.FileName = pathzzzExe;
if (Settings.ConnectionDetails.zzzLoginToken != Guid.Empty )
{
procStart.Arguments = "/RefreshToken:" + Settings.ConnectionDetails.zzzLoginToken.ToString();
}
var process = Process.Start(procStart);
return process;
}
I am trying to programmatically get my site status from IIS to see if it's stopped, but I kept getting the following error,
The object identifier does not represent a valid object. (Exception from HRESULT: 0x800710D8)
The application is using ServerManager Site class to access the site status. Here is the code,
//This is fine, gets back the site
var serverManager = new Microsoft.Web.Administration.ServerManager(ConfigPath);
var site = serverManager.Sites.FirstOrDefault(x => x.Id == 5);
if (site == null) return;
var appPoolName = site.Applications["/"].ApplicationPoolName;
//error!
var state = site.State;
I've test with static site to isolate the issue, making sure that the site is up and running, all configuration are valid, point to the valid application pool...etc.
Let me know if you need more details. Is it the COM thing?
I figured out where the problem is. Basically, there are two parts to the Server manager, the first part of the server manager allows you to read site details from configuration file, which is what I've been doing above. The problem with that is you will only able get the information that's in file and site state is not part of it.
The second part of the Server Manager allows you to connect to the IIS directly and it does this by interacting with the COM element. So what I should be doing is this:
ServerManager manager= ServerManager.OpenRemote("testserver");
var site = manager.Sites.First();
var status = site.State.ToString() ;
I had a similar problem but mine was caused by the delay needed to activate the changes from the call to CommitChanges on the ServerManager object. I found the answer I needed here:
ServerManager CommitChanges makes changes with a slight delay
It seems like polling is required to get consistent results. Something similar to this solved my problem (I got the exception when accessing a newly added application pool):
...
create new application pool
...
sman.CommitChanges();
int i = 0;
const int max = 10;
do
{
i++;
try
{
if (ObjectState.Stopped == pool.State)
{
write_log("Pool was stopped, starting: " + pool.Name);
pool.Start();
}
sman.CommitChanges();
break;
}
catch (System.Runtime.InteropServices.COMException e)
{
if (i < max)
{
write_log("Waiting for IIS to activate new config...");
Thread.Sleep(1000);
}
else
{
throw new Exception(
"CommitChanges timed out efter " + max + " attempts.",
e);
}
}
} while (true);
...
I have been researching this issue pretty extensively and cannot seem to find an answer.
I know that the Only part of a ReadProcessMemory or WriteProcessMemory request was completed exception is thrown when a 32-bit process tries to access a 64-bit process and the same for a 64-bit modifying a 32-bit process.
The solution to that issue is to change the Platform Target to 'Any CPU'. I have tried this and unfortunately this does not solve my issue.
The next block of code is what keeps throwing the exception. The program that runs this code is used to open up applications on remote computers and keeps a list of all the processes that the program itself opened so that I don't have to loop through all the processes.
Process processToRemove = null;
lock (_runningProcesses)
{
foreach (Process p in _runningProcesses)
{
foreach (ProcessModule module in p.Modules)
{
string[] strs = text.Split('\\');
if (module.ModuleName.Equals(strs[strs.Length - 1]))
{
processToRemove = p;
break;
}
}
if (processToRemove != null)
{
break;
}
}
if (processToRemove != null)
{
processToRemove.Kill();
_runningProcesses.Remove(processToRemove);
}
}
These processes can and most likely will be 32-bit and 64-bit, mixed together.
Is there anything I am doing that I shouldn't be doing, or is there just a better way to do all of this?
As detailed in the comments of the MSDN page for Process.Modules and this thread there is a known issue in Process.Modules when enumerating 32 bit processes from a 64 bit process and visa-versa:
Internally .NET's Process.Modules is using function EnumProcessModules
from PSAPI.dll. This function has a known issue that it cannot work
across 32/64 bit process boundary. Therefore enumerating another
64-bit process from 32-bit process or vice versa doesn't work
correctly.
The solution seems to be to use the EnumProcessModulesEx function, (which must be called via P/Invoke), however this function is only available on later versions of Windows.
We fixed this issue by adding
a new function called EnumProcessModulesEx to PSAPI.dll
(http://msdn2.microsoft.com/en-us/library/ms682633.aspx), but we
currently cannot use it in this case:
it only works on Windows Vista or Windows Server 2008
currently .NET 2.0 Framework don't have a service pack or hotfix to make Process.Modules use this new API
There are only some issues regarding the handling of the processes and the locking that I would change:
object lockObject = new object();
List<Process> processesToRemove = new List<Process>();
foreach (Process p in _runningProcesses)
{
foreach (ProcessModule module in p.Modules)
{
string[] strs = text.Split('\\');
if (module.ModuleName.Equals(strs[strs.Length - 1]))
{
processesToRemove.Add(p);
break;
}
}
}
lock (lockObject)
{
foreach (Process p in processesToRemove)
{
p.Kill();
_runningProcesses.Remove(p);
}
}
I'm not answering for the bounty, just wanted to give some ideas. This code isn't tested because I don't exactly know what you are trying to do there.
Just consider not to lock the process-list and to keep the lock as short as possible.
I agree with #sprinter252 that _runningProcesses should not be used as your sync object here.
//Somewhere that is accessible to both the thread getting the process list and the thread the
//code below will be running, declare your sync, lock while adjusting _runningProcesses
public static readonly object Sync = new object();
IList<Process> runningProcesses;
lock(Sync)
{
runningProcesses = _runningProcesses.ToList();
}
Process processToRemove = null;
foreach (Process p in _runningProcesses)
{
foreach (ProcessModule module in p.Modules)
{
string[] strs = text.Split('\\');
if (module.ModuleName.Equals(strs[strs.Length - 1]))
{
processToRemove = p;
break;
}
}
if (processToRemove != null)
{
break;
}
}
if (processToRemove != null)
{
//If we've got a process that needs killing, re-lock on Sync so that we may
//safely modify the shared collection
lock(Sync)
{
processToRemove.Kill();
_runningProcesses.Remove(processToRemove);
}
}
If this code is wrapped in a loop to continue to check _runningProcesses for the process you wish to kill, consider changing processToRemove to processesToRemove and change it's type to a collection, iterate over that list in the bottom block after a check for a non-zero count and lock outside of that loop to decrease the overhead of obtaining and releasing locks per process to kill.
I have an application that does some central file generation based on user requests. What I want to be able to do with it once the files are created is to place them in that user's print queue ( in this organisation there is a central print queue so users are responsible for printing their own documents ) so that they can then be printed off when the user is ready.
By using the System.Printing assemblies in .net I am able to add a job to my own print queue, so I am sound on that part. My print code looks like this:
private void RunPrintJob( string myFileName )
{
PrintServer ps = new PrintServer(#"\\printatron");
PrintQueue queue = new PrintQueue(ps, "psandqueues");
try
{
PrintSystemJobInfo pj = queue.AddJob(myFileName);
Stream myStream = pj.JobStream;
Byte[] myByteBuffer = GenerateBufferFromFile(myFileName); myStream.Write(myByteBuffer, 0, myByteBuffer.Length);
myStream.Close();
}
catch (Exception ed)
{
Debug.WriteLine(ed.Message);
if (ed.InnerException != null)
{
Debug.WriteLine(" -> " + ed.InnerException);
}
result = false;
}
queue.Commit();
}
So I have my centrally created documents, I know which user was responsible for their creation and I can send them to the printer.
What I need now is a way to send them to the printer with the user who created them set as their user. Is there a way to do this through the print queue? I know it is readable from the PrintSystemJobInfo.Submitter property, but that is read-only. If not, do I have to do it through impersonation and if so in the latter case is there anything I can do to avoid having to store a bunch of user passwords and have the software fail every time the user changes their password? That seems like it would be a really clumsy way of operating, but as this activity isn't currently performed interactively what other options do I have?
I'm doing something similar. Impersonation is not too bad, if the process has sufficient permissions to get the level of impersonation you need (e.g. impersonation or delegation vs identification).
Here is what I do to impersonate:
public static bool GetImpersonationToken(string UPN, out IntPtr dupToken)
{
dupToken = IntPtr.Zero;
WindowsImpersonationContext impersonationContext = null;
bool result = false;
try
{
WindowsIdentity wid = new WindowsIdentity(UPN);
impersonationContext = wid.Impersonate();
result = DuplicateToken(wid.Token, 2, ref dupToken) != 0;
}
finally
{
if (impersonationContext != null)
impersonationContext.Undo();
}
return result;
}
Note: it is the calling method's responsibility to clean up that token handle.
I resolve the UPN from an LDAP query based on a users email (usually they are the same, but often enough they're not).
But I'm having some issues with the byte array that I'm passing in. I tried File.ReadAllBytes, but that causes the printer to spit out gibberish. Is there some special encoding that needs to happen in GenerateBufferFromFile?
** Update **
Looks like there are a bunch of issues around working with the JobStream directly:
Is PrintSystemJobInfo.JobStream broken?
So i'm just going to write to file, although I was hoping to avoid that.
I'm building a small web application with ASP.NET MVC 2, using db4o as a datastore.
I have added an HttpModule—as per the example here—to give the application access to the db4o database, and everything is working perfectly on my development machine under the VS2008 ASP.NET Development Server.
However, when I deploy the app to my web host and try to access it, I get a DatabaseFileLockedException at the line where the HttpModule tries to open the database file. But there should be nothing else accessing the file; indeed on first run of the app it will only just have been created when this exception gets thrown.
The web host's servers are running IIS 7 on Windows Server 2008, and the application is running under Full Trust. It is a sub-application, in case that makes any difference.
I can't work out why this error is occurring on the live server, but not locally on my development server. Can anyone help me out or suggest what I should do next?
That's a mistake in the example-code. It assumes that the HttpModule.Init is only called once, which isn't necessarily true. Depending how your application is configured, it can be called multiple times. To fix this, check in the HttpModule-Handler if the instance is already there:
using System;
using System.Configuration;
using System.Web;
using Db4objects.Db4o;
namespace Db4oDoc.WebApp.Infrastructure
{
public class Db4oProvider : IHttpModule
{
private const string DataBaseInstance = "db4o-database-instance";
private const string SessionKey = "db4o-session";
// #example: open database when the application starts
public void Init(HttpApplication context)
{
if (null==HttpContext.Current.Application[DataBaseInstance])
{
HttpContext.Current.Application[DataBaseInstance] = OpenDatabase();
}
RegisterSessionCreation(context);
}
private IEmbeddedObjectContainer OpenDatabase()
{
string relativePath = ConfigurationSettings.AppSettings["DatabaseFileName"];
string filePath = HttpContext.Current.Server.MapPath(relativePath);
return Db4oEmbedded.OpenFile(filePath);
}
// #end example
// #example: close the database when the application shuts down
public void Dispose()
{
IDisposable toDispose = HttpContext.Current.Application[DataBaseInstance] as IDisposable;
if (null != toDispose)
{
toDispose.Dispose();
}
}
// #end example
// #example: provide access to the database
public static IObjectContainer Database
{
get { return (IObjectContainer)HttpContext.Current.Items[SessionKey]; }
}
// #end example
// #example: A object container per request
private void RegisterSessionCreation(HttpApplication httpApplication)
{
httpApplication.BeginRequest += OpenSession;
httpApplication.EndRequest += CloseSession;
}
private void OpenSession(object sender, EventArgs e)
{
IEmbeddedObjectContainer container =
(IEmbeddedObjectContainer)HttpContext.Current.Application[DataBaseInstance];
IObjectContainer session = container.OpenSession();
HttpContext.Current.Items[SessionKey] = session;
}
private void CloseSession(object sender, EventArgs e)
{
if (HttpContext.Current.Items[SessionKey] != null)
{
IObjectContainer session = (IObjectContainer)HttpContext.Current.Items[SessionKey];
session.Dispose();
}
}
// #end example
}
}
As alternative you could use the Application_Start from the Global.apsx, which is called only once for sure.
You have another problem here.
When AppPools restart there can be an overlap when the old AppPool is finishing request and the new AppPool is servicing new requests.
During this time you will have two processes trying to access the same db4o file
To get around this you can use something like the hack below.
Note the use of Db4oFactory.OpenServer instead of Db4oEmbedded.OpenFile. This allows the use of transactions on a more fine grained basis.
public IObjectServer OpenServer()
{
Logger.Debug("Waiting to open db4o server.");
var attempts = 0;
do
{
try
{
return Db4oFactory.OpenServer(fileName, 0);
}
catch (DatabaseFileLockedException ex)
{
attempts++;
if (attempts > 10)
{
throw new Exception("Couldn't open db4o server. Giving up!", ex);
}
Logger.Warn("Couldn't open db4o server. Trying again in 5sec.");
Thread.Sleep(5.Seconds());
}
} while (true);
}
Hope this helps
Sounds like permission issues if it works on dev. Stick a notepad file in the same directory and try to open that with some bare bones file code. I bet you'll have the same issue.