Reading a text file

To follow up the post about writing a text file with Automation I would also like to post about reading a text file with that same Automation.  Now we add the automation automation ‘Windows Script Host Object Model’.File.  First you need to create a file system object ‘Windows Script Host Object Model’.FileSystemObject. Then open the file in the File automation to get the size of the file. Finally open the textstream and start reading. This will give you correct progress dialog and the correct character set when reading.
[code htmlscript=”false”]IF FileName = ” THEN
ERROR(Text001);

CLEAR(SystemFilesSystem);
CREATE(SystemFilesSystem,TRUE,TRUE);

IF NOT SystemFilesSystem.FileExists(FileName) THEN
ERROR(Text002);

SystemFile := SystemFilesSystem.GetFile(FileName);
SystemTextStream := SystemFile.OpenAsTextStream;
FileSize := SystemFile.Size;
FilePos := 0;

Window.OPEN(Text003 + ‘\\@1@@@@@@@@@@@@@@@@@@@@’);
WindowLastUpdated := CURRENTDATETIME;
WHILE NOT SystemTextStream.AtEndOfStream DO BEGIN
Line := SystemTextStream.ReadLine;
FilePos := FilePos + STRLEN(Line) + 2;

‘Handle Line

IF (CURRENTDATETIME – WindowLastUpdated) > 100 THEN BEGIN
Window.UPDATE(1,ROUND(FilePos / FileSize * 10000,1));
WindowLastUpdated := CURRENTDATETIME;
END;
END;
Window.Close;
SystemTextStream.Close;
CLEAR(SystemFilesSystem);[/code]
 

Dialog in Dynamics NAV

In so many cases when writing code for Dynamics NAV you want to display a dialog to notify the user or open a progress dialog.  Today, we always need to consider that the code might be running from a web service where the GUIALLOWED variable is set to false.

I created a codeunit to replace the dialog variable type in my code.  That codeunit is attached at the end.  This saves me work and the code is quite simple.
[code htmlscript=”false”]Customer – OnPreDataItem()
DialogInstance.WindowOpen(
‘Reading Customer #1##################\\’ +
‘@2@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@’);
DialogInstance.WindowSetTotal(2,COUNT);

Customer – OnAfterGetRecord()
DialogInstance.WindowProcess(2);
DialogInstance.WindowUpdateText(1,"No.");

Customer – OnPostDataItem()
DialogInstance.WindowClose;[/code]
The Dialog Instance codeunit checks if a dialog can be opened and only updates the progress dialog ten times per second.
[code htmlscript=”false”]WindowProcess(WindowIndex : Integer)
IF NOT GUIALLOWED THEN
EXIT;

Counter[WindowIndex] := Counter[WindowIndex] + 1;

IF NOT DialogIsOpen THEN
EXIT;

IF (CURRENTDATETIME – WindowLastUpdated) > 100 THEN BEGIN
Window.UPDATE(WindowIndex,ROUND(Counter[WindowIndex] / Total[WindowIndex] * 10000,1));
WindowLastUpdated := CURRENTDATETIME;
END;[/code]
DialogInstance

Writing to a text file

In Classic Client we are used to use the variable type File to write to a text file.  In these cases we have had to convert the Icelandic characters from CP850 to ISO-8859-1 for this to be saved correctly.  In Role Tailored Client this file is created on the server and you will have to download the file to the client computer and save it there.

There is an easy solution to this.  Simply use the automation ‘Windows Script Host Object Model’.TextStream.  First you need to create a file system object ‘Windows Script Host Object Model’.FileSystemObject.
[code htmlscript=”false”]CREATE(SystemShell,TRUE,TRUE);
SystemTextStream := SystemShell.CreateTextFile(TheFileName,TRUE);[/code]
where SystemShell is Automation ‘Windows Script Host Object Model’.FileSystemObject and SystemTextStream is Automation ‘Windows Script Host Object Model’.TextStream

For each line you need to write to the file you do
[code htmlscript=”false”]SystemTextStream.WriteLine(Line);[/code]
and the in the end you close the stream and thereby the file.
[code htmlscript=”false”]SystemTextStream.CLOSE;[/code]
if the CREATE function use TRUE,TRUE then the automation is created on the client side and the file will be created on the client computer.

There are dotnet objects available to do the same thing if you are only running Role Tailored Client.

UTF-8 to ISO-8859-1

I need to create a XML document with ISO-8859-1 encoding.  This option is not available in XML Ports so what can you do?

