Insert network printers automatically

In my current upgrade project I need to upgrade a solution I did for Classic NAV where I used the “‘Windows Script Host Object Model’.WshNetwork” Automation object to add a network printer automatically.

I looked a round and did not find any dotnet object capable of adding a network printer.  However, there was a c# code I found that was able to do this.  So, I created a NAV Add-in with this code

[code lang=”csharp”]using System;
using System.Runtime.InteropServices;

namespace NAVPrinterAdd_in
{
public static class PrinterControls
{
[DllImport("winspool.drv")]
static extern bool AddPrinterConnection(string pName);

public static bool AddNetworkPrinter(string networkPrinterPath, ref string networkMessage)
{
bool result;
try
{
result = AddPrinterConnection(networkPrinterPath);
networkMessage = "";
}
catch (Exception e)
{
result = false;
networkMessage = e.Message;
}
return result;
}
}
}

[/code]

The Add-in is attached. Next I created a Codeunit in NAV.

AddPrinter

And used in Codeunit 1, FindPrinter trigger.

codeunit1

Now I can make sure that the correct printer is installed for the client and also for the server when printing from NAS session.

NAVPrinterAdd-in

Where is my NAS Company ?

I have now gone through my first upgrade from NAV 2013 to NAV 2013 R2.

  • Stopped and uninstalled NAV 2013 Server
  • Opened NAV 2013 database with NAV 2013 Developement Environment and upgraded the database
  • Compiled all system tables (2000000004-2000000130)
  • Installed and started NAV 2013 R2 Server
  • Compiled all objects
  • Imported Upgrade700701.IS.1.fob from the NAV 2013 R2 DVD – where IS is the localized version
  • Started Page 104001 in every company and executed “Transfer Data”
  • Started Page 104001 and executed “Delete Objects”
  • Imported NAV 2013 R2 fob file with the application
  • Restarted NAV 2013 R2 Server
  • Imported Upgrade700701.IS.2.fob from the NAV 2013 R2 DVD – where IS is the localized version
  • Started Page 104002 in every company and executed “Transfer Data”
  • Started Page 104001 and executed “Delete Unused Tables” and “Delete Upgrade Toolkit”

There where a few things I would have liked to go better.

  • I needed to upload the developer license to the database to be able to upgrade.  In an environment where multiple databases share the same license this could be a problem.
  • Codeunit 104004 only deletes the upgrade data for the current company as if the upgrade process is not designed to run for a database with more than one company.  Tables 104003 and 104037 are not emptied.  There is no way to execute this Codeunit for all companies since it is deleted in the first execution.  Therefore I needed to manually empty the upgrade tables and delete them.
  • Another problem with Codeunit 104004 is that is does not delete the Queries 104001 – 104004.  I needed to manually delete them after the upgrade process.

I sure would like Microsoft to take a look at these issues and find a solution.

Then I wanted to start the NAS Service.  In the Server Administration I did not see the NAS Company setting.  I looked at the documentation for Configuring NAS Service and found

Setting Description
NAS Company Specifies the Microsoft Dynamics NAV company that opens when NAS services start.

So I asked my MVP’s for the answer and Arend-Jan Kauffmann answered.

NAVAdministration

The PowerShell parameter changes from NASCompany to ServiceDefaultCompany.

In the documentation it states that

It is recommended that you create a separate Microsoft Dynamics NAV Server instance for each NAS services application. See How to: Create a Microsoft Dynamics NAV Server Instance.

There are multiple reasons for running NAS services sessions in dedicated Microsoft Dynamics NAV Server instances:

  • Efficiency and convenience            
     When you change any Microsoft Dynamics NAV Server setting, you must restart the instance for the change to take effect, which interrupts all services using that instance. So if you are running different types of services in the same instance—for example, RoleTailored client services and NAS services—making a change to the settings for either service type will require a server instance restart that interrupts all other service types running through that instance.
    Even for different types of NAS services applications it’s wise to run each application in a separate server instance. For example, if you will be using NAS services for a Microsoft Office Outlook Integration application and also for a Microsoft Dynamics NAV job queue application, create a separate Microsoft Dynamics NAV Server instance for each NAS services application. This way, if you need to modify settings for the Microsoft Office Outlook Integration application you will not affect the Microsoft Dynamics NAV job queue application, and vice-versa.
  • Performance            
    Configuring NAS services applications to use separate server instances makes better use of the server computer’s resources, allowing you to run more applications with less degradation.
  • Efficient error tracking            
    If a NAS services session terminates in an error, and there are no other services running on the Microsoft Dynamics NAV Server instance, the service instance terminates and can be handled like any other Windows service. For example, you could configure the Recovery tab on the Service configuration tool in Control Panel to restart or otherwise manage the service.

