Skip to main content

Issues concerning X++


I was looking at the API that we publish for the X++ compiler, and it struck me that it would be really easy to implement a script host for X++. This is a program that allows you to execute arbitrary X++ code that is stored in files in the file system. In this way, you can use X++ as a systems programming language, starting things at particular times etc. I thought it would be fun to see what it takes to implement that command line tool. In the spirit of sharing, I am listing the C# code below. It is, after all, only just over 100 lines of code. Let's imagine that we have a file called MyFile.xpp containing the following:
real f(int i, real r, str s)
{
    ;
    System.Console::WriteLine("Hello world");
    return i + r + strlen(s);
}
With the script host installed, you can do things like:
C\> XppScriptHost  MyFile.xpp 3  3.141 "I am an argument".
The script host will return a code to the operating system, so that decisions can be made in script files etc. You could even register the file type (Xpp in this case) with the operating system, so that clicking on the xpp file will automatically start the script (but you will not be able to pass parameters). This is really easy in Windows: You just right click in the .xpp file, and the system will ask you to identify the program to use to open the file. Select this tool and you're in business.
There is certainly room for improvement to this: You could introduce parameters for each of the four arguments passed to the logon call if that is useful in your scenarios. I did not do so here for the sake of simplicity.
Since the business connector is compiled against an earlier version of .NET (to be compatible with the sharepoint pieces), you will need to tell the command line tool that it is should support the earlier framework. You cannot use .NET 2, because the LINQ stuff in the code would not run in that case. Feel free to rewrite that if you must. I just use a configuration file (app.Config) with the following content:
xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>

Here is the source code in all its glory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Microsoft.Dynamics.Ax.XppScriptExecutor
{
    using Microsoft.Dynamics.BusinessConnectorNet;
    using System.IO;
 
    /// 
    /// Command line tool to evaluate X++ snippets as functions provided in a 
    /// file that is provided as parameter. Any number of parameters may follow,
    /// and these are used as parameters to the method. The result is written to 
    /// stdout. 
    /// 
    class XppScriptHost     {         ///          /// Entry point to the console application.         ///          /// "args">The arguments passed from the command line.         ///          /// "bullet">         /// 0 if all was well         /// 1 if the file was not found         /// 2 if the script contains errors         /// 3 if the some other error happened         ///          ///          static int Main(string[] args)         {             if (args.Length < 1 || args.Contains("-help"StringComparer.OrdinalIgnoreCase))             {                 System.Console.Error.WriteLine("XppScriptHost filename parameter...");                 System.Console.Error.WriteLine("    The filename must denote a file containing an X++ function.");                 System.Console.Error.WriteLine("    The following arguments are interpreted as parameter values.");                 System.Console.Error.WriteLine("    The result returned from the X++ function is printed on stdout.");                 System.Console.Error.WriteLine("    The X++ code can use System.Console::WriteLine(...) to print output");                 System.Console.Error.WriteLine("");                 System.Console.Error.WriteLine("XppScriptHost -help");                 System.Console.Error.WriteLine("    Writes this message.");                 return 0;             }                          var source = string.Empty;             try             {                 using (var stream = new StreamReader(args[0]))                 {                     source = stream.ReadToEnd();                 }             }             catch (Exception e)             {                 System.Console.Error.WriteLine("File " + args[0] + " could not be opened.");                 System.Console.Error.WriteLine(e.Message);                 return 1;             }             Axapta ax = new Axapta();             try             {                 ax.Logon(nullnullnullnull);                 AxaptaObject xppCompiler = ax.CreateAxaptaObject("XppCompiler");                 var success = (bool)xppCompiler.Call("compile", source);                 if (!success)                 {                     // An error occurred during compilation. Get the error messages.                     var messages = xppCompiler.Call("errorText"as string;                     System.Console.Error.WriteLine(messages);                     return 2;                 }                 // The compilation proceeded without error. Now execute it.                 // Push parameters on the X++ stack                 xppCompiler.Call("startArgs");                 foreach (var arg in args.Where(arg => !arg.StartsWith("-")).Skip(1))                  {                     int ival;                     decimal dval;                     if (arg.StartsWith("\"") && arg.EndsWith("\""))                     {                         xppCompiler.Call("setStrArg", arg.TrimStart('"').TrimEnd('"'));                     }                     else if (int.TryParse(arg, out ival))                     {                         xppCompiler.Call("setIntArg", ival);                     }                     else if (decimal.TryParse(arg, out dval))                     {                         xppCompiler.Call("setRealArg", dval);                     }                     else                      {                         xppCompiler.Call("setStrArg", arg);                     }                 }                 xppCompiler.Call("endArgs");                 var o = xppCompiler.Call("executeEx");                 // Write the return value to stdout.                 System.Console.Out.WriteLine(o.ToString());                 return 0;             }             catch (Exception e)             {                 System.Console.Error.WriteLine("Error during execution");                 System.Console.Error.WriteLine(e.Message);                 return 3;             }             finally             {                 ax.Logoff();             }         }             } }

Popular posts from this blog

What does this mean: "The form datasource query object does not support changing its AllowCrossCompany property after the form has executed the query."?

I have made a form with datasources vendtable and vendtrans. Inside vendtable_ds.executequery() looks like this: QueryBuildDataSource queryBuildDatasource ,queryBDS_VendTrans_Invoice; ; queryBuildDatasource = this.query().dataSourceTable(tablenum(vendtable)); queryBDS_VendTrans_Invoice = this.query().dataSourceTable(tablenum(vendtrans)); if (curext() == "MASTERCOMP") { this.query().allowCrossCompany(true); } else { this.query().allowCrossCompany(false); } //FilterVendorName = stringedit control on form if (FilterVendorName.text()) { queryBuildDatasource.addRange(fieldNum(VendTable,Name)).value(strfmt("*%1*", FilterVendorName.text())); } else { queryBuildDatasource.clearRange(fieldNum(VendTable,Name)); } //FilterInvoiceNumber = stringedit control on form if (FilterInvoiceNumber.valueStr() == "") { queryBDS_VendTrans_Invoice.enabled(false); } else { queryBDS_VendTrans_Invoice.enabled(true); queryBDS_VendTrans_In...

Credit Note [Dynamics AX] using X++

This post will help to create credit note for a sales order based on the invent lot id. All the invoices raised for a particular sales line – Lot Id will be raised back as a credit note. Information on Credit Note: A credit note or credit memorandum (memo) is a commercial document issued by a seller to a buyer. The seller usually issues a Credit Memo for the same or lower amount than the invoice, and then repays the money to the buyer or sets it off against a balance due from other transactions Below Code will help to create credit note for all the invoices raised against the sales line -lot id. Please note: This code can be customized as per your requirements. This is just a template to help creating credit note using X++ code. Please test the code before use. static void SR_CreateCreditNote_Sales(Args _args) { // Coded by Sreenath Reddy CustInvoiceTrans custInvoiceTrans; Dialog dialog = new Dialog(“Create credit note – for sales.”); DialogField dfInv...