How to print any file in Dynamics AX

January 27, 2014 at 23:09
filed under Dynamics AX, RealDolmen Experts
Tagged , , , , , ,

Hi All!

So you need to print some files from Dynamics AX? And it also has to work in batch?
Below are 2 methods that will help you do just that.

Printing text files using the copy method

This was the first method I used, and it works flawlessly when using text files, such as .txt or .csv files. It also works great on .prn files.
To print such a file, you can simple copy the file to the printer. See example code below.

static void KlFor_PrintUsingCopy(Args _args)
{
    // use only for text files

    #File
    Set     permissionSet;
    System.Exception interopException;
    FileName    destinationFileName;

    // parameters
    Filename    fileName    = @"C:\text.txt";
    PrinterName printername = @"\\PrintServer\YourPrinter";
    ;

    try
    {
        // combine the destination path using the printer and the filename
        new InteropPermission(InteropKind::ClrInterop).assert();
        destinationFileName = System.IO.Path::Combine(printerName, System.IO.Path::GetFileName(fileName));
        CodeAccessPermission::revertAssert();

        // then copy the file
        permissionSet =  new Set(Types::Class);
        permissionSet.add(new FileIOPermission(fileName,#io_write));
        permissionSet.add(new FileIOPermission(destinationFileName,#io_write));
        permissionSet.add(new InteropPermission(InteropKind::ClrInterop));

        CodeAccessPermission::assertMultiple(permissionSet);

        System.IO.File::Copy(fileName, destinationFileName);

        CodeAccessPermission::revertAssert();
    }
    catch(Exception::CLRError)
    {
        // catch CLR errors and show inner exception
        interopException = CLRInterop::getLastException();
        while (!CLRInterop::isNull(interopException.get_InnerException()))
        {
            interopException = interopException.get_InnerException();
        }

        error(strFmt("%1", CLRInterop::getAnyTypeForObject(interopException.get_Message())));
    }
}

An easy method, but do not use it on binary file types. Doing this would result in and endless stream of pages with unreadable characters on it. Yes… I’ve tried.

Print any file use System.Diagnostics.Process

A better way to print files is using the System.Diagnostics namespace. This example uses the “printto” verb to print the files to the server. This verb is visible in the context menu when you right click on a file, it will be listed as the option “Print”. Basically we are using Windows this functionality together with file extension associations to automatically print any file type.
A prerequisite is that an application is available that can open and print the files you want to print. If for example you want to print PDF files in batch using this method, you should install PDF reader software on the AOS server.

static void KlFor_PrintUsingSystemDiagnostics(Args _args)
{
    #File
    System.Diagnostics.ProcessStartInfo processInfo;
    System.Diagnostics.Process process;
    System.Exception interopException;
   
    // Parameters
    Filename    fileName    = @"C:\text.txt";
    PrinterName printername = @"\\PrintServer\YourPrinter";
    ;

    printerName = '"' + printerName + '"';

    try
    {
        // assert permissions
        new InteropPermission(InteropKind::ClrInterop).assert();

        process = new System.Diagnostics.Process();

        processInfo = process.get_StartInfo();

        processInfo.set_UseShellExecute(true);
        processInfo.set_CreateNoWindow(true);
        processInfo.set_FileName(fileName);
       
        // the argument is the printer name
        processInfo.set_Arguments(printerName);
       
        // set the verb to printto
        processInfo.set_Verb('printto');
        processInfo.set_WindowStyle(System.Diagnostics.ProcessWindowStyle::Hidden);

        process.Start();

        // revert asserted permissions
        CodeAccessPermission::revertAssert();
    }
    catch(Exception::CLRError)
    {
        interopException = CLRInterop::getLastException();
        while (!CLRInterop::isNull(interopException.get_InnerException()))
        {
            interopException = interopException.get_InnerException();
        }

        error(strFmt("@RDS10563", fileName, CLRInterop::getAnyTypeForObject(interopException.get_Message())));
    }
}

You can use the code above to do many other things .The functionality is similar to shellexecute, only neater. If you are interested in modifying the code for other purposes using other verbs, you can figure out what verbs you can use using the job below. You will notice that the verbs that are available depend on the extension the file is using.

static void KlFor_GetAllVerbsForFile2(Args _args)
{
    System.Diagnostics.ProcessStartInfo processInfo;
    System.Array verbs;
    int i;
    List fileNames;
    container verbsContainer;
    ListEnumerator listEnumerator;

    fileNames = new List(Types::String);
    fileNames.addEnd(".xlsx");
    fileNames.addEnd(".docx");
    fileNames.addEnd(".png");
    fileNames.addEnd(".txt");
    fileNames.addEnd(".pdf");

    listEnumerator = fileNames.getEnumerator();

    new InteropPermission(InteropKind::ClrInterop).assert();

    while(listEnumerator.moveNext())
    {
        processInfo = new System.Diagnostics.ProcessStartInfo(listEnumerator.current());
        verbs = processInfo.get_Verbs();

        verbsContainer = conNull();

        for( i=0; i<ClrInterop::getAnyTypeForObject(verbs.get_Length()); i++ )
        {
            verbsContainer += ClrInterop::getAnyTypeForObject(verbs.GetValue(i));
        }

        info(strFmt("%1 has verbs: %2", listEnumerator.current(), con2Str(verbsContainer)));
    }

    CodeAccessPermission::revertAssert();
}

It takes a while to figure out how to do these things so hopefully this will save someone the trouble of going through that.

5 comments

RSS / trackback

  1. Thomas Lorenzi

    Really nice post. Was exactly the one I was look for.
    I have an issue when executing the code with System.Diagnostics.Process class on server side on AX2012 R2 CU7
    when trying to print a PDF file using adobe acrobat reader. On client side it works like a charm.

    The error I get is the following:
    c:\test.pdf The specified executable is not a valid application for this OS platform.

    Is this code limited to client processing only or should it work also on server/batch side? Maybe I’m missing something.

    Any help would be great :-)

  2. Klaas Deforche

    Hi Thomas,

    It should work on server side. We use it on server (in batch) and it works fine.
    However this means that you need software installed on the AOS that prints it, and the aos service account must have access to the file you are trying to print. C:\test.pdf should be on the C drive of the AOS server. In most cases there are more than one AOS so you can not use such paths. It is best practice always to use UNC paths (\\aserver\afolder\etc), especially for security reasons.

    Hope this helps :).

  3. Luis

    Great post! I’ve implemented it and it works great!
    I’m getting a strange error when i send docx files to the default printer. Word raises printer exception, but the document prints great. Any idea? could it be a permissons problem?

    Thanks in advance

  4. Trackbacks

respond