The same then applies to OData Services I guess – create a dedicated server instance for OData.

I used the following PowerShell script to create a new NAS Server Instance

[code]$DatabaseServerName = ‘localhost’
$DatabaseName = ‘NAV2013_COMPANY’
$ServiceInstanceName = ‘NAV2013_COMPANY’
$CompanyName = ‘Company Name’
$MgtPort = 7089
$ClientPort = $MgtPort + 1
$SoapPort = $ClientPort + 1
$ODataPort = $SoapPort + 1

Get-Credential | New-NAVServerInstance -ServerInstance $ServiceInstanceName -ServiceAccount User -ClientServicesCredentialType Windows -ClientServicesPort $ClientPort -DatabaseName $DatabaseName -DatabaseServer $DatabaseServerName -ManagementServicesPort $MgtPort -SOAPServicesPort $SoapPort -ODataServicesPort $ODataPort
Set-NAVServerInstance -ServerInstance $ServiceInstanceName -Start
Set-NAVServerConfiguration -ServerInstance $ServiceInstanceName -KeyName ‘NASServicesStartupCodeunit’ -KeyValue ‘450’
Set-NAVServerConfiguration -ServerInstance $ServiceInstanceName -KeyName ‘NASServicesStartupMethod’ -KeyValue ”
Set-NAVServerConfiguration -ServerInstance $ServiceInstanceName -KeyName ‘NASServicesStartupArgument’ -KeyValue ‘JOBQUEUE’
Set-NAVServerConfiguration -ServerInstance $ServiceInstanceName -KeyName ‘ServicesDefaultCompany’ -KeyValue $CompanyName
Set-NAVServerConfiguration -ServerInstance $ServiceInstanceName -KeyName ‘NASServicesEnableDebugging’ -KeyValue $True
Set-NAVServerInstance -ServerInstance $ServiceInstanceName -Restart[/code]

Now I can start to use NAV 2013 R2.

The Kill Idle batch

In an earlier post I showed how to kill idle sessions from C/AL code.  I have been asked to provide a batch that works with Job Queue.

To set this up please add a field to table 91.  Field name is “Kill Idle Duration” and the data type is “Duration”.  In my case this field has the number 50000.  If that number is unavailable just use another number and import the text object and compile.

Attached is the report that can be used in the job queue.  Just make sure that the user running the job queue has enough permissions as shown in my earlier post.

If this is of any value to you please check my copyright page and donate to the web.

Report 89209

Need NAS to communicate with a web service ?

My recent post on creating a class in visual studio to simplify the NAV C/AL code for web service is a easy solution for NAV 2013.  It will work both for the user and for the Job Queue.  In NAV 2009 it will also work for the user running the Role Tailored Client but it will not work in the Job Queue.

There is a solution.  Use the VB.NET NAV Application Server.  This application server is able to execute any codeunit in NAV with dotnet support. 

The license required to start this application server is also cheaper than the classic NAS license.

Microsoft Connect is working

On July 31, 2012 I posted a patch to codeunit 5895 to be able to execute inventory adjustment with NAS.  I also suggested that this solution should be implemented in NAV and now I am receiving a knowledge base article from Microsoft with ID: 2763255 that handles this issue.

Was Microsoft listening to my comments or did someone else suggest this to Microsoft.  At least I can see that the solution that they provide does not match exactly the solution that I suggested – but a solution anyway.

bioPDF writer

I have been using PDFCreator to create PDF documents from my NAV client.  I am also using it with NAS (Nav Application Server) on my Windows 2003 32bit server.  I needed to get this up an running on a 64bit Windows 2008 server and I ran into problems.

I could easily create the PDF documents from my client.  I saw the pdf file appear in my file system and attached it to my record.  But, when NAS was running the same job no files where created and the job failed.  I unsuccessfully tried a few tricks but nothing changed.  I decided to find another path and looked at bioPDF writer.

