Code highlighting

Tuesday, January 05, 2010

Editor script for simplifying the search in AOT

When doing code refactoring, or when investigating a particular class or form to understand the functionality behind it, it is often handy to search for variables used in its methods, looking at where and how each variable is used.

It is tedious to manually go through the steps of copying the name of the variable into the clipboard, opening up the parent class/form, opening up the AOT Find dialog for it, pasting the variable name into the Selected Text field and pressing Find now.

I have automated these steps, putting the code into an editor script.

You can download the xpo, which contains the EditorScripts class with the additional method, by following this link.

Now you should be able to very easily and quickly find the required code.

The code of the editor script:

///
/// Editor script for finding all occurrences of the selected text in the parent node of the selected method
///

///
/// Reference to the AX editor
///
///
/// 2010-01-05, ivanv
///

void addIns_FindAllOccurrencesInNode(Editor e)
{
#AOT
TreeNode treeNode;
FreeText selectedText;

FreeText GetSelectedTextFromEditor(Editor _e)
{
str 1 curSymbol;
int iCopyFrom;
int iCopyTo;
FreeText _selectedLine;
;
if (_e.markMode() != MarkMode::NoMark && _e.selectionStartCol() != _e.selectionEndCol())
{
_selectedLine = strLRTrim(subStr(_e.currentLine(), _e.selectionStartCol(), _e.selectionEndCol() - _e.selectionStartCol()));
}
else
{
_selectedLine = _e.currentLine();
for (iCopyFrom = _e.columnNo()+1; iCopyFrom >= 0; iCopyFrom--)
{
curSymbol = subStr(_selectedLine, iCopyFrom, 1);
if (!strAlpha(curSymbol) && curSymbol != '_')
break;
}
for (iCopyTo = _e.columnNo()+1; iCopyTo <= strLen(_selectedLine); iCopyTo++)
{
curSymbol = subStr(_selectedLine, iCopyTo, 1);
if (!strAlpha(curSymbol) && curSymbol != '_')
break;
}
_selectedLine = (iCopyFrom < iCopyTo) ? subStr(_selectedLine, iCopyFrom + 1, iCopyTo - iCopyFrom - 1) : '';
}
return _selectedLine;
}

void FindLinesContainingSelectedText(FreeText _selectedLine)
{
Args args = new Args(formstr(SysAOTFind));
FormRun sysAOTFindFormRun;
FormStringControl containingTextCtrl;
FormButtonControl findNowBtnCtrl;
;

sysAOTFindFormRun = classFactory.formRunClass(args);
sysAOTFindFormRun.init();
sysAOTFindFormRun.run();

// Set the text to find
containingTextCtrl = sysAOTFindFormRun.design().controlName(identifierStr(ContainingText));
containingTextCtrl.setFocus();
containingTextCtrl.pasteText(_selectedLine);

sysAOTFindFormRun.detach();

// Launch the search process
findNowBtnCtrl = sysAOTFindFormRun.design().controlName(identifierStr(FindNow));
findNowBtnCtrl.clicked();
}
;

selectedText = GetSelectedTextFromEditor(e);
if (selectedText)
{
// Find the currently open method node
treeNode = TreeNode::findNode(e.path());
// Find the parrent node of the method
treeNode = TreeNode::findNode(xUtilElements::getNodePathRough(xUtilElements::parentElement(xUtilElements::findTreeNode(treeNode))));
if (treeNode)
{
treeNode.AOTnewWindow();

FindLinesContainingSelectedText(selectedText);
}
}
}

Thursday, December 03, 2009

Correct KernelFunctions xpo

Hey, guys.

Just a small update on my previous post on Kernel Functions for those interested.
I have now corrected the link to point to the right xpo.
It was previously pointing to a parser class I used to get some of the functions in.

Sorry about that :)
The uploaded xpo is compatible with AX 2009

Wednesday, November 25, 2009

Kernel functions exposed through BC.NET

BC.NET is a great feature of Dynamics AX. It allows to access the AX application from any external application that supports .NET, and provides a rather rich functionality out of the box.
But one feature that is missing in BC.NET is the ability to call AX kernel functions, like fieldId2Name, num2str, decRound, etc.

I needed to use these functions in my code recently, and in order to access them, I created a class in X++, that simply wraps the useful functions in static methods, kinda like the ones that exist in class Global.

