Thursday, December 18, 2008

Run DAX4 AOS as a console application

I've recently found an interesting feature of DAX4 Object Server: it can run not only as a service, but also as a console application started from command line. All you have to do is pass a console parameter before the configuration number, e.g. 

ax32serv.exe /console 01
This feature was probably added for debugging purpose as it's rather difficult to start a Windows service under a debugger.
When started as a console application AOS can be terminated by pressing Ctrl-C or Ctrl-Break.


In Dynamics AX it's often necessary to install some custom files such as ActiveX components, DLLs, templates, etc (that are not part of the installation package) on each client computer. There's a couple of handy classes to accomplish this task: SysFileDeployer and SysFileDeployment (and it's descendant SysFileDeploymentFile). Unfortunately this mechanism has been completely broken in AX4 due to Code Access Security covering «dangerous API» usage on the server tier, see Writing Secure X++ Code.

Implementing Code Access Security
Code access security must be implemented by the dangerous API owner and all consumers of the dangerous API.
1. The owner secures the dangerous API by implementing a specific type of permission class and calling the demand() method on that class
2. Each API consumer must explicitly request permission to invoke a secured dangerous API by calling the assert() method on the permission class.
Application code will break unless both of these steps are completed.

And everything is ok with the demand() calls in the WinAPIServer class whereas SysFileDeployment* classes lack the assert() calls. Probably they were not tested thoroughly or something… To be precise, here is what I've found:

SysFileDeploymentFile.serverVersion() lacks corresponding assert() call before WinAPIServer::getFileModifiedDate();

SysFileDeployment.getServerFileTimeAccessed()/Modified()/Created() lack corresponding assert() calls before WinAPIServer::getFileTime();

SysFileDeployment.isNameValid() splits full file name via fileNameSplit() and after that adds a redundant dot between the name and the extension (fileNameSplit() already returns file extension with a leading dot) - as a result isNameValid() always returns false which in its place causes corresponding assert() calls not to happen;

Finally, SysFileDeployment.sourcePath() returns xInfo::directory(DirectoryType::Include) by default and the latter is known to return different results depending on which (client or server) tier it is called. Of course, SysFileDeploy* classes work in such a way that SysFileDeployment.sourcePath() is called on both tiers - and that completely ruins all the program logic


You can download a small fix from axaptapedia.com to make SysFileDeployment work again. Microsoft claims there's already a hot fix with a description "File deployment error FileIOPermission failed", so there's a hope we will see this fixed in AX4 SP2...

Labels: AX4, SysFileDeployment



I think it's quite a common task to read data from Excel files. A straight-forward way is to use Excel COM interfaces (Workbook, Worksheet, Range, Cell, etc), but damn it’s slow! On the other hand there is ADO (ActiveX Data Objects) that is fast, flexible and familiar to many developers. So it would be nice to access Excel files via ADO to speed up data import and simplify application code…

Here is a utility class that allows reading Excel worksheets as Recordsets. It uses ADOX.Catalog to collect Excel worksheet names and ADODB.Connection, ADODB.Recordset to access worksheet data with a simple "select * from [sheetname]" Recordset's command.

A typical scenario would be something like this:
Counter         cnTotal = 0;
ItemId          itemId;
ItemName        itemName;
AmountCur       price;
Filename        strFilename;
container       conSheets;
ExcelImportADO  xlImport;
;
strFilename = @"c:\import.xls";
xlImport    = new ExcelImportADO(strFilename);
try
{
    // open the first sheet by default
    if(!xlImport.openFile())
        throw error(strfmt("Error opening Excel file «%1»", strFilename));
    if(xlImport.getFieldsCount() <>
        throw error(strfmt("Too few columns in the recordset:" +
                           " found %1, expected at least %2",
                           xlImport.getFieldsCount(), 3));
    while(!xlImport.eof())
    {
        itemId   = xlImport.getFieldValue(1);
        itemName = xlImport.getFieldValue('ItemName');
        price    = xlImport.getFieldValue('ItemPrice', false);
        // process data...
        cnTotal++;
        xlImport.moveNext();
    }
    xlImport.finalize();
    Box::info(strfmt("%1 records read", cnTotal));
}
catch(Exception::Error)
{
    xlImport.finalize();
}
You can download the source code of the class at axaptapedia.com. I would like to thank Gustav and blokva for inspiration and very useful tips on improving the class.
Note: ADOX.Catalog returns table names (Excel sheet names) in an alphabetical order - not in the order they appear in a worksheet, and by default the class uses the first name returned by ADOX.Catalog!

posted by gl00mie at 11:29 AM | 0 comments 
Wednesday, July 19, 2006
Dynamics AX and ADSI 

     I've been using ADSI for a while during my work as a system administrator and I realy adore it but AX does lack Active Directory access facilities. There is only one built-in tool (as far as I know) to access AD from AX - that is ADObject class. But all it can do is find a user in the default domain and get some properties of it. To be precise it finds AD objects in the GetObject("LDAP://rootDSE").get("defaultNamingContext") using filter "(&(objectClass=user)(objectCategory=person)(sAMAccountName=%s))". AX supports COM objects (via IDispatch interface only), but if you try somehow to get an instance of IADs object you'll get an error so we should figure out how to get an IADs instance for a given LDAP path...
     Unfortunately it looks like there is no way to do this whithout an external tool. I'm planning to write a small COM object that will make possible for an AX application to get instances of ADSI objects for a given LDAP path. Later I'll tell what I managed to do...


View my complete profile
Links
Google News
Edit-Me
Edit-Me
Previous
Run DAX4 AOS as a console application
AX4 SysFileDeployment Fix
Read Excel table via ADO
Dynamics AX and ADSI
Archives

No comments:

How to identify the user that was used to change an object from AOT in AX2012

Get the object name for which we need to track these (user and date&time) information's. Login to SQL Server Management Studio an...