The result is a new single instance codeunit to handle the print of any report to a pdf file.  Lets say that the user would like to be able to print and open a sales invoice in pdf format.  Copy the function PrintRecords to PrintRecordsToPDF for table no. 112, Sales Invoice Header.  Add two lines and you are done.

[code htmlscript=”false”]WITH SalesInvHeader DO BEGIN
COPY(Rec);
FIND(‘-‘);
ReportSelection.SETRANGE(Usage,ReportSelection.Usage::"S.Invoice");
ReportSelection.SETFILTER("Report ID",'<>0’);
ReportSelection.FIND(‘-‘);
REPEAT
bioPDFMgt.BeforeReportPrint(ReportSelection."Report ID"); // Dynamics.is
REPORT.RUNMODAL(ReportSelection."Report ID",ShowRequestForm,FALSE,SalesInvHeader);
HYPERLINK(bioPDFMgt.AfterReportPrintGetFileName(ReportSelection."Report ID")); // Dynamics.is
UNTIL ReportSelection.NEXT = 0;
END;[/code]

Where bioPDFMgt is a local variable for the BioPDF Management Codeunit.

The BioPDF Management Codeunit needs to be added to global variable in Codeunit 1, ApplicationManagement.  Add to the FindPrinter function.

[code htmlscript=”false”]FindPrinter(ReportID : Integer) : Text[250]
// Dynamics.is start
IF bioPDFMgt.PrinterBufferExists(ReportID) THEN
EXIT(bioPDFMgt.GetPrinterName);
// Dynamics.is end

CLEAR(PrinterSelection);

IF NOT PrinterSelection.GET(USERID,ReportID) THEN
IF NOT PrinterSelection.GET(”,ReportID) THEN
IF NOT PrinterSelection.GET(USERID,0) THEN
IF PrinterSelection.GET(”,0) THEN;

// Dynamics.is start
bioPDFMgt.SaveLastPrinter(PrinterSelection."Printer Name");
// Dynamics.is end

EXIT(PrinterSelection."Printer Name");[/code]

The BioPDF Management Codeunit includes these functions:

Name Description
BeforeReportPrint Setup PDF printer and printer selection
AfterReportPrintGetBLOB Copy the PDF file to a tempBLOB record
AfterReportPrintGetFileName Return the PDF file name
CleanUp Clear PDF automation objects
ConfirmFileExists Return an error if file does not exist
FileExists Return a boolean value for given file name
FileRename Rename a file (copy and delete combined)
FileErase Delete a file
FileCopy Copy a file
ClearPrinterBuffer Clear the printer selection buffer
CreatePrinterBuffer Adds a report and a printer name to the printer selection buffer
PrinterBufferExists Check if a printer selection buffer exists for given report
GetPrinterName Get the printer name from the printer selection buffer
SaveLastPrinter Save the name of the last printer used
GetLastPrinter Get the name of the last printer used

BioPDF Management Codeunit (2009 R2)

BioPDF Management Codeunit (5 SP1)

NAS Failes to initialize with error “‘Já’ is not an option.”

Just got a request from a client because the Job Queue NAS is not working.  Stopped working after a service restart.  Looking through Event Viewer I see the following error.

'Já' is not an option.

The existing options are:

No, Yes

In the %APPDATA% folder for the service user I find a file called NaviBP.xml.  This file includes a list of breakpoints from the last debugging session.  It seems that the debugger writes this file and uses the Icelandic ‘Já’ instead of the required English ‘Yes’.

<?xml version="1.0" ?>
<BreakpointList>
  <Object Type="Codeunit" ID="10010883" Name="E. Com Send-Receive Utilities">
      <Breakpoint>
      <TriggerName>CreateSendInvoiceRequest</TriggerName>
      <CodeNo>8</CodeNo>
      <TriggerLine>32</TriggerLine>
      <Enabled>Já</Enabled>
    </Breakpoint>
  </Object>
</BreakpointList>

After changing the <Enabled>Já</Enabled> to <Enabled>Yes</Enabled> the NAS should start normally.  If those breakpoints are no longer needed it is fine to just remove the file.

 

Job Queue stops when lock time out occurs

Running NAS with Job Queue will start a timer to process Job Queue Entries every two seconds. In the original code the Timer is disabled before checking the Job Queue Entries and then enabled again after the process. If NAS will not be able to read the Job Queue Entry then the function will exit without enabling the Timer and nothing will be processed.

The original code in Codeunit 448 is
[code htmlscript=”false”]HandleRequest()
JobQueueSetup.GET;
IF NOT JobQueueSetup."Job Queue Active" THEN
EXIT;

NavTimer.Enabled := FALSE;

ThisSessionIsActive := UpdateJobQueueSession(JobQueueEntry,FALSE);

CleanUpJobQueue;
COMMIT;

NavTimer.Enabled := TRUE;[/code]
I suggest that you will find a suitable period of time where everything in the Queue should be processed and change the behaviour. Do not disable the timer, just change the interval.  Here I change the interval to five minutes.

The replacement code would then be
[code htmlscript=”false”]HandleRequest()
JobQueueSetup.GET;
IF NOT JobQueueSetup."Job Queue Active" THEN
EXIT;

NavTimer.Enabled := FALSE;
NavTimer.Interval := 5 * 60 * 1000; // 5 min
NavTimer.Enabled := TRUE;

ThisSessionIsActive := UpdateJobQueueSession(JobQueueEntry,FALSE);

CleanUpJobQueue;
COMMIT;

NavTimer.Enabled := FALSE;
NavTimer.Interval := 2 * 1000; // 2 sec.
NavTimer.Enabled := TRUE;[/code] 

VB.NET NAV Application Server

Most of my clients require a running NAV Application Server.  The NAS that is included in NAV 2009 R2 requires a license that is included in most licenses today.  However, there are cases where more than one NAS is needed.  That requires additional NAS licenses.  Where the customer is running NAV 2009 R2 middle tier service this changes.  By running a VB.NET NAV Application Server it is possible to setup multiple services on a single CAL license.  The CAL license is not as expensive as the NAS license.  Here is the solution that I offer.

First, I create a codeunit in NAV

[code htmlscript=”false”]ExecuteCodeunit(CodeunitID : Integer;Log : Boolean) Success : Boolean

IF Log THEN LogEntryNo := InsertLogEntry(5,CodeunitID);
Success := CODEUNIT.RUN(CodeunitID);
IF Log THEN
UpdateLogEntry(LogEntryNo,Success)
ELSE IF NOT Success THEN BEGIN
LogEntryNo := InsertLogEntry(5,CodeunitID);
UpdateLogEntry(LogEntryNo,Success)
END;

InsertLogEntry(ObjectType : ‘,,,Report,,Codeunit’;ObjectNo : Integer) : Integer
WITH JobQueueLogEntry DO BEGIN
INIT;
ID := CREATEGUID;
“User ID” := USERID;
“Start Date/Time” := CURRENTDATETIME;
“Object Type to Run” := ObjectType;
“Object ID to Run” := ObjectNo;
INSERT(TRUE);
COMMIT;
EXIT(“Entry No.”);
END;

UpdateLogEntry(LogEntryNo : Integer;WasSuccess : Boolean)
WITH JobQueueLogEntry DO BEGIN
GET(LogEntryNo);
“End Date/Time” := CURRENTDATETIME;
IF WasSuccess THEN
Status := Status::Success
ELSE BEGIN
Status := Status::Error;
SetErrorMessage(COPYSTR(GETLASTERRORTEXT,1,1000));
END;
MODIFY;
COMMIT;
END;[/code]

This codeunit uses the Job Queue Log to log the execution.  Next step is to publish this codeunit as a web service in table no. 2000000076.  Default web service name is NAVAppServer.

On the server you install the following files (in my case to C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server)


Next step is to edit the VB.NET NAV Application Server.exe.config file and customize the values.

 <applicationSettings>
        <NAV_Application_Server.My.MySettings>
            <setting name="NAVAppServer" serializeAs="String">
                <value>http://<Middle Tiere Host Name>:7047/DynamicsNAV/WS/<CompanyName>/Codeunit/NAVAppServer</value>
            </setting>
            <setting name="CodeunitID" serializeAs="String">
                <value>81004</value>
            </setting>
            <setting name="LogMode" serializeAs="String">
                <value>False</value>
            </setting>
            <setting name="TimerInterval" serializeAs="String">
                <value>90000</value>
            </setting>
            <setting name="LogFrequency" serializeAs="String">
                <value>15:00:00</value>
            </setting>
            <setting name="SMTPHost" serializeAs="String">
                <value>mail.dynamics.is</value>
            </setting>
            <setting name="FromAddress" serializeAs="String">
                <value>gunnar@dynamics.is</value>
            </setting>
            <setting name="ToAddress" serializeAs="String">
                <value>gunnar@dynamics.is</value>
            </setting>
            <setting name="RetryTimerInterval" serializeAs="String">
                <value>45000</value>
            </setting>
        </NAV_Application_Server.My.MySettings>
    </applicationSettings>

To install as a service start command prompt in elevated mode and execute installutil.exe command.  The install will prompt for the user to start the service.

C:\Windows\Microsoft.NET\Framework\v2.0.50727>InstallUtil.exe "C:\Program Files(x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe"

Microsoft (R) .NET Framework Installation utility Version 2.0.50727.5420
Copyright (c) Microsoft Corporation.  All rights reserved.

Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe assembly's progress.

The file is located at C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.InstallLog.
Installing assembly 'C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe'.
Affected parameters are:
   logtoconsole =
   assemblypath = C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe
   logfile = C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.InstallLog
Installing service VB.NET-NAVAppServer$DEFAULT...
Service VB.NET-NAVAppServer$DEFAULT has been successfully installed.
Creating EventLog source VB.NET-NAVAppServer$DEFAULT in log Application...

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe assembly's progress.

The file is located at C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.InstallLog.
Committing assembly 'C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe'.
Affected parameters are:
   logtoconsole =
   assemblypath = C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.exe
   logfile = C:\Program Files (x86)\Dynamics.is\VB.NET NAV Application Server\VB.NET NAV Application Server.InstallLog

The Commit phase completed successfully.

The transacted install has completed.

The last step is to change the service startup and start the service.

The service will create entries in the Application Log.

NAV Web Service Codeunit

The following ZIP files are encrypted.

VB.NET NAV Application Server DEFAULT Executables

NAV App Server Visual Studio 2008 Project

Using Web Services for your NAS jobs

In NAV 7 the NAV Application Server will no longer be supported.  The Job Queue has been redesigned to support the new STARTSESSION feature that will create a new session on the service tier to execute a given task.

In NAV 2009 and going forward it is possible to use web services to act like an application server with the help of a simple program with a timer.

For example a program with a code like this
[code htmlscript=”false” lang=”vb”]Public Class NAVAppServer
Dim Success As Boolean
Dim NAVApp1 As New NAVApp.NAVAppServer
Dim SystemUser As New System.Net.NetworkCredential
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
NAVApp1.UseDefaultCredentials = True
NAVApp1.Url = "https://dynamics.is:7047/DynamicsNAS/WS/Dynamics%20Inc/Codeunit/NAVAppServer"
NAVApp1.ExecuteCodeunit(50059, True)

End Sub
End Class[/code]
The Codeunit NAVAppServer is here and also attached
[code htmlscript=”false”]ExecuteCodeunit(CodeunitID : Integer;Log : Boolean) Success : Boolean
LogEntryNo := InsertLogEntry(5,CodeunitID);
Success := CODEUNIT.RUN(CodeunitID);
UpdateLogEntry(LogEntryNo,Success);

InsertLogEntry(ObjectType : ‘,,,Report,,Codeunit’;ObjectNo : Integer) : Integer
WITH JobQueueLogEntry DO BEGIN
INIT;
ID := CREATEGUID;
"User ID" := USERID;
"Start Date/Time" := CURRENTDATETIME;
"Object Type to Run" := ObjectType;
"Object ID to Run" := ObjectNo;
INSERT(TRUE);
COMMIT;
EXIT("Entry No.");
END;

UpdateLogEntry(LogEntryNo : Integer;WasSuccess : Boolean)
WITH JobQueueLogEntry DO BEGIN
GET(LogEntryNo);
"End Date/Time" := CURRENTDATETIME;
IF WasSuccess THEN
Status := Status::Success
ELSE BEGIN
Status := Status::Error;
SetErrorMessage(COPYSTR(GETLASTERRORTEXT,1,1000));
END;
MODIFY;
COMMIT;
END;[/code]
This is published as a web service by adding an entry into table 2000000076 “Web Service”.

NAVAppServerCodeunit