Symbolic Links on .NET

word_to_pdfI was talking with a colleague earlier today. A few days ago the requirements for a short project specified a service / application that would monitor an input folder (e.g., c:\temp\DocFolder) for *.doc (Word) files and generate in an output folder (e.g., c:\temp\PDFFolder) a *.pdf (Portable Document Format) version of the files. The application / server was implemented and tested.

As usual, requirements tend to change. Yesterday they did. The issue is that clients running on the same machine need to generate a copy of the *.doc files and copy them to the input folder. Of course clients may generate the copy directly in the input folder eliminating the need for the copy operation. Instead of modifying the application / service that converts the files, the client could easily (as shown in this blog) could generate a link to an input *.doc file and copy the link to the input folder.

Let’s place a *.doc file in the c:\temp\DocFolder and from a command prompt execute the following:

C:\> dir c:\temp\docfolder

Volume in drive C is OS

Volume Serial Number is 26E8-87B0

Directory of c:\temp\docfolder

10/14/2016  04:03 PM    <DIR>          .

10/14/2016  04:03 PM    <DIR>          ..

10/14/2016  03:36 PM    <SYMLINK>      bar.doc [c:\temp\foo.doc]

1 File(s)        366,080 bytes

2 Dir(s)  785,369,051,136 bytes free

The results show that we have a bar.doc file that we copied to the c:\temp\DocFolder but it is a <SYMLINK> to the c:\temp\foo.doc file (the actual file we want to generate a PDF. A client application / server could execute the following code:

C:\> linkapp

<<< symbolicLink ==>c:\DocFolder\bar.doc<==

<<<     fileName ==>c:\temp\foo.doc<==

The code takes the original foo.doc file and generates a bar.doc symbolic link to be processed by the application / server that converts *.doc files to *.pdf.

Following is a screen capture of the application / service used to convert *.doc files to *.pdf:

C:\> java -jar c:\temp\wordtopdf.jar

<<< args:

<<< inFileName ==><==

<<< theDocFolder ==><==

<<< thePDFFolder ==><==

<<< configFile ==>c:\ProgramData\ISng\config.json<== NOT found

<<< theDocFolder ==>c:\temp\DocFolder<==

<<< thePDFFolder ==>c:\temp\PDFFolder<==

<<< sleeping …

<<< sleeping …

<<< docFile ==>C:\Temp\DocFolder\bar.doc<==

<<< pdfFile ==>c:\temp\PDFFolder\bar.pdf<==

Oct 14, 2016 3:43:35 PM org.artofsolving.jodconverter.office.ProcessPoolOfficeManager <init>

INFO: ProcessManager implementation is PureJavaProcessManager

Oct 14, 2016 3:43:36 PM org.artofsolving.jodconverter.office.OfficeProcess prepareInstanceProfileDir

WARNING: profile dir ‘C:\Users\John\AppData\Local\Temp\.jodconverter_socket_host-127.0.0.1_port-2002’ already exists; deleting

Oct 14, 2016 3:43:36 PM org.artofsolving.jodconverter.office.OfficeProcess deleteProfileDir

SEVERE: could not delete profileDir: Unable to delete file: C:\Users\John\AppData\Local\Temp\.jodconverter_socket_host-127.0.0.1_port-2002\user\uno_packages\cache\uno_packages.pmap

Oct 14, 2016 3:43:36 PM org.artofsolving.jodconverter.office.OfficeProcess start

INFO: starting process with acceptString ‘socket,host=127.0.0.1,port=2002,tcpNoDelay=1’ and profileDir ‘C:\Users\John\AppData\Local\Temp\.jodconverter_socket_host-127.0.0.1_port-2002’

Oct 14, 2016 3:43:36 PM org.artofsolving.jodconverter.office.OfficeProcess start

INFO: started process

Oct 14, 2016 3:43:38 PM org.artofsolving.jodconverter.office.OfficeConnection connect

INFO: connected: ‘socket,host=127.0.0.1,port=2002,tcpNoDelay=1’

<<< OpenOffice started

<<< document converter created

<<< PDF generated in 12852ms

<<< PDF file created

Oct 14, 2016 3:43:51 PM org.artofsolving.jodconverter.office.ProcessPoolOfficeManager stop

INFO: stopping

Oct 14, 2016 3:43:51 PM org.artofsolving.jodconverter.office.ManagedOfficeProcess doEnsureProcessExited

INFO: process exited with code 0

Oct 14, 2016 3:43:51 PM org.artofsolving.jodconverter.office.OfficeConnection$1 disposing

INFO: disconnected: ‘socket,host=127.0.0.1,port=2002,tcpNoDelay=1’

Oct 14, 2016 3:43:51 PM org.artofsolving.jodconverter.office.OfficeProcess deleteProfileDir

SEVERE: could not delete profileDir: Unable to delete file: C:\Users\John\AppData\Local\Temp\.jodconverter_socket_host-127.0.0.1_port-2002\user\uno_packages\cache\uno_packages.pmap

Oct 14, 2016 3:43:51 PM org.artofsolving.jodconverter.office.ProcessPoolOfficeManager stop

INFO: stopped

<<< OpenOffice stopped

<<< sleeping …

We now may look in the output folder as illustrated by the following screen capture:

C:\> dir c:\temp\pdffolder

Volume in drive C is OS

Volume Serial Number is 26E8-87B0

Directory of c:\temp\pdffolder

10/14/2016  04:12 PM    <DIR>          .

10/14/2016  04:12 PM    <DIR>          ..

10/14/2016  04:12 PM           326,367 bar.pdf

1 File(s)        326,367 bytes

2 Dir(s)  785,368,989,696 bytes free

The proper bar.pdf file was generated. The foo.doc (symlink) was deleted and the original foo.doc file in the c:\temp\ folder was left untouched. The C# code that generates the symbolic link follows:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Runtime.InteropServices;

using System.IO;

namespace LinkApp {

class Program {

[DllImport(“kernel32.dll”)]

static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, SymbolicLink dwFlags);

[DllImport(“kernel32.dll”)]

static extern int GetLastError();

enum SymbolicLink {

File     = 0,

Directory      = 1

}

static void Main (string[] args) {

string symbolicLink = @”c:\temp\bar.doc”;

string fileName             = @”c:\temp\foo.doc”;

Console.WriteLine(“<<< symbolicLink ==>” + symbolicLink + “<==”);

Console.WriteLine(“<<<     fileName ==>” + fileName + “<==”);

bool success = CreateSymbolicLink(symbolicLink, fileName, SymbolicLink.File);

if (!success) {

Console.WriteLine(“<<< CreateSymbolicLink failed”);

int    error = GetLastError();

Console.WriteLine(“<<< error: ” + error);

}

// **** ****

return;

}

}

}

As you can see, the client application / server could easily change a few lines and address the new requirement.

If you have comments or questions on this blog entry, please do not hesitate and send me an email message.

John

john.canessa@gmail.com

Leave a Reply

Your email address will not be published. Required fields are marked *