Inventor COM object not released when created with Activator.CreateInstance() - c#

My problem is this:
If Autodesk Inventor is not running, my app (console app) creates a new instance with Activator.CreateInstance(InventorType); and uses it as a COM object. When my app does not quit Inventor but leaves it open and the user later quits it by hand there is still a process inventor.exe running in TaskManager which can only be killed in TaskManager.
Curiously the problem only arises when these two things are combined. Whenever my app quits Inventor with InventorApp.Quit(); it is closed properly and there is no process left open.
If my app starts Inventor with Process.Start(..); or the user starts Inventor before starting the app and then my app grabs Inventor with Marshal.GetActiveObject(ProgId); there is no problem no matter if the app or the user quits Inventor.
If my app starts Inventor with Activator.CreateInstance(InventorType); then leaves Inventor open, the app is closed and then restarted, it grabs Inventor with Marshal.GetActiveObject(..); and then quits Inventor via InventorApp.Quit(); there is no problem.
So, the problem with the left open process only arises in this specific combination:
start Inventor via Activator.CreateInstance(InventorType);
the user quits Inventor by hand
The left open process is not in the Running Object Table anymore so it can't be handled as a COM object anymore and it has no visible UI, which means it can only be killed in TaskManager.
Using the 'bad combination' as described I even tried to call GC.WaitForPendingFinalizers(); GC.Collect(); several times (I know this is bad but I am just trying everything) in different combinations and before and/or after Marshal.ReleaseComObject(invApp); Marshal.FinalReleaseComObject(invApp);. I even tried a minimal app which literally does nothing else. See below for the code.
So, what is Activator.CreateInstance(InventorType); doing that is causing this? Is there any way to prevent this? Or is this a problem specific to Inventor?
Minimal app example:
Inventor.Application invApp = null;
string ProgId = "Inventor.Application";
try
{
invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
}
catch (Exception e1)
{
try
{
Type InventorType = Type.GetTypeFromProgID(ProgId);
invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
}
catch (Exception e2)
{
Console.WriteLine(e1);
Console.WriteLine(e2);
}
}
invApp = invApp as Inventor.Application;
invApp.Visible = true;
Console.Write("Quit Inventor? (y/n) ");
string quit = Console.ReadLine();
if (quit == "y")
{
invApp.Quit();
}
// desperately trying to release the COM object ...
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
if (invApp != null)
{
Marshal.ReleaseComObject(invApp);
Marshal.FinalReleaseComObject(invApp);
}
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
if (invApp != null)
{
Marshal.ReleaseComObject(invApp);
Marshal.FinalReleaseComObject(invApp);
}
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
invApp = null;

