dzrtfox
14 years agoContributor
COM Interop Memory Leak?
Hi, I am running into an issue while automating TestExecute using COM Interop. I have created a C# class to run script routines through COM. As each test runs, the memory usage displayed for TestExecute in the Windows Task Manager continues to grow until the entire test run gets brought to its knees.
I am not an expert at COM and may be doing something incorrectly. Here is how I have it set up:
When the class is first initialized, I execute this routine to open the project suite:
public static bool OpenTCProject()
{
const string projectSuitePath = "C:\\AtlasAutomation\\AtlasAutomationSuite\\AtlasAutomationSuite.pjs";
object TestExecuteObject = null;
ITestCompleteCOMManager TestExecuteManager = null;
ItcIntegration IntegrationObject = null;
try
{
TestExecuteObject = Marshal.GetActiveObject(TEProgID);
}
catch
{
try
{
TestExecuteObject = Activator.CreateInstance(Type.GetTypeFromProgID(TEProgID));
}
catch
{
}
}
if (TestExecuteObject == null)
{
return false;
}
try
{
TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
IntegrationObject = TestExecuteManager.Integration;
IntegrationObject.OpenProjectSuite(projectSuitePath);
if (!IntegrationObject.IsProjectSuiteOpened())
{
return false;
}
return true;
}
catch
{
return false;
}
finally
{
Marshal.ReleaseComObject(IntegrationObject);
Marshal.ReleaseComObject(TestExecuteManager);
Marshal.ReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
}
The idea here is to only open the project suite once, at the beginning of the test run. This works fine and opens the project suite as expected (and returns a boolean value indicating success/failure in opening the project). Now for each test I run this method:
public static bool RunTest(string projectName, string testName)
{
object TestExecuteObject = Marshal.GetActiveObject(TEProgID);
ITestCompleteCOMManager TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
ItcIntegration IntegrationObject = TestExecuteManager.Integration;
try
{
IntegrationObject.RunRoutine(projectName, testName, "Main");
while (IntegrationObject.IsRunning())
Thread.Sleep(2000);
if (IntegrationObject.GetLastResultDescription().Status == TC_LOG_STATUS.lsError)
{
return false;
}
else
{
return true;
}
}
catch
{
return false;
}
finally
{
Marshal.ReleaseComObject(IntegrationObject);
Marshal.ReleaseComObject(TestExecuteManager);
Marshal.ReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
}
Finally, I have a cleanup method that runs when the class is destructed:
public static void Cleanup()
{
object TestExecuteObject = Marshal.GetActiveObject(TEProgID);
ITestCompleteCOMManager TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
ItcIntegration IntegrationObject = TestExecuteManager.Integration;
TestExecuteManager.Quit();
Marshal.FinalReleaseComObject(IntegrationObject);
Marshal.FinalReleaseComObject(TestExecuteManager);
Marshal.FinalReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
Using this code, I can execute about 150 tests before the entire process crashes. I get an exception saying the thread pool has run out, and the test run ends. Memory usage by this point is over 1GB.
Am I doing something incorrectly? It feels like something isn't getting released properly... Thanks in advance!!!
I am not an expert at COM and may be doing something incorrectly. Here is how I have it set up:
When the class is first initialized, I execute this routine to open the project suite:
public static bool OpenTCProject()
{
const string projectSuitePath = "C:\\AtlasAutomation\\AtlasAutomationSuite\\AtlasAutomationSuite.pjs";
object TestExecuteObject = null;
ITestCompleteCOMManager TestExecuteManager = null;
ItcIntegration IntegrationObject = null;
try
{
TestExecuteObject = Marshal.GetActiveObject(TEProgID);
}
catch
{
try
{
TestExecuteObject = Activator.CreateInstance(Type.GetTypeFromProgID(TEProgID));
}
catch
{
}
}
if (TestExecuteObject == null)
{
return false;
}
try
{
TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
IntegrationObject = TestExecuteManager.Integration;
IntegrationObject.OpenProjectSuite(projectSuitePath);
if (!IntegrationObject.IsProjectSuiteOpened())
{
return false;
}
return true;
}
catch
{
return false;
}
finally
{
Marshal.ReleaseComObject(IntegrationObject);
Marshal.ReleaseComObject(TestExecuteManager);
Marshal.ReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
}
The idea here is to only open the project suite once, at the beginning of the test run. This works fine and opens the project suite as expected (and returns a boolean value indicating success/failure in opening the project). Now for each test I run this method:
public static bool RunTest(string projectName, string testName)
{
object TestExecuteObject = Marshal.GetActiveObject(TEProgID);
ITestCompleteCOMManager TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
ItcIntegration IntegrationObject = TestExecuteManager.Integration;
try
{
IntegrationObject.RunRoutine(projectName, testName, "Main");
while (IntegrationObject.IsRunning())
Thread.Sleep(2000);
if (IntegrationObject.GetLastResultDescription().Status == TC_LOG_STATUS.lsError)
{
return false;
}
else
{
return true;
}
}
catch
{
return false;
}
finally
{
Marshal.ReleaseComObject(IntegrationObject);
Marshal.ReleaseComObject(TestExecuteManager);
Marshal.ReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
}
Finally, I have a cleanup method that runs when the class is destructed:
public static void Cleanup()
{
object TestExecuteObject = Marshal.GetActiveObject(TEProgID);
ITestCompleteCOMManager TestExecuteManager = (ITestCompleteCOMManager)TestExecuteObject;
ItcIntegration IntegrationObject = TestExecuteManager.Integration;
TestExecuteManager.Quit();
Marshal.FinalReleaseComObject(IntegrationObject);
Marshal.FinalReleaseComObject(TestExecuteManager);
Marshal.FinalReleaseComObject(TestExecuteObject);
IntegrationObject = null;
TestExecuteManager = null;
TestExecuteObject = null;
}
Using this code, I can execute about 150 tests before the entire process crashes. I get an exception saying the thread pool has run out, and the test run ends. Memory usage by this point is over 1GB.
Am I doing something incorrectly? It feels like something isn't getting released properly... Thanks in advance!!!