<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-31341829</id><updated>2011-10-24T23:12:44.783+04:00</updated><category term='MOSS 2007'/><category term='AX2009'/><category term='Query hints'/><category term='Kernel'/><category term='Documentation'/><category term='AX3'/><category term='Book'/><category term='RPC'/><category term='AOS'/><category term='ForceNestedLoop'/><category term='Query'/><category term='AOCP'/><category term='SysFileDeployment'/><category term='SqlDictionary'/><category term='Best Practices'/><category term='ForceSelectOrder'/><category term='AX4'/><title type='text'>Ms Dynamics AX</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-31341829.post-6482326858261908386</id><published>2011-09-01T02:25:00.001+04:00</published><updated>2011-09-23T11:38:09.620+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='SqlDictionary'/><title type='text'>Script to recreate SqlDictionary records in AX 2009</title><content type='html'>&lt;div&gt;Here's a script that can recreate SqlDictionary records in AX 2009 for any field of (nonsystem) table - in case such record is missing for some reason while the field exists in the database. I used this script during an upgrade to AX 2009 last year. Some fields in the standard application got the same IDs as fields added during customizations and they had different base types, of course, and it seemed impossible to fix the situation using standard means. The script might be of interest to those who wants to know the relations between the SqlDictionary and the AOT.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on" class="axapta"&gt;&lt;code&gt;&lt;pre&gt;#macrolib.DictField
// This type is used instead of Types::Int64 for fields of type
// RecId/RefRecId/createdTransactionId/modifiedTransactionId
#define.RecIdBaseType   (49)
// For nonsystem fields of type UtcDateTime an additional field is created
// that holds the actual time zone in which the value has been set
#define.TZIDsuffix      ('_TZID')          

SqlDictionary   sqlDict;
SysdictType     dictType;
DictTable       dictTable;
DictField       dictField;
ArrayIdx        arrIdx;
Counter         numOfSqlFields; // number of records created in SqlDictionary
fieldName       fieldName;
fieldId         fieldId;
tableId         tableId = tablenum(TheTable2Fix);  // TARGET

boolean processTableField(
    DictField   _dictField,
    ArrayIdx    _arrIdx,
    boolean     _isTzIdField = false
    )
{
    ArrayIdx    dictArrIdx;
    str         infoName;       // this field name is for messages only
    FieldName   sqlName;
    boolean     ret;
    ;
    if (_isTzIdField)
    {
        if (    _dictField.baseType()   != Types::UtcDateTime
            ||  _dictField.id()         == fieldnum(Common, createdDateTime)
            ||  _dictField.id()         == fieldnum(Common, modifiedDateTime)
           )
        {
            throw error(Error::wrongUseOfFunction(funcname()));
        }
        dictArrIdx  = _dictField.arraySize() + _arrIdx;
        sqlName     = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1);
        infoName    = _dictField.name() + #TZIDsuffix;
    }
    else
    {
        dictArrIdx  = _arrIdx;
        sqlName     = _dictField.name(DbBackend::Sql, _arrIdx);
        infoName    = _dictField.name();
    }
    select firstonly sqlDict
        where   sqlDict.tabId   == _dictField.tableid()
            &amp;amp;&amp;amp;  sqlDict.fieldId == _dictField.id()
            &amp;amp;&amp;amp;  sqlDict.array   == dictArrIdx
                ;
    if (!sqlDict)
    {
        sqlDict.clear();
        sqlDict.initValue();
        sqlDict.tabId           = _dictField.tableid();
        sqlDict.fieldId         = _dictField.id();
        sqlDict.array           = dictArrIdx;
        sqlDict.name            = strupr(_dictField.name(DbBackend::Native, _arrIdx));
        sqlDict.sqlName         = sqlName;
        dictType                = new SysDictType(_dictField.typeId());
        if (_isTzIdField)
        {
            sqlDict.fieldType   = Types::Integer;
        }
        else
        if (        _dictField.id()     == fieldnum(Common, RecId)
            ||      _dictField.id()     == fieldnum(Common, createdTransactionId)
            ||      _dictField.id()     == fieldnum(Common, modifiedTransactionId)
            ||      _dictField.typeId() == extendedtypenum(RecId)
            ||      _dictField.typeId() == extendedtypenum(RefRecId)
            ||  (   dictType
                &amp;amp;&amp;amp;  dictType.isExtending(extendedtypenum(RecId))
                )
           )
        {
            // This type is used instead of Types::Int64 for fields of type
            // RecId/RefRecId/createdTransactionId/modifiedTransactionId
            sqlDict.fieldType   = #RecIdBaseType;
        }
        else
        {
            sqlDict.fieldType   = _dictField.baseType();
        }
        sqlDict.strSize         = _dictField.stringLen();
        sqlDict.shadow          = bitTest(_dictField.flags(), #DBF_SHADOW);
        sqlDict.rightJustify    = bitTest(_dictField.flags(), #DBF_RIGHT);
        sqlDict.flags           = sqlDict.shadow;   // not _dictField.flags() at all!
        sqlDict.nullable        =   _dictField.baseType() == Types::Container
                                ||  _dictField.baseType() == Types::VarString
                                    ;
        if (sqlDict.validateWrite())
        {
            sqlDict.insert();
            ret = true;
            info(strfmt(@"Created record for field %1.%2%3",
                        dictTable.name(), infoName,
                        _dictField.arraySize() &amp;gt; 1 ? strfmt(@"[%1]", _arrIdx) : ''));
            // for all nonsystem UtcDateTime fields we also create a related TZID-field
            if (   !_isTzIdField
                &amp;amp;&amp;amp;  _dictField.baseType()   == Types::UtcDateTime
                &amp;amp;&amp;amp;  _dictField.id()         != fieldnum(Common, createdDateTime)
                &amp;amp;&amp;amp;  _dictField.id()         != fieldnum(Common, modifiedDateTime)
               )
            {
                processTableField(_dictField, _arrIdx, true);
            }
        }
        else
        {
            ret = checkFailed(strfmt(@"%1 record for %2.%3 was not created",
                                     tablestr(SqlDictionary), dictTable.name(), infoName));
        }
    }
    return ret;
}
;
dictTable = new DictTable(tableId);
if (!dictTable)
{
    throw error(strfmt(@"Failed to create %1 for '%2' (%3)",
                classstr(DictTable), tableid2name(tableId), tableId));
}
if (dictTable.isSystemTable())
{
    throw error(strfmt(@"'%1' is a system table, no way...", dictTable.name()));
}
if (!dictTable.isSql())
{
    throw error(strfmt(@"Table '%1' should not be in DB", dictTable.name()));
}
for (fieldId = dictTable.fieldNext(0); fieldId; fieldId = dictTable.fieldNext(fieldId))
{
    dictField = dictTable.fieldObject(fieldId);
    if (dictField &amp;amp;&amp;amp; dictField.isSql())
    {
        fieldName = dictField.name();
        for (arrIdx = 1; arrIdx &amp;lt;= dictField.arraySize(); arrIdx++)
        {
            numOfSqlFields++;
            processTableField(dictField, arrIdx);
        }
    }
}
select firstonly sqlDict
    where   sqlDict.tabId   == tableId
        &amp;amp;&amp;amp;  sqlDict.fieldId == 0
            ;
if (!sqlDict)
{
    sqlDict.clear();
    sqlDict.initValue();
    sqlDict.tabId       = tableId;
    sqlDict.name        = strupr(dictTable.name());
    sqlDict.sqlName     = dictTable.name(DbBackend::Sql);
    sqlDict.strSize     = numOfSqlFields;       // for the table "header" - num of fields 
    sqlDict.flags       = dictTable.isView();   // that's the way it is
    sqlDict.insert();
    info(strfmt(@"Created record for table %1", dictTable.name()));
}&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-6482326858261908386?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/6482326858261908386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=6482326858261908386' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/6482326858261908386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/6482326858261908386'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2011/09/script-to-recreate-sqldictionary.html' title='Script to recreate SqlDictionary records in AX 2009'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-9188796111200412610</id><published>2011-05-04T00:09:00.002+04:00</published><updated>2011-05-04T10:28:04.427+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='Best Practices'/><title type='text'>Best Practice checks for objects on the current layer only</title><content type='html'>&lt;p&gt;There are many Best Practice checks implemented in the standard AX application, which can help you to verify and improve your (or someone’s) source code and other application artifacts. But it should be noted that the execution of Best Practice checks:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Dramatically degrades compilation speed;&lt;/li&gt;
&lt;li&gt;Is pointless for application objects that are not present on the current application layer.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;So I decided to make Best Practice checks work only for objects on the current application layer. It turned out to be pretty easy to implement - all you need is add a new enum field to turn on or off this new behavior, for instance, to the SysBPParameters table and customize the SysBPCheck class a little.&lt;br /&gt;
&lt;code&gt;&lt;pre&gt;&lt;span style="color:blue"&gt;private void&lt;/span&gt; doTreeNode(TreeNode _treeNode)
{
    TreeNodeTraverser   treeNodeTraverser;
    SysBPCheckBase      sysBPCheckBase;
    TreeNode            treeNodeToRelease;
    boolean             checkChildren = &lt;span style="color:blue"&gt;true&lt;/span&gt;;
    TreeNodePath        parentPath;
    &lt;span style="color:blue"&gt;int&lt;/span&gt;                 infologLines;

    Map map = &lt;span style="color:blue"&gt;new&lt;/span&gt; Map(Types::Integer, Types::Integer);
    MapEnumerator enum;
    &lt;span style="color:blue"&gt;int&lt;/span&gt; length;
    ;
    treeNodeToRelease = &lt;span style="color:blue"&gt;null&lt;/span&gt;;
&lt;span style="color:green"&gt;//  if (_treeNode)                              //-gl00mie, 14.02.2011&lt;/span&gt;
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (this.mustCheckTreeNode(_treeNode))      &lt;span style="color:green"&gt;//+gl00mie, 14.02.2011&lt;/span&gt;
    {
        treeNodeTraverser = &lt;span style="color:blue"&gt;new&lt;/span&gt; TreeNodeTraverser(_treeNode);

        &lt;span style="color:blue"&gt;while&lt;/span&gt; (treeNodeTraverser.next()) &lt;span style="color:green"&gt;// Check Best Practices&lt;/span&gt;
        {
            &lt;span style="color:blue"&gt;if&lt;/span&gt; (!sysCompilerOutput)
            {
                setprefix(treeNodeTraverser.infologPrefix());
            }

            treeNode = treeNodeTraverser.currentNode();
            &lt;span style="color:blue"&gt;if&lt;/span&gt;(treeNode)
            {
                length=strlen(treeNode.treeNodePath());
            }
            &lt;span style="color:blue"&gt;else&lt;/span&gt;
            {
                length=0;
            }
            &lt;span style="color:blue"&gt;if&lt;/span&gt; (treeNodeToRelease &amp;amp;&amp;amp; SysTreeNode::isApplObject(treeNode) &amp;amp;&amp;amp;
                (treeNodeToRelease.sysNodeType()!= #NT_DBVIEW || 
                (treeNode &amp;amp;&amp;amp; treeNode.sysNodeType()!= #NT_QE) ||
                length &lt; strlen(treeNodeToRelease.treeNodePath()) ||
                substr(treeNode.treeNodePath(),1,strlen(treeNodeToRelease.treeNodePath())) !=
                treeNodeToRelease.treeNodePath()))
            {
                treeNodeToRelease.treeNodeRelease();
                treeNodeToRelease = &lt;span style="color:blue"&gt;null&lt;/span&gt;;
            }
&lt;span style="color:green"&gt;//          if (checkChildren ||                        //-gl00mie, 14.02.2011&lt;/span&gt;
            &lt;span style="color:blue"&gt;if&lt;/span&gt; ((checkChildren ||                       &lt;span style="color:green"&gt;//+gl00mie, 14.02.2011&lt;/span&gt;
                substr(treeNode.treeNodePath(),1,strlen(parentPath)) != parentPath)
                &amp;amp;&amp;amp;  this.mustCheckTreeNode( treeNode )) &lt;span style="color:green"&gt;//+gl00mie, 14.02.2011&lt;/span&gt;
            {
                sysBPCheckBase = this.getSysBpCheckBase(treeNode);
                sysBPCheckBase.parmSysBPCheck(this);
                &lt;span style="color:green"&gt;// ...&lt;/span&gt;&lt;/pre&gt;&lt;/code&gt;And here's the new SysBPCheck method that decides whether to run BP checks for a TreeNode:&lt;code&gt;&lt;pre&gt;&lt;span style="color:green"&gt;// gl00mie, 14.02.2011&lt;/span&gt;
&lt;span style="color:blue"&gt;protected&lt;/span&gt; boolean mustCheckTreeNode(TreeNode _treeNode)
{
    UtilEntryLevel  applObjectLayer;
    UtilEntryLevel  currentAOLayer;
    boolean         ret;
    ;
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (_treeNode)
    {
        ret = &lt;span style="color:blue"&gt;true&lt;/span&gt;;
        currentAOLayer = currentAOLayer();
        &lt;span style="color:green"&gt;// if you develop on the sys layer then this modification is obviously pointless&lt;/span&gt;
        &lt;span style="color:blue"&gt;if&lt;/span&gt; (currentAOLayer &amp;gt; UtilEntryLevel::sys)
        {
            applObjectLayer = _treeNode.applObjectLayer();
            &lt;span style="color:blue"&gt;if&lt;/span&gt; (    applObjectLayer &amp;lt; currentAOLayer
                &amp;amp;&amp;amp;  enum2str(_treeNode.applObjectType()) != ''
               )
            {
                &lt;span style="color:green"&gt;// depending on your setup we can skip BP checks for objects situated on lower layers&lt;/span&gt;
                &lt;span style="color:green"&gt;// NB! you'll have to add this new field to a table and to a form by yourself&lt;/span&gt;
                ret = !SysBPParameters::find().CheckBPForCurrentAOLayerOnly;
            }
        }
    }
    &lt;span style="color:blue"&gt;return&lt;/span&gt; ret;
}&lt;/pre&gt;&lt;/code&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-9188796111200412610?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/9188796111200412610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=9188796111200412610' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/9188796111200412610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/9188796111200412610'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2011/05/best-practice-checks-for-objects-on.html' title='Best Practice checks for objects on the current layer only'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-1007912115399667684</id><published>2011-04-30T11:33:00.008+04:00</published><updated>2011-04-30T14:04:32.176+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='Kernel'/><category scheme='http://www.blogger.com/atom/ns#' term='Query hints'/><category scheme='http://www.blogger.com/atom/ns#' term='ForceNestedLoop'/><category scheme='http://www.blogger.com/atom/ns#' term='Query'/><category scheme='http://www.blogger.com/atom/ns#' term='ForceSelectOrder'/><title type='text'>Query forceSelectOrder and forceNestedLoop hints are not saved when you pack and unpack a query</title><content type='html'>&lt;p&gt;In some situations it's useful to take a query from a form or a calling class, transform this query somehow and use in another part of the application. For instance, you might take a query from a form datasource to process all the records queried by the form, or take a query and transform it to count records that can be fetched by the query (what SysQuery::countTotal() does), or just pass a query object from one tier to another (client to server). In all those cases you would do something like this:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;Query newQuery = &lt;span style="color:blue"&gt;new&lt;/span&gt; Query(oldQuery.pack());&lt;/pre&gt;&lt;/code&gt;&lt;p&gt;Yes, I know that in case of a form datasource query you should also take care of dynalinks but this is not the point. The point is that you might not get the same execution plan for the resulting query because forceSelectOrder and forceNestedLoop hints are not preserved during query pack/unpack. This is true at least for the AX 2009 SP1 RU7 kernel (5.0.1500.4570) so for now if you use these hints you should not rely solely on the kernel when you copy a query. For this purpose you can create a SysQuery method like this:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;&lt;span style="color:green"&gt;/// &amp;lt;summary&amp;gt;
/// Copies a query including a one with dynalinks and hints
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name="_q"&amp;gt;
/// source query
/// &amp;lt;/param&amp;gt;
/// &amp;lt;param name="_copyHints"&amp;gt;
/// must the forceSelectOrder and forceNestedLoop hints also be copied
/// &amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span style="color:blue"&gt;public client server static&lt;/span&gt; Query copy(
    Query   _q,
    boolean _copyHints = &lt;span style="color:blue"&gt;true&lt;/span&gt;
    )
{
    QueryBuildDataSource    qbdsOld;
    QueryBuildDataSource    qbdsNew;
    QueryBuildDynalink      qbdl;
    QueryBuildRange         qbr;
    Query                   ret;
    &lt;span style="color:blue"&gt;str&lt;/span&gt;                     sq;
    Counter                 n;
    ;
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (!(_q &lt;span style="color:blue"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _q.dataSourceCount()))
    {
        &lt;span style="color:blue"&gt;throw&lt;/span&gt; error(Error::wrongUseOfFunction(funcname()));
    }
    ret = &lt;span style="color:blue"&gt;new&lt;/span&gt; Query(_q.pack(&lt;span style="color:blue"&gt;false&lt;/span&gt;));
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (_copyHints)
    {
        &lt;span style="color:green"&gt;// Query methods forceSelectOrder() and forceNestedLoop() have mandatory parameters
        // so you cannot use these methods as ordinary properties to find out current values&lt;/span&gt;
        sq = _q.dataSourceNo(1).toString();
        &lt;span style="color:blue"&gt;if&lt;/span&gt; (match(&lt;span style="color:red"&gt;@"^SELECT WITH.* SELECT_ORDER[ ,]"&lt;/span&gt;, sq))
        {
            ret.forceSelectOrder(&lt;span style="color:blue"&gt;true&lt;/span&gt;);
        }
        &lt;span style="color:blue"&gt;if&lt;/span&gt; (match(&lt;span style="color:red"&gt;@"^SELECT WITH.* NESTED_LOOP[ ,]"&lt;/span&gt;, sq))
        {
            ret.forceNestedLoop(&lt;span style="color:blue"&gt;true&lt;/span&gt;);
        }
    }
    qbdsNew = ret.dataSourceNo(1);
    qbdsOld = _q.dataSourceNo(1);
    &lt;span style="color:blue"&gt;for&lt;/span&gt; (n = 1; n &lt;span style="color:blue"&gt;&amp;lt;=&lt;/span&gt; qbdsOld.dynalinkCount(); n++)
    {
        qbdl = qbdsOld.dynalink(n);
        &lt;span style="color:green"&gt;// clear all existing ranges for this field&lt;/span&gt;
        &lt;span style="color:blue"&gt;while&lt;/span&gt; (qbdsNew.findRange(qbdl.field()))
        {
            qbdsNew.clearRange(qbdl.field());
        }
        &lt;span style="color:green"&gt;// set the range value by the current dynalinked cursor field value&lt;/span&gt;
        qbr = qbdsNew.addRange(qbdl.field());
        qbr.value(queryValue(qbdl.cursor().(qbdl.dynamicField())));
        qbr.status(RangeStatus::Locked);
    }
    &lt;span style="color:blue"&gt;return&lt;/span&gt; ret;
}&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-1007912115399667684?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/1007912115399667684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=1007912115399667684' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/1007912115399667684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/1007912115399667684'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2011/04/query-forceselectorder-and.html' title='Query forceSelectOrder and forceNestedLoop hints are not saved when you pack and unpack a query'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-6868102213482369789</id><published>2011-04-30T00:42:00.010+04:00</published><updated>2011-04-30T14:08:07.480+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='Kernel'/><title type='text'>AX 2009 client freezes and doesn't redraw its window during long operations (FIX!)</title><content type='html'>&lt;p&gt;Hey, it's been a long time since the last post!.. Anyway, when you run a long lasting operation in AX 2009, after about 5 seconds a client window would blink - and bam! it's in the Not Responding state and doesn't redraw anything. This behavior has been annoying me since I've first seen AX 2009 but now there seems to be a solution! I've found it accidentally in &lt;a href="http://blogs.msdn.com/b/emeadaxsupport/archive/2009/06/18/error-executing-code-the-method-has-been-called-with-an-invalid-number-of-parameters.aspx"&gt;this post&lt;/a&gt; of the &lt;a href="http://blogs.msdn.com/b/emeadaxsupport/"&gt;EMEA Dynamics AX Support blog&lt;/a&gt;. Here what they say:&lt;/p&gt;&lt;blockquote&gt;...One reason for running into this issue &lt;span style="font-style:italic;"&gt;can be&lt;/span&gt; if the Windows Operating System is replacing the Dynamics AX application window by a &lt;span style="font-style:italic;"&gt;ghost window&lt;/span&gt;. When Dynamics AX starts a lengthy COM operation, it is not responding to messages sent by the Windows Operating System in time. So Windows supposes Dynamics AX has stopped responding. When this happens the Dynamics AX application &lt;span style="font-weight:bold;"&gt;window is replaced by a ghost window&lt;/span&gt; until Dynamics AX resumes. Window ghosting is a nice feature that allows the user to minimize, move or close the main window even if the application is not responding. You can easily identify a ghost window as it shows &lt;span style="font-style:italic;"&gt;(Not responding)&lt;/span&gt; in the window title.&lt;/blockquote&gt;&lt;p&gt;One of the proposed solutions is to run a client in the WinXP SP2 compatibility mode, but it requires you to create a special shortcut for it and run every deployed client with such a shortcut, which is not always convenient. But theres's also another way.&lt;/p&gt;&lt;p&gt;The Win32 API function &lt;a href="http://msdn.microsoft.com/en-us/library/ms648415.aspx"&gt;DisableProcessWindowsGhosting&lt;/a&gt; can disable windows ghosting feature for the calling process and you can customize your Dynamics AX application to make the client call this function on startup. All you have to do is add a new method to the WinAPI class:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;&lt;span style="color:green;"&gt;/// &amp;lt;summary&amp;gt;
/// Call user32.DisableProcessWindowsGhosting
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;remarks&amp;gt;
/// Disables the window ghosting feature for the calling GUI process. Window
/// ghosting is a Windows Manager feature that lets the user minimize, move,
/// or close the main window of an application that is not responding.
/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;span style="color:blue;"&gt;public static client void&lt;/span&gt; disableProcessWindowsGhosting()
{
    DLL         dll     = &lt;span style="color:blue;"&gt;new&lt;/span&gt; DLL(#UserDLL);
    DLLFunction dllFunc = &lt;span style="color:blue;"&gt;new&lt;/span&gt; DLLFunction(dll, &lt;span style="color:red;"&gt;@"DisableProcessWindowsGhosting"&lt;/span&gt;);
    ;
    dllFunc.returns(ExtTypes::void);
    dllFunc.arg();

    dllFunc.call();
}&lt;/pre&gt;&lt;/code&gt;&lt;p&gt;And then call it say from info.startupPost():&lt;/p&gt;&lt;code&gt;&lt;pre&gt;&lt;span style="color:green;"&gt;/*
No SYS code must exist in this method
*/&lt;/span&gt;
&lt;span style="color:blue;"&gt;void&lt;/span&gt; startupPost()
{
    &lt;span style="color:blue;"&gt;if&lt;/span&gt; (clientKind() == ClientType::Client)
    {
        &lt;span style="color:green;"&gt;// BP deviation documented&lt;/span&gt;
        WinAPI::disableProcessWindowsGhosting();
    }
}&lt;/pre&gt;&lt;/code&gt;&lt;p&gt;This Win32 API function is supported in at least WinXP SP3 and Windows Server 2003 SP2. It's a pity windows ghosting is not disabled by the client kernel itself.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-6868102213482369789?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/6868102213482369789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=6868102213482369789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/6868102213482369789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/6868102213482369789'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2011/04/ax-2009-client-freezes-and-doesnt.html' title='AX 2009 client freezes and doesn&apos;t redraw its window during long operations (FIX!)'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-4938528277399851064</id><published>2008-10-29T23:48:00.004+03:00</published><updated>2008-10-30T00:01:47.717+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Book'/><category scheme='http://www.blogger.com/atom/ns#' term='MOSS 2007'/><title type='text'>Book review: Microsoft® Office SharePoint® Server 2007 Best Practices</title><content type='html'>Microsoft Press has started a very interesting "Best Practices" series with a book dedicated to a product sometimes called &lt;a href="http://blogs.zdnet.com/microsoft/?p=327"&gt;the next big ‘operating system’ from Microsoft&lt;/a&gt;. First off this is not a book for those just starting to learn how to install, configure, and administrate SharePoint Server 2007; there are &lt;a href="http://www.microsoft.com/mspress/books/9537.aspx"&gt;some other&lt;/a&gt; books suggested for reading in this case. What is valuable in this book is that best practices presented in it (except for those few based on hard-coded limits in the product) are the essence of many, many discussions, analyzes, resolved support issues and numerous implementation projects undertaken by the authors. 
The book consists of four parts: Planning and Designing, Building, Deploying, and Operating. And although there are plenty of possible deployment scenarios for a SharePoint Server 2007, throughout each part of the book the authors present common threads of best practices that can be utilized across a number of different deployment scenarios. Your solid knowledge and understanding of these best practices built on the proper use of Microsoft SharePoint Products and Technologies will eventually lead to better and wiser design decisions: it will help you to efficiently map the features and functionalities in Microsoft Office SharePoint Server 2007 to your specific environment, culture, requirements and deployment.
But let's take a look at the other side of technologies: not only they empower individual information workers but also bring a significant change in a corporate environment and power dynamics within an organization. It takes more than a creative idea or a unique skill to help solve a significant problem - you need allies within your corporation, and the more influence they have at the enterprise level of the corporation, the more likely it is that your deployment will be successful. Another point essential to your SharePoint Server 2007 implementation is the development of business requirements: clearly articulated, measurable, agreed upon and written down for the reference. Also a strong governance for a deployment is required to mitigate inevitable conflicts.
As the authors mention, &lt;blockquote&gt;Yet changes that can cause conflict that result in power wars, if not anticipated, discussed, and managed properly, can kill your deployment.&lt;/blockquote&gt; Sad but true, and what is especially valuable in the book is that besides diving into technical details the authors also discuss these less-talked-about sides of a deployment. Think about the power dynamics in your organization, thoroughly define business requirements prior to deployment and implement proper governance for the deployment itself. These are best practices at least just as important as those concerning security policy or an information architecture development, and you will be presented with an excess discussion of these best practices as well as numerous examples, notes from the field and lessons learned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-4938528277399851064?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.microsoft.com/mspress/books/12197.aspx' title='Book review: Microsoft® Office SharePoint® Server 2007 Best Practices'/><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/4938528277399851064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=4938528277399851064' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/4938528277399851064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/4938528277399851064'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2008/10/book-review-microsoft-office-sharepoint.html' title='Book review: Microsoft® Office SharePoint® Server 2007 Best Practices'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-3325545704355396620</id><published>2008-09-30T15:48:00.008+04:00</published><updated>2009-08-15T00:44:23.822+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX3'/><category scheme='http://www.blogger.com/atom/ns#' term='RPC'/><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='AOCP'/><category scheme='http://www.blogger.com/atom/ns#' term='AX4'/><title type='text'>View AOCP/RPC Interface Version used by an Arbitrary DAX Binary</title><content type='html'>First off a little bit of theory. Dynamics AX is a client-server application and utilizes a special protocol/interface for client-server communication. Namely versions prior to 4.0 used AOCP (Axapta Object Communication Protocol) implemented over TCP/IP and versions 4.0 and above have moved to an &lt;a href="http://msdn.microsoft.com/en-us/library/ms691207.aspx"&gt;RPC interface&lt;/a&gt;. The client-server communication details inevitably change over time as kernel updates are released and obviously for robust Dynamics AX kernel operation it's vital to make sure that a client and a server &lt;i&gt;understand&lt;/i&gt; each other precisely i.e. use the very same «dialect» of the communication protocol/interface. Hence an internal communication protocol/&lt;a href="http://msdn.microsoft.com/en-us/library/aa378963.aspx"&gt;interface versioning&lt;/a&gt; is used in Dynamics AX kernel to find out if an arbitrary client can safely connect to and communicate with an arbitrary server.
Those of you who has worked with Axapta 3 might remember that when you start a client with a kernel build number different from the AOS' kernel build number then the client most likely fails to connect to the AOS and complains about «Internal revision mismatch in Axapta Object Communication Protocol»; then it &lt;b&gt;suggests&lt;/b&gt; to use the same kernel build for both client and Object Server. Why is it just a suggestion? First, the client has no idea if the server's kernel build matches it's own, and vice versa the AOS has no idea if a client attempting to connect has the same kernel build number as the AOS. Second, Dynamics AX clients and servers with non-matching kernel build numbers &lt;b&gt;can&lt;/b&gt; connect and work with each other in case they use the same version of a communication protocol/interface.
The first statement might look arguable. If you look in the &lt;b&gt;User log&lt;/b&gt; (&lt;u&gt;Administration&lt;/u&gt;/&lt;u&gt;Inquiries&lt;/u&gt;/&lt;u&gt;User log&lt;/u&gt;) you can find there some technical information about a user's session such as a client computer name, a client type and some kind of build number. Aha! So Object Server &lt;i&gt;does&lt;/i&gt; know exactly the client's kernel build number, right? Wrong. A &lt;a href="http://msdn.microsoft.com/en-us/library/aa879188.aspx"&gt;SysUserLog&lt;/a&gt; record which represents User log data is filled right in &lt;code&gt;SysUserLog.insert()&lt;/code&gt; method. And while &lt;code&gt;SysUserLog.Computername&lt;/code&gt; and &lt;code&gt;SysUserLog.ClientType&lt;/code&gt; field values represent corresponding &lt;code&gt;xSession&lt;/code&gt; object properties, &lt;code&gt;SysUserLog.BuildNum&lt;/code&gt; field is initialized with a value returned by &lt;code&gt;xInfo::buildNo()&lt;/code&gt; static method which has no explicit &lt;code&gt;client&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/aa891619.aspx"&gt;modifiers&lt;/a&gt;. In 3-tier environment a table variable is generally located on the server side, hence &lt;code&gt;SysUserLog.insert()&lt;/code&gt; and &lt;code&gt;xInfo::buildNo()&lt;/code&gt; are executed &lt;i&gt;on the server side&lt;/i&gt; and &lt;code&gt;SysUserLog.BuildNum&lt;/code&gt; field receives &lt;b&gt;server's&lt;/b&gt; kernel build number. So, once again, an AOS has no idea if a client attempting to connect to it has the same kernel build number or not.
So when it comes to client/server interoperability, Dynamics AX kernel relies solely on the communication protocol/interface version, not kernel build numbers. And if that version is the same in client an server binaries then those binaries can be safely used together. But how can you know what communication protocol/interface version does an arbitrary Dynamics AX kernel binary use? Well, here is a utility for you: &lt;a href="http://cid-91b2ae372817b3bf.skydrive.live.com/self.aspx/public/dax-aocp-version.zip?download"&gt;viewer for Dynamics AX AOCP/RPC interface version&lt;/a&gt; used by an given DAX kernel binary. Prior to a kernel binary file name you can pass these command line arguments:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-nosigcheck&lt;/code&gt; - omit Authenticode verification (useful for some legacy kernel builds that miss the Authenticode digital signature)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-debug&lt;/code&gt; - output additional technical information about the binary and print more detailed error messages if any&lt;/li&gt;
&lt;/ul&gt;
You now can easily find out (and check it out) that Dynamics AX 4.0 SP1 and SP2 binaries work together as they use the same RPC Interface version; and so do Dynamics AX 2009 TAP3 and RTM binaries...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-3325545704355396620?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/3325545704355396620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=3325545704355396620' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/3325545704355396620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/3325545704355396620'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2008/09/view-aocprpc-interface-version-used-by.html' title='View AOCP/RPC Interface Version used by an Arbitrary DAX Binary'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-5220740321802079624</id><published>2008-08-13T15:10:00.005+04:00</published><updated>2008-10-02T17:25:33.269+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AX2009'/><category scheme='http://www.blogger.com/atom/ns#' term='Documentation'/><title type='text'>Dynamics AX 2009 Guides and White Papers</title><content type='html'>It's interesting to note that Microsoft has released lots of documentation on Ms Dynamics AX 2009:
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=76a651a9-88e7-4e2d-b0d2-8622cea5e7e7"&gt;Microsoft Dynamics AX 2009 White Paper: Journal Batch Posting&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=04add4d1-df50-4be3-8b4d-de72fe802398"&gt;Microsoft Dynamics AX 2009 White Paper: Shipping Carrier Interface&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=ad185a6c-f5eb-43ff-9d25-df21024f81be"&gt;Microsoft Dynamics AX 2009 White Paper: Setting Up Intercompany for Intracompany Direct Delivery&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=92db8fd7-50d7-495e-bab8-b97144d762b2"&gt;Microsoft Dynamics AX 2009 White Paper: Integration of Workflow Capabilities into Journals&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=2d5894f7-27fb-449c-a5f2-a2109af6cff2"&gt;Microsoft Dynamics AX 2009 White Paper: Credit Card Processing&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=b930378b-3116-44e3-a001-b35ccde90d12"&gt;Microsoft Dynamics AX 2009 White Paper: Posting to the Ledger&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=f6ea2586-e397-4e93-8297-2b40a6a417ec"&gt;Microsoft Dynamics AX 2009 White Paper: Standard Cost Conversion Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=78e6eed5-f5db-458d-a2e8-80bfd607ee61"&gt;Microsoft Dynamics AX 2009 White Paper: Multisite Activation&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=fc750f30-39f0-411a-bf65-df87742fdb67"&gt;Microsoft Dynamics AX 2009 White Paper: How to Write Data Upgrade Scripts&lt;/a&gt;
and also:
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=6a685df3-912d-4545-b990-cd2283c159fb"&gt;Microsoft Dynamics AX 2009 Business Intelligence Cube Reference Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e08d8cf4-341d-49b7-b869-020d315990db"&gt;Microsoft Dynamics AX 2009 Security Hardening Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e7bfb7a8-eb99-4171-b75a-d9a37067bc19"&gt;Microsoft Dynamics AX 2009 Server and Database Administration Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=60aac6d7-9489-4f15-880a-edc149fab092"&gt;Microsoft Dynamics AX 2009 Enterprise Portal Administration Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=4b681c44-d944-4b3f-b846-1e2a025569fd"&gt;Microsoft Dynamics AX 2009 Installation Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=466c7165-4349-4903-8717-27c924b6f58a"&gt;Microsoft Dynamics AX 2009 Implementation Guide&lt;/a&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=d9dae144-55b4-432d-b11e-f65ddb2d1b9c"&gt;Microsoft Dynamics AX 2009 Upgrade Guide&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-5220740321802079624?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/5220740321802079624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=5220740321802079624' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/5220740321802079624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/5220740321802079624'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2008/08/dynamics-ax-2009-guides-and-white.html' title='Dynamics AX 2009 Guides and White Papers'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-8246304810459222425</id><published>2007-10-30T22:36:00.033+03:00</published><updated>2007-12-20T09:58:51.671+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AOS'/><category scheme='http://www.blogger.com/atom/ns#' term='AX4'/><title type='text'>Run DAX4 AOS as a console application</title><content type='html'>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 &lt;code&gt;&amp;lt;any_single_character&amp;gt;console&lt;/code&gt; parameter before the configuration number, e.g. &lt;blockquote&gt;&lt;br /&gt;ax32serv.exe /console 01&lt;/blockquote&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-8246304810459222425?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/8246304810459222425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=8246304810459222425' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/8246304810459222425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/8246304810459222425'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2007/10/run-dax4-aos-as-console-application.html' title='Run DAX4 AOS as a console application'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-3555358778629975739</id><published>2007-07-10T10:55:00.050+04:00</published><updated>2007-07-10T22:38:50.080+04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SysFileDeployment'/><category scheme='http://www.blogger.com/atom/ns#' term='AX4'/><title type='text'>AX4 SysFileDeployment Fix</title><content type='html'>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 &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=5e050494-1613-4b3a-9363-d69d60c56877"&gt;Writing Secure X++ Code&lt;/a&gt;.
&lt;blockquote&gt;
&lt;span style="font-weight:bold;"&gt;Implementing Code Access Security&lt;/span&gt;
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 &lt;code&gt;demand()&lt;/code&gt; method on that class
2. Each API consumer must explicitly request permission to invoke a secured dangerous API by calling the &lt;code&gt;assert()&lt;/code&gt; method on the permission class.
Application code will break unless both of these steps are completed.
&lt;/blockquote&gt;
And everything is ok with the &lt;code&gt;demand()&lt;/code&gt; calls in the WinAPIServer class whereas SysFileDeployment* classes lack the &lt;code&gt;assert()&lt;/code&gt; calls. Probably they were not tested thoroughly or something… To be precise, here is what I've found:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SysFileDeploymentFile.serverVersion()&lt;/code&gt; lacks corresponding &lt;code&gt;assert()&lt;/code&gt; call before &lt;code&gt;WinAPIServer::getFileModifiedDate()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SysFileDeployment.getServerFileTimeAccessed()/Modified()/Created()&lt;/code&gt; lack corresponding &lt;code&gt;assert()&lt;/code&gt; calls before &lt;code&gt;WinAPIServer::getFileTime()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SysFileDeployment.isNameValid()&lt;/code&gt; splits full file name via &lt;code&gt;fileNameSplit()&lt;/code&gt; and after that adds a redundant dot between the name and the extension (&lt;code&gt;fileNameSplit()&lt;/code&gt; already returns file extension with a leading dot) - as a result &lt;code&gt;isNameValid()&lt;/code&gt; &lt;span style="font-weight:bold;"&gt;always&lt;/span&gt; returns &lt;code&gt;false&lt;/code&gt; which in its place causes corresponding &lt;code&gt;assert()&lt;/code&gt; calls not to happen;&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;SysFileDeployment.sourcePath()&lt;/code&gt; returns &lt;code&gt;xInfo::directory(DirectoryType::Include)&lt;/code&gt; 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 &lt;code&gt;SysFileDeployment.sourcePath()&lt;/code&gt; is called on both tiers - and that completely ruins all the program logic&lt;/li&gt;
&lt;/ul&gt;
You can download a small fix from &lt;a href="http://www.axaptapedia.com/Image:AX4_SysFileDeploymentFIX.zip"&gt;axaptapedia.com&lt;/a&gt; to make SysFileDeployment work again. Microsoft claims there's already a &lt;a href="https://mbs.microsoft.com/knowledgebase/KBDisplay.aspx?scid=kb;en-us;938404"&gt;hot fix&lt;/a&gt; with a description "File deployment error FileIOPermission failed", so there's a hope we will see this fixed in AX4 SP2...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-3555358778629975739?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/3555358778629975739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=3555358778629975739' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/3555358778629975739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/3555358778629975739'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2007/07/ax4-sysfiledeployment-fix.html' title='AX4 SysFileDeployment Fix'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-116662878516725108</id><published>2006-12-20T11:29:00.001+03:00</published><updated>2011-04-30T14:26:49.089+04:00</updated><title type='text'>Read Excel table via ADO</title><content type='html'>&lt;p&gt;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 &lt;i&gt;fast&lt;/i&gt;, 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.&lt;p&gt;&lt;p&gt;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 &lt;code&gt;"select * from [sheetname]"&lt;/code&gt; Recordset's command.&lt;/p&gt;&lt;p&gt;A typical scenario would be something like this:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;Counter         cnTotal = 0;
ItemId          itemId;
ItemName        itemName;
AmountCur       price;
Filename        strFilename;
&lt;span style="color:blue"&gt;container&lt;/span&gt;       conSheets;
ExcelImportADO  xlImport;
;
strFilename = &lt;span style="color:red"&gt;@&amp;quot;c:\import.xls&amp;quot;&lt;/span&gt;;
xlImport    = &lt;span style="color:blue"&gt;new&lt;/span&gt; ExcelImportADO(strFilename);
&lt;span style="color:blue"&gt;try&lt;/span&gt;
{
    &lt;span style="color:green"&gt;// open the first sheet by default&lt;/span&gt;
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (!xlImport.openFile())
    {
        &lt;span style="color:blue"&gt;throw&lt;/span&gt; error(strfmt(&lt;span style="color:red"&gt;&amp;quot;Error opening Excel file '%1'&amp;quot;&lt;/span&gt;, strFilename));
    }
    &lt;span style="color:blue"&gt;if&lt;/span&gt; (xlImport.getFieldsCount() &amp;lt; 3)
    {
        &lt;span style="color:blue"&gt;throw&lt;/span&gt; error(strfmt(&lt;span style="color:red"&gt;&amp;quot;Too few columns in the recordset: found %1, expected at least %2&amp;quot;&lt;/span&gt;,
                           xlImport.getFieldsCount(), 3));
    }
    &lt;span style="color:blue"&gt;while&lt;/span&gt; (!xlImport.eof())
    {
        itemId   = xlImport.getFieldValue(1);
        itemName = xlImport.getFieldValue(&lt;span style="color:red"&gt;'ItemName'&lt;/span&gt;);
        price    = xlImport.getFieldValue(&lt;span style="color:red"&gt;'ItemPrice'&lt;/span&gt;, &lt;span style="color:blue"&gt;false&lt;/span&gt;);
        &lt;span style="color:green"&gt;// process data...&lt;/span&gt;
        cnTotal++;
        xlImport.moveNext();
    }
    xlImport.finalize();
    Box::info(strfmt(&lt;span style="color:red"&gt;&amp;quot;%1 records read&amp;quot;&lt;/span&gt;, cnTotal));
}
&lt;span style="color:blue"&gt;catch&lt;/span&gt; (Exception::Error)
{
    xlImport.finalize();
}&lt;/pre&gt;&lt;/code&gt;&lt;p&gt;You can download the source code of the class at &lt;a href="http://www.axaptapedia.com/Image:ExcelImportADO.zip"&gt;axaptapedia.com&lt;/a&gt;. I would like to thank &lt;a href="http://axforum.info/forums/member.php?u=5597&amp;langid=1"&gt;Gustav&lt;/a&gt; and &lt;a href="http://axforum.info/forums/member.php?u=1496&amp;langid=1"&gt;blokva&lt;/a&gt; for inspiration and very useful tips on improving the class.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Note&lt;/span&gt;: 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!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-116662878516725108?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/116662878516725108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=116662878516725108' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/116662878516725108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/116662878516725108'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2006/12/read-excel-table-via-ado.html' title='Read Excel table via ADO'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-31341829.post-115329642625084189</id><published>2006-07-19T11:17:00.000+04:00</published><updated>2007-01-09T00:54:57.943+03:00</updated><title type='text'>Dynamics AX and ADSI</title><content type='html'>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;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 &lt;code&gt;GetObject("LDAP://rootDSE").get("defaultNamingContext")&lt;/code&gt; using filter &lt;code&gt;"(&amp;(objectClass=user)(objectCategory=person)(sAMAccountName=%s))"&lt;/code&gt;. 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...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;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...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/31341829-115329642625084189?l=gl00mie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gl00mie.blogspot.com/feeds/115329642625084189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=31341829&amp;postID=115329642625084189' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/115329642625084189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/31341829/posts/default/115329642625084189'/><link rel='alternate' type='text/html' href='http://gl00mie.blogspot.com/2006/07/dynamics-ax-and-adsi.html' title='Dynamics AX and ADSI'/><author><name>gl00mie</name><uri>http://www.blogger.com/profile/09664306668052320767</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