This is not specific for Inventor but for any COM object (Excel for example).
Usually I don't use this COM communication for production, because there are many vulnerabilities and some performance issues. I recommend you to use another workflow when possible.
But to your question. You can't release this COM object as you try. I recommend you to wrap Inventor.Application to some IDisposable object and quit them in dispose method, when you create your own instance.
static void Main(string[] args)
{
InventorTest();
//Waiting for dispose message
//Console.ReadKey();
}
private static void InventorTest()
{
using (var invProvider = new InventorDisposableProvider())
{
var invApp = invProvider.InventorApp;
invApp.Visible = true;
Console.Write("Quit Inventor? (y/n) ");
string quit = Console.ReadLine();
if (quit == "y")
{
invApp.Quit();
}
}
}
class InventorDisposableProvider : IDisposable
{
private Application invApp;
private bool startedByMe = false;
/// <summary>
/// Gets running or start new instance of Inventor
/// </summary>
public Application InventorApp
{
get
{
if (invApp == null) GetInventorApp();
return invApp;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (startedByMe && invApp != null)
{
invApp .Quit();
Console.WriteLine("Quit");
}
}
private void GetInventorApp()
{
string ProgId = "Inventor.Application";
try
{
invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
startedByMe = false;
}
catch (Exception e1)
{
try
{
Type InventorType = Type.GetTypeFromProgID(ProgId);
invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
startedByMe = true;
}
catch (Exception e2)
{
Console.WriteLine(e1);
Console.WriteLine(e2);
}
}
}
}
I don't know if this is the best solution but it is a good start point.
I found only one issue. When user quit the console application by cross. In this case you can see this article how to solve this case.
capture-console-exit-c-sharp

Related

Is there a way to force an AssemblyLoadContext to unload?

I am working on a big ASP.Net 5 web application, and I would like to implement a self-update feature. Since the program can be deployed on many different platform and different ways (e.g. a Windows Service, a systemd service on linux, Docker container, etc...), the only viable way of implementing a self update mechanism is to write a host process that can load and unload the main program DLL and its dependencies. Unloading is important because the host program (updater) must be able to overwrite the server's DLL files while it's running.
Here's what I've done so far: I have changed the output type of the main web application to Library, and I've made a small loader program that loads this DLL and its dependencies into an HostAssemblyLoadContext, then invokes the original Main() method. The server application is programmed to shut down gracefully a few seconds after it starts up, so the CreateHostBuilder(args).Build().Run() and the Main() method can return. After this, I try to call HostAssemblyLoadContext.Unload(), but for some reason, the load context refuses to actually unload.
Here's my HostAssemblyLoadContext implementation:
public class HostAssemblyLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public HostAssemblyLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName name)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(name);
if (assemblyPath != null)
return LoadFromAssemblyPath(assemblyPath);
string filePath = $"{name.FullName.Split(',')[0]}.dll";
if(File.Exists(filePath))
return Assembly.LoadFrom(filePath);
return null;
}
}
My loader code:
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExecuteMainMethod(string[] args, string assemblyFileName, out WeakReference contextReference, out HostAssemblyLoadContext loadContext)
{
loadContext = new HostAssemblyLoadContext(new DirectoryInfo(".").FullName);
contextReference = new WeakReference(loadContext);
var assembly = loadContext.LoadFromAssemblyPath(assemblyFileName);
var mainMethod = FindMainMethod(assembly)!;
try
{
mainMethod.Invoke(null, new object?[] {args});
}
catch
{
// ignore
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Unload(WeakReference weakReference, ref HostAssemblyLoadContext loadContext)
{
loadContext.Unload();
loadContext = null;
for (int i = 0; weakReference.IsAlive && i < 100; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"is alive: {weakReference.IsAlive}");
}
public static void Main(string[] args)
{
ExecuteMainMethod(args, new FileInfo("FirestormSW.SmartGrade.dll").FullName, out var weakReference, out var loadContext);
Unload(weakReference, ref loadContext);
Console.Out.WriteLine("Press ENTER to exit");
Console.ReadLine();
}
And the main method of the actual ASP server:
public static void Main(string[] args)
{
Console.Out.WriteLine("Starting Server...");
CreateHostBuilder(args).Build().Run();
Console.Out.WriteLine("Server stopped.");
}
Note that if I replace the contents of this Main method with a simple Thread.Sleep(1000), the context does unload successfully.
When I run this program, and wait for the server to shut itself down, this is what I see on the console:
Starting Server...
Shutting down...
is alive: True
This means that the server's main method has returned, but something is still keeping the load context alive. I have looked at the thread count (Process.GetCurrentProcess().Threads.Count) before and after the server is started/stopped, and the number jumps from 8 up to 27. This makes me assume that the context is being kept alive by some threads that are created by the ASP.Net application, but I'm not sure. And if that's the case, I don't know how to find out which threads are responsible, and even if I could, I'm not sure if it's possible to abort them.

Mutex kills application

This is my code:
protected override async void OnStartup(StartupEventArgs e)
{
// Used to check if we can create a new mutex
bool newMutexCreated = false;
try
{
// Create a new mutex object with a unique name
mutex = new Mutex(false, MutexName, out newMutexCreated);
}
catch (Exception ex)
when (ex is UnauthorizedAccessException ||
ex is IOException ||
ex is WaitHandleCannotBeOpenedException ||
ex is ArgumentException)
{
Logger.Error("Error while launching application. Failed to check for other instances.", ex);
Shutdown((int)ExitCode.ApplicationAlreadyRunning);
}
// When the mutex is created for the first time
// we run the program since it is the first instance.
if (newMutexCreated)
{
await ContinueStartup(e);
return;
}
else
{
// Otherwise we get the first instance with that process name,
Process[] currentProcesses = Process.GetProcessesByName(AssemblyName);
IntPtr mainWindowHandle = currentProcesses[0].MainWindowHandle;
if (mainWindowHandle != IntPtr.Zero)
{
// maximize it, if it was minimized, and set it to foreground.
Logger.Info("Another instance of the application is already running.");
ShowWindow(mainWindowHandle, WindowShowNormal);
SetForegroundWindow(mainWindowHandle);
}
// Then shutdown this instance.
Logger.Info("Shutting down.");
Shutdown((int)ConsoleModeExitCode.ApplicationAlreadyRunning);
}
}
protected override void OnExit(ExitEventArgs e)
{
Logger.Info("Exiting application.");
// Close mutex.
mutex.Dispose();
base.OnExit(e);
}
What happens here is that my application should start once. While it is running, every attempt to start a new instance should bring the first instance to the front.
But what actually happens is: after 2-10 launch-attempts the first instance's GUI is killed, the process is still running and blocking the Mutex and can only be killed in the TaskManager. If I try to debug this behaviour and run the application in VisualStudio, it just never happens. Trying to open the application 50 times never kills it, so I can't follow the events that seem to occur.
Is it normal behaviour of the GarbageCollector? It kills the first instance in case it is hanging?
Or am I missing something?
Okay, as #Luaan mentioned the problem was not the Mutex.
I fixed my code with this solution:
https://stackoverflow.com/a/9059657/3319147
ShowWindowAsync and a slightly different handling of the IntPtr-value of the handle seems to make this way more stable. Couldn't crash it since. For me this is enough stability :)

C# Timeout with Solidworks VBA Macro

I have a few functions in a Solidworks Addin which call on a VBA macro (Via the runMacro2 method) a co-worker has been working on for the last few weeks. In his code he calls a Solidworks function which, under certain, unknown conditions, hangs for a long period of time. How long seems to depend upon the size and quantity of bodies in the part. Considering at least one of the functions we want to run this from i automatic, this just wont do.
I have tried using the Thread.Join(int) method (shown below) but it doesnt work. I also tried modifying the code from this answer Close a MessageBox after several seconds with the same results. Is there anything I can do either in C# or VBA to handle a timeout for this without re-writing his entire macro?
public void runBB()
{
Stopwatch testStop = new Stopwatch();
Thread workerThread = new Thread(bbRun);
testStop.Start();
workerThread.Start();
if (!workerThread.Join(50))
{
workerThread.Abort();
testStop.Stop();
MessageBox.Show("Unable to generate Bounding Box after " + testStop.ElapsedMilliseconds/1000 + " seconds. Please enter data manually.", "Solidworks Derped Error.");
}
return;
}//Still uses Macro (2-5-16)
public static void bbRun()
{
iSwApp.RunMacro2(macroPath + "BOUNDING_BOX.swp", "test11", "main", 0, out runMacroError);
return;
}
I was getting this same exact issue with SOLIDWORKS hanging on an open of a file. Almost all reference on SO was that you should never do this, but in this scenario, you either have to close it or wait forever. In C# I created a callWithTimeout method:
private void callWithTimeout(Action action, int timeoutMilliseconds, String errorText) {
Thread threadToKill = null;
Action wrappedAction = () =>
{
threadToKill = Thread.CurrentThread;
action();
};
IAsyncResult result = wrappedAction.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) {
wrappedAction.EndInvoke(result);
} else {
threadToKill.Abort();
throw new TimeoutException(errorText);
}
}
Then the code that was hanging put in a block as such:
bool timedOut = false;
try {
callWithTimeout(delegate() {
// code that hangs here
}, 60000, "Operation timed out. SOLIDWORKS could not open the file. This file will be processed later.");
} catch (TimeoutException){
timedOut = true;
} finally {
if(timedOut) {
Process[] prs = Process.GetProcesses();
foreach (Process p in prs) {
if (p?.ProcessName.Equals("SLDWORKS") ?? false)
p?.Kill();
}
}
}

How to kill only processes started by my application

I am using Selenium WebDriver in an application and I have code to kill the webdrivers and browser instances. However, I am thinking that if the user had any IE browsers open before running the application that this code will kill not only the IE processes spawned by my application but also the IE instances that user had open prior to running the application.
Is there a way to track the processes started by my application so I can filter this method to kill only IE processes spawned by my application, or determine that IE driver and browser instance was spawned by my application, or perhaps both?
public void KillAllBrowsersAndWebDrivers()
{
var webDrivers = Process.GetProcessesByName("IEDriverServer").Select(p => p.Id);
var browsers = Process.GetProcessesByName("iexplore").Select(p => p.Id);
var processIds = webDrivers.Concat(browsers);
// do some stuff with PID, if you want to kill them, do the following
foreach (var pid in processIds)
{
try
{
Process.GetProcessById(pid).Kill();
Logger.Log(Loglevel.Debug, "Kill Process:{0}", pid);
}
catch (Exception)
{
Logger.Log(Loglevel.Error, "Error killing process: {0}", pid);
}
}
}
All you would have to do is keep a list of all the processes you've created.
this is a very simple process manager. This code is error prone, and there is no exception handling
private static List<Process> processes = new List<Process>();
static void Main(string[] args)
{
int PID = StoreProcess (yourProcess);
KillProcess(PID);
}
/// <summary>
/// Stores the process in a list
/// </summary>
/// <returns>The PID</returns>
/// <param name="prc">The process to be stored</param>
public static int StoreProcess(Process prc)
{
int PID = prc.Id; // Get the process PID and store it in an int called PID
processes.Add (prc); // Add this to our list of processes to be kept track of
return PID; // Return the PID so that the process can be killed/changed at a later time
}
/// <summary>
/// Kills a process
/// </summary>
/// <param name="PID">The PID of the process to be killed.</param>
public static void KillProcess(int PID)
{
// Search through the countless processes we have and try and find our process
for (int i = 0; i <= processes.Count; i++) {
if (processes [i] == null)
{
continue; // This segment of code prevents NullPointerExceptions by checking if the process is null before doing anything with it
}
if (processes [i].Id == PID) { // Is this our process?
processes [i].Kill (); // It is! Lets kill it
while (!processes [i].HasExited) { } // Wait until the process exits
processes [i] = null; // Mark this process to be skipped the next time around
return;
}
}
// Couldn't find our process!!!
throw new Exception ("Process not found!");
}
Advantages:
You can keep track of all the processes you've initialized, and terminate them one by one at any time
Drawbacks:
I don't believe there is any
Another possible solution is to get a list of the processes running BEFORE spawning any new processes. Then just kill the ones that are not in the list of previously running processes.
public void KillOnlyProcessesSpawnedBySelenium()
{
// get a list of the internet explorer processes running before spawning new processes
var pidsBefore = Process.GetProcessesByName("iexplore").Select(p => p.Id).ToList();
var driver = new Driver(Settings);
var driver1 = driver.InitiateDriver(); // this method creates new InternetExplorerDriver
var driver2 = driver.InitiateDriver();
var driver3 = driver.InitiateDriver();
driver1.Navigate().GoToUrl("http://google.com");
driver2.Navigate().GoToUrl("http://yahoo.com");
driver3.Navigate().GoToUrl("http://bing.com");
var pidsAfter = Process.GetProcessesByName("iexplore").Select(p => p.Id);
var newInternetExplorerPids = pidsAfter.Except(pidsBefore);
// do some stuff with PID, if you want to kill them, do the following
foreach (var pid in newInternetExplorerPids)
{
Debug.WriteLine("Killing pid: {0}", pid);
Process.GetProcessById(pid).Kill();
}
Assert.IsTrue(pidsBefore.Count > 0);
// determine if each process before the drivers spawned are running
foreach (var running in pidsBefore.Select(pid => Process.GetProcessById(pid).IsRunning()))
{
Assert.IsTrue(running);
}
}
Here is an extension method to use to determine if a process is still running or not...
public static bool IsRunning(this Process process)
{
if (process == null)
throw new ArgumentNullException("process");
try
{
Process.GetProcessById(process.Id);
}
catch (ArgumentException)
{
return false;
}
return true;
}

How to make my app singleton application? [duplicate]

This question already has answers here:
What is the correct way to create a single-instance WPF application?
(39 answers)
Closed 6 years ago.
I have a application but currently it is not a singleton application.
I like to make it singleton application so that its another instance does not exit at the run time .
If this can be done please reply with some sample codes .
I think the following codes will be helpful for you.
Here is the related link:
http://geekswithblogs.net/chrisfalter/archive/2008/06/06/how-to-create-a-windows-form-singleton.aspx
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
/*====================================================
*
* Add codes here to set the Winform as Singleton
*
* ==================================================*/
bool mutexIsAvailable = false;
Mutex mutex = null;
try
{
mutex = new Mutex(true, "SampleOfSingletonWinForm.Singleton");
mutexIsAvailable = mutex.WaitOne(1, false); // Wait only 1 ms
}
catch (AbandonedMutexException)
{
// don't worry about the abandonment;
// the mutex only guards app instantiation
mutexIsAvailable = true;
}
if (mutexIsAvailable)
{
try
{
Application.Run(new SampleOfSingletonWinForm());
}
finally
{
mutex.ReleaseMutex();
}
}
//Application.Run(new SampleOfSingletonWinForm());
}
}
Here are some good sample applications. Below is one possible way.
public static Process RunningInstance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName (current.ProcessName);
//Loop through the running processes in with the same name
foreach (Process process in processes)
{
//Ignore the current process
if (process.Id != current.Id)
{
//Make sure that the process is running from the exe file.
if (Assembly.GetExecutingAssembly().Location.
Replace("/", "\\") == current.MainModule.FileName)
{
//Return the other process instance.
return process;
}
}
}
//No other instance was found, return null.
return null;
}
if (MainForm.RunningInstance() != null)
{
MessageBox.Show("Duplicate Instance");
//TODO:
//Your application logic for duplicate
//instances would go here.
}
Many other possible ways. See the examples for alternatives.
First one.
Second One.
Third One.
The approach I know of is the following. The program must attempt to open a named mutex. If that mutex existed, then exit, otherwise, create the mutex. But this seems to contradict your condition that "its another instance does not exit at the run time". Anyway, maybe this too was helpful

Categories