Download Class_KernelFunctions.xpo

You need to import it into AX before using, of course. This can be automated, if you don't want to include this XPO as part of the package you deliver to your customers.

Now you can call it through the Business Connector the following way:

Axapta ax = new Axapta();
ax.Logon("ext", null, "localhost", null);
// num2str(123.45, -1, 2, 1, 1);
Convert.ToDouble(ax.CallStaticClassMethod("KernelFunctions", "num2str", 123.45m, -1, 2, 1, 1));

Tuesday, November 24, 2009

SysCompilerTarget - compiling X++ code without showing the compilation window

I have been posed with a question about how to avoid showing the compilation window during compilation of some X++ code (When doing compilation from code, naturally).

So I decided to post a possible solution for this question, re-using one of my previous posts about ClassBuild class.

Basically, AOTcompile method on TreeNode class supports an optional flag, controlling the compilation output:
An integer that indicates whether output should be sent to the message box. If the value 1 is passed, no output is sent to the message box. The default value is 0. This parameter is optional.


So all we need to do now is change the compilation output target to Message Window. Looking at the SysCompilerSetup form code, it's rather easy to come up with the following code:


static void SysCompilerTargetExample(Args _args)
{
ClassBuild classBuild;
DictClass dictClass;
SysCompilerTarget targetOrig;
;
// Setting Compilation Target = Message Window
targetOrig = SysUserInfo::compilerTarget();
SysUserInfo::compilerTarget(SysCompilerTarget::MessageWindow);
SysCompilerOutput::setCompilerTarget(SysCompilerTarget::MessageWindow);
SysCompilerOutput::updateParm();

// Building the class with 1 method
classBuild = new ClassBuild("TRN_ClassBuild", false);
classBuild.addMethod("test", 'void test()\n{\n}');
classBuild.addSourceToMethod("test", @"
;
info('We created a Class and can call its methods');");

// The actual compilation. Note the _flag argument == 1
classBuild.classNode().AOTcompile(1);

// Call the class method to show that we are done and the code is compiled
dictClass = new DictClass(className2Id(classBuild.name()));
dictClass.callObject('test', dictClass.makeObject());

// Restoring Compilation Target to its original value
SysUserInfo::compilerTarget(targetOrig);
SysCompilerOutput::setCompilerTarget(targetOrig);
SysCompilerOutput::updateParm();
}


Don't forget to restore the original settings after the compilation is complete, as shown above.

Friday, November 06, 2009

How to quickly set up Application Integration Framework (AIF) for testing Axd documents

Purpose of this post
Part of the test effort on our team at MDCC goes for verifying the Axd documents shipped with the Dynamics AX application.

As many people find it hard to setup AIF, I have tried to help them by creating an automated way of doing the initial setup, which can be used for testing the documents using a 1-box setup and FileSystem transport adapter.

Download link
From the following link, you will be able to download an X++ class that initializes all the entities required for AIF to work, like the endpoints, transport adapters and channels, services, their actions and action policies.
Note, that since I am working on the Inventory team, I am filtering out the services accordingly. You can easily extend/modify this to suite your specific needs by changing the filter or adding new ones in classDeclaration and method main

Download xpo

Example from the standard application
To give a specific example, I encourage you to try sending a selected item electronically in form of an XML file right after executing the job.
To do that, navigate to Inventory Management\Common forms\Item details and press Send electronically button. Fill in the values for endpoint, filter on a specific item(s), and press OK.

Your screen should look similar to this:


Processing the document
The document is successfully placed into the queue for processing and sending through the gateway. If you have a batch job for processing AIF services up and running, you will soon see a file in the outbound files folder setup on the outbound channel.
If you would like to learn, how to set up the batch jobs for this, follow the below link: Start and stop the asynchronous AIF services

In order to avoid having to set the batch jobs up, I have added 2 extra methods to the class for starting the processing of inbound and outbound messages.

public static void ProcessOutboundDocuments()
{
    new AifOutboundProcessingService().run();
    new AifGatewaySendService().run();
}
public static void ProcessInboundDocuments()
{
    new AifGatewayReceiveService().run();
    new AifInboundProcessingService().run();
}


Just call the DEV_AifInitialSetup::ProcessOutboundDocuments method from a job, and the document will be automatically processed and sent on its way to the outbound documents folder.

Output
The resulting XML file for the item should look similar to the following: