Closed sagatowski closed 9 months ago
I think the issue with the zombie instances the problem might be that there are a lot of "catch all exceptions" statements in the code rather than specific catchs and only 1 global
try
{
// do stuff
}
finally
{
CleanUpAndExitApplication(...);
}
around the automation interface stuff. I don't know the code well enough right now for a proposal to fix this, but I'll look into it.
@stefanbesler I'd very much appreciate if you could look into it.
@stefanbesler Have you had any time to look into it?
@sagatowski yes, but it was really buggy on my testsystem. It worked sometimes, but often, the Automation Interface just “froze” and would not respond anymore.
Before any changes or after changes? What changes did you do? Have you tried with a Virtual Machine?
I did not actually try with TcUnit-Runner, but one of my own project. Instead of calling CreateInstance to create an EnvDTE.DTE instance, I used the following code snippet to check for existing DTEs
static DTE dte_;
static String progId_ = "TcXaeShell.DTE.15.0";
static IEnumerable<DTE> GetInstances(String progId)
{
IRunningObjectTable rot;
IEnumMoniker enumMoniker;
int retVal = GetRunningObjectTable(0, out rot);
if (retVal == 0)
{
rot.EnumRunning(out enumMoniker);
IntPtr fetched = IntPtr.Zero;
IMoniker[] moniker = new IMoniker[1];
while (enumMoniker.Next(1, moniker, fetched) == 0)
{
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
string displayName;
moniker[0].GetDisplayName(bindCtx, null, out displayName);
Console.WriteLine("Display Name: {0}", displayName);
if (displayName.StartsWith($"!{progId}"))
{
object obj;
rot.GetObject(moniker[0], out obj);
DTE dte = obj as DTE;
Console.WriteLine($"\tfound candidate for {progId}");
if (!dte.Solution.IsOpen)
yield return dte;
else
Console.WriteLine("\tSolution is open, skip candidate");
}
}
}
}
[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
Only if this method yielded 0 instances I was creating a new one. It worked okish on a physical pc when starting the program from hand. However, on all of my virtual machines the story was quite different.
If you are running a Jenkins node as a service you have to make sure to call dte_.UserControl = true;
to keep the DTE process open, otherwise the instance is closed even when not calling dte_.Quit - only calling UserControl = true
keeped the process open, I didn't find any other way. When attaching to the DTE the UserControl property can not be set to false . If you try there will be an exception about insufficent permissions. Attaching to a DTE with UserControl=true works just find, but 8/10 times the process justs hangs when calling the first method of such a DTE.
So my main problem was attaching to a DTE in a Jenkins / Virtual machine context. It would probably work with if was willing to run my Jenkins nodes not as a Windows service, but in a command prompt. However, this is not really an option for me.
@stefanbesler Thank you for your effort!
That is indeed weird. I might give this a try as well. Is it possible that you could do a fork of TcUnit-Runner, and commit your changes into it so I could elaborate on this a little further?
This project is archived. Replacement for it is in the works.
In the current implementation of the TcUnit-Runner, a new instance of visual studio is created when running the TwinCAT project. If the visual studio process is not killed correctly for some reason, this can lead to many zombie-instances of visual studio being running in the system. What should be done instead is to try to attach to an existing instance of visual studio (if one with the same version as the project was created with is existing).