I wrote the following function to read an UTF-8 encoded file and convert it to ISO-8859-1.
[code htmlscript=”false”]PROCEDURE UFT8File_To_ISO88591File@1200050006(UFT8_FileName@1200050000 : Text[1024];ISO88591_FileName@1200050001 : Text[1024]);
VAR
UFT8Stream@1200050008 : Automation "{2A75196C-D9EB-4129-B803-931327F72D5C} 2.8:{00000566-0000-0010-8000-00AA006D2EA4}:’Microsoft ActiveX Data Objects 2.8 Library’.Stream";
ISOStream@1200050007 : Automation "{2A75196C-D9EB-4129-B803-931327F72D5C} 2.8:{00000566-0000-0010-8000-00AA006D2EA4}:’Microsoft ActiveX Data Objects 2.8 Library’.Stream";
String@1200050009 : Text[1024];
adReadAll@1200050002 : Integer;
adSaveCreateOverWrite@1200050003 : Integer;
adTypeBinary@1200050004 : Integer;
adTypeText@1200050005 : Integer;
adWriteChar@1200050006 : Integer;
BEGIN
adReadAll := -1;
adSaveCreateOverWrite := 2;
adTypeBinary := 1;
adTypeText := 2;
adWriteChar := 0;

CREATE(UFT8Stream);
CREATE(ISOStream);
UFT8Stream.Open;
UFT8Stream.Type := adTypeBinary;
UFT8Stream.LoadFromFile(UFT8_FileName);
UFT8Stream.Type := adTypeText;
UFT8Stream.Charset := ‘UTF-8’;

ISOStream.Open;
ISOStream.Type := adTypeText;
ISOStream.Charset := ‘iso-8859-1′;
WHILE NOT UFT8Stream.EOS DO BEGIN
String := UFT8Stream.ReadText(MAXSTRLEN(String));
ISOStream.WriteText(String,adWriteChar);
END;
ISOStream.SaveToFile(ISO88591_FileName,adSaveCreateOverWrite);
ISOStream.Close;
CLEAR(ISOStream);
UFT8Stream.Close;
CLEAR(UFT8Stream);
END;[/code]
Then I stream the BLOB that holds the UTF-8 encoded XML to a file and convert it.
[code htmlscript=”false”]IF ISCLEAR(SystemShell) THEN
CREATE(SystemShell);

Log."Outgoing Message".CREATEINSTREAM(InStr);
UFT8_FileName := ThreeTierMgt.ServerTempFileName(”,’XML’);
PaySlipFile.CREATE(UFT8_FileName);
PaySlipFile.CREATEOUTSTREAM(OutStr);
COPYSTREAM(OutStr,InStr);
PaySlipFile.CLOSE;
IF ISSERVICETIER THEN BEGIN
ISO_FileName := ThreeTierMgt.ServerTempFileName(”,’XML’);
Helper.UFT8File_To_ISO88591File(UFT8_FileName,ISO_FileName);
PaySlipFile.OPEN(ISO_FileName);
PaySlipFile.CREATEINSTREAM(InStr);
DOWNLOADFROMSTREAM(InStr,Text006,”,Text007,ToFile);
SystemShell.DeleteFile(ISO_FileName);
END ELSE BEGIN
ISO_FileName := ToFile;
Helper.UFT8File_To_ISO88591File(UFT8_FileName,ISO_FileName);
END;
SystemShell.DeleteFile(UFT8_FileName);[/code]
Manually you will need to replace the first line in the XML Document from ‘<?xml version=”1.0″ encoding=”UTF-8″ ?>’ to ‘<?xml version=”1.0″ encoding=”ISO-8859-1″?>’

 

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

 

WinHTTP and RTC Client

I have been using the automation ‘Microsoft XML, v6.0’.XMLHTTP to communicate with web services and web sites.  I have been experiencing a problem with this automation when running in Role Tailored Client.  The solution has been to use the automation ‘Microsoft XML, v6.0’.ServerXMLHTTP when running in the service tier.
[code htmlscript=”false”]IF ISSERVICETIER THEN BEGIN
IF ISCLEAR(WinHTTPServer) THEN
CREATE(WinHTTPServer,TRUE,FALSE);
WinHTTPServer.open(‘GET’,URL,FALSE);
WinHTTPServer.send(”);

IF WinHTTPServer.status <> 200 THEN
ERROR(Text007,WinHTTPServer.status,WinHTTPServer.statusText);

DOMDocument.load(WinHTTPServer.responseXML);
CLEAR(WinHTTPServer);
END ELSE BEGIN
IF ISCLEAR(WinHTTP) THEN
CREATE(WinHTTP,TRUE,FALSE);
WinHTTP.open(‘GET’,URL,FALSE);
WinHTTP.send(”);

IF WinHTTP.status <> 200 THEN
ERROR(Text007,WinHTTP.status,WinHTTP.statusText);

DOMDocument.load(WinHTTP.responseXML);
CLEAR(WinHTTP);
END;[/code]
Where Error string Text007 is “Status error %1 %2”.