Hello;
I am using Magick.Net to convert PDF files and export them to Tiff format. I have a series of unit tests to test this code. One test attempts to delete the Ghostscript dll (to force an exception condition and test error/exception handling), and other tests are intended to verify that the code successfully converts and exports to TIFF. I have a master test-driver method to ensure that the delete-and-handle-exception test runs first in the test sequence. Even so, when the sequence is run multiple times in close succession, the exception-handling test fails (with an access-violation exception) when it attempts to delete the Ghostscript dll. The only thing I have been able to conclude is that Magick.Net does not release the Ghostscript dll in a prompt and predictable manner and that the Ghostscript dll (gsdll32.dll – using 32 bit for backwards compatibility) still seems to be held by one of the “successful” unit tests at the end of the prior execution of the test sequence. This doesn’t seem to occur if the tests are run in debug mode, but is a problem if the test-series is run multiple times in close succession from Test Explorer and might be a problem if run from an integration serve.
I have attached code snippets – is there anything I can do differently, or add, to ensure the release of the Ghostscript dll to avoid the issue above?
Thanks for any assistance!
I am using Magick.Net to convert PDF files and export them to Tiff format. I have a series of unit tests to test this code. One test attempts to delete the Ghostscript dll (to force an exception condition and test error/exception handling), and other tests are intended to verify that the code successfully converts and exports to TIFF. I have a master test-driver method to ensure that the delete-and-handle-exception test runs first in the test sequence. Even so, when the sequence is run multiple times in close succession, the exception-handling test fails (with an access-violation exception) when it attempts to delete the Ghostscript dll. The only thing I have been able to conclude is that Magick.Net does not release the Ghostscript dll in a prompt and predictable manner and that the Ghostscript dll (gsdll32.dll – using 32 bit for backwards compatibility) still seems to be held by one of the “successful” unit tests at the end of the prior execution of the test sequence. This doesn’t seem to occur if the tests are run in debug mode, but is a problem if the test-series is run multiple times in close succession from Test Explorer and might be a problem if run from an integration serve.
I have attached code snippets – is there anything I can do differently, or add, to ensure the release of the Ghostscript dll to avoid the issue above?
Thanks for any assistance!
// unit-test class
public class UT_FormatConversion
{
private IFormatConversion fFormatConversion;
private IPlgRegELib fPlgRegELib;
private string fINIFile = @"C:\DevVS_CSharp\RegELib\Test_ConvertToTiff2\RegE.INI";
private int fTestCaseID;
[TestInitialize]
public void TestInitialize()
{
fPlgRegELib = new TPlgRegELib();
ConnectToDB();
}
[TestMethod]
public void TestDriver()
{
ImgConvPdfToTiffFail();
ImgConvPdfToTiffSuccess();
ImgConvPdfToTiffPlugin();
}
/// <summary>
/// Tests exception/error handling under various scenarios, including missing Ghostscript components
/// </summary>
public void ImgConvPdfToTiffFail()
{
string sourceDirectory = @"C:\DevVS_CSharp\RegELib\Test_ConvertToTiff2";
string currDir = Directory.GetCurrentDirectory();
string ghostScriptDLL = "gsdll32.dll";
string ghostScriptDLLsv = "gsdll32_sv.dll";
string ghostScriptExe = "gswin32c.exe";
string ghostScriptEXEsv = "gswin32c_sv.exe";
// Verifies the backup-up GhostScript files exist - if not found, manually copy the files into place
ghostScriptDLLsv = sourceDirectory + @"\" + ghostScriptDLLsv;
Assert.IsTrue(File.Exists(ghostScriptDLLsv));
ghostScriptEXEsv = sourceDirectory + @"\" + ghostScriptEXEsv;
Assert.IsTrue(File.Exists(ghostScriptEXEsv));
ghostScriptDLL = currDir + @"\" + ghostScriptDLL;
ghostScriptExe = currDir + @"\" + ghostScriptExe;
// set up the other files
string sourcePdfFileRoot = "NonexistentFile";
string sourcePdfFileExt = "pdf";
string fullSourcePDFFile = sourceDirectory + @"\" + sourcePdfFileRoot + "." + sourcePdfFileExt;
Assert.IsFalse(File.Exists(fullSourcePDFFile));
string destPath = @"C:\DevVS_CSharp\RegELib\Test_ConvertToTiff2";
string destinationExtension = "tiff";
string destPathFile = destPath + @"\" + sourcePdfFileRoot + "." + destinationExtension;
if (File.Exists(destPathFile))
{ File.Delete(destPathFile); }
Assert.IsFalse(File.Exists(destPathFile));
// Test for failure due to missing GhostScript files
// Test for missing GhostScript DLL
if (File.Exists(ghostScriptDLL))
File.Delete(ghostScriptDLL); // <<--THIS IS THE STATEMENT THAT FAILS BECAUSE THE DLL HAS NOT RELEASED FROM A PREVIOUS TEST CYCLE
Assert.IsFalse(File.Exists(ghostScriptDLL));
TFormatConversion tiffConverter = new TFormatConversion();
Assert.IsNotNull(tiffConverter);
try
{
Assert.IsFalse(tiffConverter.ConvertToTiff(fullSourcePDFFile, destPath));
Assert.AreNotEqual(tiffConverter.ErrorMsg, null);
string expectedErrorMsg = "File " + ghostScriptDLL + " could not be found - aborting";
Assert.AreEqual(tiffConverter.ErrorMsg, expectedErrorMsg);
}
catch (Exception e) { throw; }
finally
{
// Ref-establish the GS dll so it's present for later tests
File.Copy(ghostScriptDLLsv, ghostScriptDLL);
File.SetAttributes(ghostScriptDLL, FileAttributes.Normal);
Assert.IsTrue(File.Exists(ghostScriptDLL));
}
... (more code
} // end ImgConvPdfToTiffFail
} // end test class
//Format-conversion class
public class TFormatConversion : IFormatConversion
{
#region PrivateVariables
private string fErrorMsg;
#endregion
#region Properties
public string ErrorMsg
{
get { return fErrorMsg; }
}
#endregion
#region Public Methods
/// <summary>
/// Converts an image of any supported format to TIFF format
/// </summary>
/// <param name="aSourcePathFile">The full path and file name of the file to be read and converted</param>
/// <param name="aDestinationPathFile">The full destination path and file name for the converted file</param>
/// <returns>boolean - true for successful conversion, false if unsuccessful, ErrorMg contains error-detail in case return is false</returns>
public bool ConvertToTiff(string aSourcePathFile, string aDestinationPathFile)
{
string ghostScriptDLL = "gsdll32.dll";
string ghostScriptExe = "gswin32c.exe";
// if dealing with PDFs, MagickNet is very sensitive to the GhostScript directory. If that setting is in error,
// MagickNet may fail to find the file to be read and converted or throw other exceptions
string currDirectory = Directory.GetCurrentDirectory();
ghostScriptDLL = currDirectory + @"\" + ghostScriptDLL;
if (!File.Exists(ghostScriptDLL))
{
fErrorMsg = "File " + ghostScriptDLL + " could not be found - aborting";
return false;
}
ghostScriptExe = currDirectory + @"\" + ghostScriptExe;
if (!File.Exists(ghostScriptExe))
{
fErrorMsg = "File " + ghostScriptExe + " could not be found - aborting";
return false;
}
// Validate the destination path/file
if ((aDestinationPathFile.Substring(aDestinationPathFile.Length - 4).ToLower() != ".tif") &&
(aDestinationPathFile.Substring(aDestinationPathFile.Length - 5).ToLower() != ".tiff"))
{
fErrorMsg = "Destination filename must have an extension of .tif or .tiff";
return false;
}
MagickNET.SetGhostscriptDirectory(currDirectory);
// A combination of settings at different levels was required to get the desired results
// High-level settings here - image-level settings later
MagickReadSettings settings = new MagickReadSettings();
settings.Density = new Density(192, 192);
settings.BackgroundColor = new MagickColor(MagickColors.White);
settings.CompressionMethod = CompressionMethod.Group4;
string sourceFileRoot = Path.GetFileNameWithoutExtension(aSourcePathFile);
if (File.Exists(aDestinationPathFile))
{ File.Delete(aDestinationPathFile); }
try
{
using (MagickImageCollection images = new MagickImageCollection())
{
images.Read(aSourcePathFile, settings);
foreach(MagickImage image in images)
{
// Image-level settings - Quality affects file size and viewability, Alpha affects form
// background effects
image.Quality = 60;
image.Alpha(AlphaOption.Remove);
image.BitDepth(1);
image.CompressionMethod = CompressionMethod.Group4;
}
images.Write(aDestinationPathFile);
}
}
catch (Exception e)
{
fErrorMsg = e.Message;
return false;
}
if (!File.Exists(aDestinationPathFile))
{
fErrorMsg = "The file " + aDestinationPathFile + " could not be found after the conversion attempt!";
return false;
}
return true;
}
}