c# adobe acrobat SDK: file is still locked after SDK quit - c#

I'm working with C# and adobe acrobat SDK.
When the program throws an error due to the pdf already being compressed I want to move the pdf.
However, C# complains that the file is being used by another process and I know it has to do with the SDK and not another program.
After some debugging I found out that compressPDFOperation.Execute is the culprit.
How can I close it so that I can move the file?
try {
// Initial setup, create credentials instance.
Console.WriteLine(".json: " + Directory.GetCurrentDirectory() + "/pdftools-api-credentials.json");
Credentials credentials = Credentials.ServiceAccountCredentialsBuilder()
.FromFile(Directory.GetCurrentDirectory() + "/pdftools-api-credentials.json")
.Build();
// Create an ExecutionContext using credentials and create a new operation instance.
ExecutionContext executionContext = ExecutionContext.Create(credentials);
CompressPDFOperation compressPDFOperation = CompressPDFOperation.CreateNew();
// Set operation input from a source file.
FileRef sourceFileRef = FileRef.CreateFromLocalFile(directory + #"\" + pdfname);
compressPDFOperation.SetInput(sourceFileRef);
// Execute the operation.
FileRef result = compressPDFOperation.Execute(executionContext);
// Save the result to the specified location.
//if pdf is part of a group, the group directory name will be stored in fileGroupDirectory
string fileGroupDirectory = directory.Replace(sourceDir, "");
result.SaveAs(finishedDir + fileGroupDirectory + pdfname);
}
catch (ServiceApiException ex)
{
Console.WriteLine("Exception encountered while executing operation", ex.Message);
if (ex.Message.Contains("The input file is already compressed"))
{
File.Move(file, finishedDir + fileGroupDirectory + fileName);
}
}

I've found a solution , it's not best practice but I don't know an other way to do it.
I've declared all the variables used to execute the compression (sourceFileRef, compressPdfOperation, ...) before the try catch statement and after result.SaveAs(...) I set those variables to null and run the garbage collection.
compressPDFOperation = null;
result = null;
sourceFileRef = null;
executionContext = null;
credentials = null;
GC.Collect();

Related

C# Windows Service Not Compressing Folder Correctly

Im currently building a Windows service that will be used to create backups of logs. Currently, the logs are stored at the path E:\Logs and intent is to copy the contents, timestamp their new folder and compress it. After this, you should have E:\Logs and E:\Logs_[Timestamp].zip. The zip will be moved to C:\Backups\ for later processing. Currently, I am using the following to try and zip the log folder:
var logDirectory = "E://Logs";
var timeStamp = DateTime.Now.ToString("yyyyMMddHHmm");
var zippedFolder = logDirectory + "_" + timeStamp + ".zip";
System.IO.Compression.ZipFile.CreateFromDirectory(logDirectory, zippedFolder);
While this appears to create a zip folder, I get the error Windows cannot open the folder. The Compressed (zipped) Folder E:\Logs_201805161035.zip is invalid.
To address any troubleshooting issues, the service is running with an AD account that has a sufficient permission level to perform administrative tasks. Another thing to consider is that the service kicks off when its FileSystemWatcher detects a new zip folder in the path C:\Aggregate. Since there are many zip folders that are added to C:\Aggregate at once, the FileSystemWatcher creates a new Task for each zip found. You can see how this works in the following:
private void FileFoundInDrops(object sender, FileSystemEventArgs e)
{
var aggregatePath = new DirectoryInfo("C://Aggregate");
if (e.FullPath.Contains(".zip"))
{
Task task = Task.Factory.StartNew(() =>
{
try
{
var logDirectory = "E://Logs";
var timeStamp = DateTime.Now.ToString("yyyyMMddHHmm");
var zippedFolder = logDirectory + "_" + timeStamp + ".zip";
ZipFile.CreateFromDirectory(logDirectory, zippedFolder);
}
catch (Exception ex)
{
Log.WriteLine(System.DateTime.Now.ToString() + " - ERROR: " + ex);
}
});
task.Dispose();
}
}
How can I get around the error I am receiving? Any help would be appreciated!

System.IO.File.Move error - Could not find a part of the path

I have a sync software, which loads CSV files from "Incoming" folder, processes them and then moves them to the "Archive" folder.
Today, I saw the following error with this sync software:
[23/06/2014 00:06:04 AM] : Failed to move file from
D:\IBI_ORDER_IMPORTER_FTP_SERVER\Template3\Fifty &
Dean\Incoming\5A040K___d6f1ca45937b4ceb98d29d0db4601bf4.csv to
D:\IBI_ORDER_IMPORTER_FTP_SERVER\Template3\Fifty &
Dean\Archive\5A040K___d6f1ca45937b4ceb98d29d0db4601bf4.csv - Could not
find a part of the path.
Here's a snippet taken out of the sync software, where the file is processed and moved:
public static void ProcessSingleUserFile(Int32 TemplateId, String ImportedBy, String FilePath)
{
// Always Rename File To Avoid Conflict
string FileName = Path.GetFileNameWithoutExtension(FilePath);
String NewFilePath = FilePath.Replace(FileName, Utils.RandomString() + "___" + FileName);
File.Move(FilePath, NewFilePath);
FilePath = NewFilePath;
// Log
SyncUtils.ConsoleLog(String.Format("Processing [ {0} as {1} ] By [ {2} ] On Template [ #{3} ]",
FileName + ".csv",
Path.GetFileName(FilePath),
ImportedBy,
TemplateId));
// Init
List<OrderDraft> myOrderDrafts = new List<OrderDraft>();
// Parsed Based On Template Id
if (TemplateId == Settings.Default.Multi_Order_Template_Id)
{
// Try Parse File
myOrderDrafts = Utils.ParseMultiImportFile(TemplateId, ImportedBy, FilePath, true);
}
else
{
// Try Parse File
myOrderDrafts.Add(Utils.ParseImportFile(TemplateId, ImportedBy, FilePath, true));
}
// Process Orders
foreach (OrderDraft myOrderDraft in myOrderDrafts)
{
/* code snipped */
}
// Archive File
File.Move(FilePath, FilePath.Replace("Incoming", "Archive"));
}
Any idea what this error means? and how to circumvent it?
I wrote a cut down version of the above to test this in a controlled environment and I am not getting the error with this code:
static void Main(string[] args)
{
try
{
string baseDir = #"C:\Users\Administrator\Desktop\FTP_SERVER\Template3\Fifty & Dean\Incoming\";
string[] filePaths = Directory.GetFiles(baseDir, "*.csv");
foreach (string filePath in filePaths)
{
// do some work here ...
// move file
string newFilePath = filePath.Replace("Incoming", "Archive");
File.Move(filePath, newFilePath);
Console.WriteLine("File successfully moved");
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
Console.ReadKey();
}
You need to include the checks to make sure that the paths exist at runtime and check the output, something very simple like:
if(!Directory.Exists(Path.GetDirectoryName(filePath)))
{
Console.WriteLine("filePath does not exist: " + filePath);
}
if(!Directory.Exists(Path.GetDirectoryName(newFilePath)))
{
Console.WriteLine("newFilePath does not exist: " + newFilePath);
}
File.Move(filePath, newFilePath);
The reason I am suggesting this method is due to a possibility that the paths momentarily become available or not under the multi-tasking OSs depending on a multitude of factors: network connectivity, permissions (pushed down by GPO at any time), firewall rules, AV exclusions getting blown away etc. Even running low on CPU or RAM may create issues. In short, you never know what exactly occurred when your code was running if you are only checking the paths availability after the fact.
Or if your issue is intermittent, you can try and catch the error and write information to some sort of a log similarly to below:
try
{
File.Move(filePath, newFilePath);
}
catch(Exception ex)
{
if(!Directory.Exists(Path.GetDirectoryName(filePath)))
{
Console.WriteLine("filePath does not exist: " + filePath);
}
if(!Directory.Exists(Path.GetDirectoryName(newFilePath)))
{
Console.WriteLine("newFilePath does not exist: " + newFilePath);
}
}
"Could not find a part of the path" exception could also thrown from File.Move if argument used was longer than MAX_PATH (260) in .NET Framework.
So I prepend the path I used with long path syntax before passing to File.Move and it worked.
// Prepend long file path support
if( !packageFile.StartsWith( #"\\?\" ) )
packageFile = #"\\?\" + packageFile;
See:
How to deal with files with a name longer than 259 characters?
I have this
Could not find a part of the path
happened to me when I
File.Move(mapfile_path , Path.Combine(newPath, mapFile));
. After some testing, I find out our server administrator has blocked any user application from writing to that directory in that [newPath]!
So, right click on that directory to observe the rights matrix on Security tab to see anything that would block you.
Another cause of DirectoryNotFoundException "could not find a part of the path" thrown by File.Move can be spaces at the end of the directory name. Consider the following code:
string destinationPath = "C:\\Folder1 "; //note the space on the end
string destinationFileNameAndPath = Path.Combine(destinationPath, "file.txt");
if (!Directory.Exists(destinationPath))
Directory.CreateDirectory(destinationPath);
File.Move(sourceFileNameAndPath, destinationFileNameAndPath);
You may expect this code to succeed, since it creates the destination directory if it doesn't already exist, but Directory.CreateDirectory seems to trim the extra space on the end of the directory name, whereas File.Move does not and gives the above exception.
In this circumstance, you can workaround this by trimming the extra space yourself, e.g. (depending on how you load your path variable, the below is obviously overkill for a hard-coded string)
string destinationPath = "C:\\Folder1 ".Trim();

IO Exception - File In Use By Another Process

C# / .NET 3.5, WindowsForms.
I have this Windows form that displays an image from a file, and whenever user saves the record this code is executed:
string oldLoc = itemsBO.ImageLoc;
if (oldLoc != SystemSettings.NoImageLocation)
{
if (File.Exists(oldLoc))
{
try { File.Delete(oldLoc); }
catch (IOException ex)
{
MessageBox.Show("1 - " + ex.GetType().ToString() + " " + ex.Message);
}
}
}
string saveLoc = itemsBO.ImageSearchLoc + ".jpg";
if (File.Exists(saveLoc))
{
try { File.Delete(saveLoc); }
catch (IOException ex)
{
MessageBox.Show("2 - " + ex.GetType().ToString() + " " + ex.Message);
}
}
try
{
if (pictureBox2.Image != null)
pictureBox2.Image.Save(saveLoc, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch (IOException ex)
{
MessageBox.Show("3 - " + ex.GetType().ToString() + " " + ex.Message);
}
Disregard the poor MessageBox messages, but it errors out in each Catch statement. It can't delete the "existing" Image because it says it's in use by another process. Can't save because a file exists in that same path because it's not deleting.
This is the code that sets the Image when they try to add a new picture;
Image clipImage = Clipboard.GetImage();
if (tabControl2.SelectedTab == tabPage5)
{
pictureBox1.Image = clipImage;
itemsBO.IsDirtyImage = true;
}
else if (tabControl2.SelectedTab == tabPage6)
{
pictureBox2.Image = clipImage;
itemsBO.IsDirtyImage2 = true;
}
Then when the form loads up an existing record with an image, this is the code used to fetch/display it:
byte[] bits = File.ReadAllBytes(imgfil);
msImage = new MemoryStream(bits, 0, bits.Length);
if (tabControl2.SelectedTab == tabPage5)
pictureBox1.Image = Image.FromStream(msImage);
else if (tabControl2.SelectedTab == tabPage6)
pictureBox2.Image = Image.FromStream(msImage);
imgfil being a path to the image, of course.
Absolutely no idea where to begin...
I have this Windows form that displays an image from a file, and whenever user saves the record
If you're still displaying the image when they save the file, the application will still be accessing the file if I'm not mistaken. Try disposing of the file first, probably by setting the picture box's (or whatever you're using to display the image) image to null, or load a blank picture before you perform the operation.
If it says file in use by another process, well then it must be in use by another process :)
Have you tried monitoring the file lock using Process Explorer.
Once you have identified what's holding your file, close that file handle using Process Explorer and then try to run your code.
This might help-
How to find out what processes have folder or file locked?
So I had inherited this application from another user, turns out the pictureBoxes were having their Image set in another chunk of code independent of that third block of code in the original post. It was because of this that the IOException was happening :(

Handling XSLT file names c#

Wondering how to best deal with a problem I am having with xsltransform. Long story short, everything works in my test environment, but it crashes when I run it on the server due to the filenames it tries to deal with, which are output from another program, over which I have no control.
For example. "4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml" a simple # would solve this, but it's actually running on a service, and this service gets all files of that type in the directory and processes them one by one.
string[] filepaths = Directory.GetFiles(path, Filetype);
I keep the file name variable in:
FileInfo f = new FileInfo(filepaths[i]);
But the method I use for the transform:
myXslTransform = new XslCompiledTransform();
myXslTransform.Transform(filename,OutputFileName);
Only accepts (String, String) and thus when it sees "4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml" it has a heart attack and cuts it off.
I was thinking save the original name, rename, remove whitespace, transform, and rename back. But I think there is a smarter way to handle it out there, just not sure where to look. Is there a way of telling C# to handle a variable as a literal? Or a different transform method that accepts these weird filenames with very bad naming conventions?
Any insight that helps would be great!
The error & exception message I recieve from the Eventvwr is
Cannot Translate
\\9g031\Export\4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml
OutputName = \\9g031\Export\done\4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml
XSL LOC = C:\CXS.xsl
System.IO.IOException: The specified path is invalid.
private void PreformTranslation(FileInfo FileName, String OutputFileName , String result)
{
try
{
XslCompiledTransform myXslTransform;
myXslTransform = new XslCompiledTransform();
myXslTransform.Load(XSLname);
EventLog.WriteEntry(FileName.ToString(), OutputFileName);
myXslTransform.Transform(FileName.Name,OutputFileName);
EventLog.WriteEntry("TranslationComplete");
if (File.Exists(path + result))
{
MoveVideoFiles(path + result, outputPath + result);
}
// Rename(OutputFileName, FileName, Out);
}
catch (Exception e)
{
EventLog.WriteEntry("Cannot Translate " + FileName + " OutputName = " + OutputFileName + " \r\n"+
"XSL LOC = " + XSLname + "\r\n" + e);
}
}
The default directory when running a service is something like "windows/system32" and this isn't the directory of the executable.
This is probably the reason the XML file isn't found.

IManExt ImportCmd trouble

I have been writing a small application using C# to copy a document into an individuals 'My Documents' folder on our DMS server.
I've beased the code around the listing provided in the 'WorkSite SDK 8: Utilize the IMANEXT2Lib.IManRefileCmd to File New Document Folders' blog.
Using this code in a WinForm application I have no problems copying the file from the source folder into the users DMS 'My Documents' folder.
However if I use the code in a command line application/.dll or any other type of application (other than WinForm) during the copy process I receive the error messages;
1.
Error occurred when try to log the event!
IManExt: Error occurred when try to log the event!
Access is denied.
2.
The document was imported to the database, but could not be added to
the folder.
IManExt: The document was imported to the database, but could not be
added to the folder.
IManExt.LogRuleEventsCmd.1: Error occurred when try to log the event!
IManExt.LogRuleEventsCmd.1: Access is denied.
Error occurred when try to log the event!
-%-
Does anyone know why I'd receiving the 'Access Denied' error messages when using a non-WinForms application to copy documents?
What would I need to do to get around this issue?
Any help would be amazing!
Code in place:
public void moveToDMS(String servName, String dBName, String foldName)
{
const string SERVERNAME = servName; //Server name
const string DATABASENAME = dBName; //Database name
const string FOLDERNAME = foldName; //Matter alias of workspace
IManDMS dms = new ManDMSClass();
IManSession sess = dms.Sessions.Add(SERVERNAME);
sess.TrustedLogin();
//Get destination database.
IManDatabase db = sess.Databases.ItemByName(DATABASENAME);
//Get destination folder by folder and owner name.
IManFolderSearchParameters fparms = dms.CreateFolderSearchParameters();
fparms.Add(imFolderAttributeID.imFolderOwner, sess.UserID);
fparms.Add(imFolderAttributeID.imFolderName, FOLDERNAME);
//Build a database list in which to search.
ManStrings dblist = new ManStringsClass();
dblist.Add(db.Name);
IManFolders results = sess.WorkArea.SearchFolders(dblist, fparms);
if (results.Empty == true)
{
//No results returned based on the search criteria.
Console.WriteLine("NO RESULTS FOUND!");
}
IManDocumentFolder fldr = null;
if (results.Empty == false)
{
//Assuming there is only one workspace returned from the results.
fldr = (IManDocumentFolder)results.ItemByIndex(1);
}
if (fldr != null)
{
// Import file path
string docPath = #"C:\Temp\";
string docName = "MyWord.doc";
// Create an instance of the ContextItems Collection Object.
ContextItems context = new ContextItemsClass();
// Invoke ImportCmd to import a new document to WorkSite database.
ImportCmd impCmd = new ImportCmdClass();
// The WorkSite object you pass in can be a database, session, or folder.
// Depends on in where you want the imported doc to be stored.
context.Add("IManDestinationObject", fldr); //The destination folder.
// Filename set here is used for easy example, a string variable is normally used here
context.Add("IManExt.Import.FileName", docPath + docName);
// Document Author
context.Add("IManExt.Import.DocAuthor", sess.UserID); //Example of a application type.
// Document Class
context.Add("IManExt.Import.DocClass", "BLANK"); //Example of a document class.
//context.Add("IManExt.Import.DocClass", "DOC"); //Example of a document class.
// Document Description (optional)
context.Add("IManExt.Import.DocDescription", docName); //Using file path as example of a description.
// Skip UI
context.Add("IManExt.NewProfile.ProfileNoUI", true);
impCmd.Initialize(context);
impCmd.Update();
if (impCmd.Status == (int)CommandStatus.nrActiveCommand)
{
impCmd.Execute();
bool brefresh = (bool)context.Item("IManExt.Refresh");
if (brefresh == true)
{
//Succeeded in importing a document to WorkSite
IManDocument doc = (IManDocument)context.Item("ImportedDocument");
//Succeeded in filing the new folder under the folder.
Console.WriteLine("New document number, " + doc.Number + ", is successfully filed to " + fldr.Name + " folder.");
}
}
}
}
Just in case this helps someone else.
It seems my issue was the result of a threading issue.
I noticed the C# winform apps I had created were automatically set to run on a single 'ApartmentState' thread ([STAThread]).
Whereas the console applications & class library thread state and management hadn't been defined within the project and was being handled with the default .NET config.
To get this to work: In the console application, I just added the [STAThread] tag on the line above my Main method call.
In the class library, I defined a thread for the function referencing the IMANxxx.dll and set ApartmentState e.g.
Thread t = new Thread(new ThreadStart(PerformSearchAndMove));
t.SetApartmentState(ApartmentState.STA);
t.Start();
In both cases ensuring single 'ApartmentState' thread was implemented set would resolve the issue.

Categories