- [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AOS60$01]
- Windows service configurations
- HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dynamics Server
- AOS Server(s) configurations
- HKEY_CURRENT_USER\Software\Microsoft\Dynamics\
- When a user opens the client config, this registry key is created and it stores their configurations which take priority over HKLM
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Dynamics
- These are the client config settings for the machine that are used when HKCU settings aren't present.
This technical blog will be about my adventures with Microsoft Dynamics 365 for Operations (AX7/D3fo), AX 2012, and AX 2009.
Thursday, December 3, 2015
Some notes on Dynamics AX 2012 and the registry
Tuesday, November 10, 2015
CacheDataMethod (AX2012 feature) property vs CacheAddMethod
A someone little known feature of AX 2012 is a new property called "CacheDataMethod".
Previously, when you wanted to improve performance by caching your display/edit methods, you would place a line of code like this after the super() in the datasource's init() method:
In AX 2012, you can just change the "CacheDataMethod" property on the object without adding code. I prefer this method where possible mainly because it keeps everything packaged together and I don't have to intermingle my code with base code.
Previously, when you wanted to improve performance by caching your display/edit methods, you would place a line of code like this after the super() in the datasource's init() method:
this.cacheAddMethod(tableMethodStr(CustTable, MyDisplayMethod));
In AX 2012, you can just change the "CacheDataMethod" property on the object without adding code. I prefer this method where possible mainly because it keeps everything packaged together and I don't have to intermingle my code with base code.
Monday, November 9, 2015
The better way to pass containers between objects/forms using the Args() class, and not by converting to a string
If you need to pass a container between objects/forms using the Args class, don't convert it to a string and then back, use parmObject() and ContainerClass()! I see many suggestions about converting it to a string, which can have much more unpredictable results and is not as versatile.
You wrap your container in the class ContainerClass() and then unwrap it at the other end.
Make your call like this:
To test this, create a form (Form1) and overwrite the init method and put in this code:
Then create a Job and put in this code:
And then run the job!
You wrap your container in the class ContainerClass() and then unwrap it at the other end.
Make your call like this:
And retrieve it like this:args.parmObject(new ContainerClass(["Real container", 1234, "Not con2str container"]));
containerClass = element.args().parmObject() as ContainerClass;
myContainer = containerClass.value();
To test this, create a form (Form1) and overwrite the init method and put in this code:
public void init() { ContainerClass containerClass; container conValue; if (!(element.args() && element.args().parmObject() && element.args().parmObject() is ContainerClass)) throw error("@SYS22539"); super(); containerClass = element.args().parmObject() as ContainerClass; conValue = containerClass.value(); info(strFmt("The container contains '%1'", con2Str(conValue))); }
Then create a Job and put in this code:
static void JobForm1(Args _args) { Args args; FormRun formRun; args = new Args(); args.name(formStr(Form1)); args.parmObject(new ContainerClass(['Real containers', 1234, 'Not con2str containers'])); formRun = classFactory.formRunClass(args); formRun.init(); formRun.run(); formRun.wait(); }
And then run the job!
Wednesday, October 28, 2015
How to create a self-elevating PowerShell script that will run as administrator every time
Often there are various build processes or other automated tasks that run via PowerShell and need to be run as administrator. If you forget to run it as administrator, it won't work, and you don't always know.
I came across a great blog post by Ben Armstrong that I have to share, where he's created a block of code you just prefix to the beginning of your PowerShell script that will re-launch it as administrator if it is not. Check his post out here:
http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/09/23/a-self-elevating-powershell-script.aspx
Here's a screenshot of the PowerShell code in case his blog goes down:
I came across a great blog post by Ben Armstrong that I have to share, where he's created a block of code you just prefix to the beginning of your PowerShell script that will re-launch it as administrator if it is not. Check his post out here:
http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/09/23/a-self-elevating-powershell-script.aspx
Here's a screenshot of the PowerShell code in case his blog goes down:
Tuesday, October 13, 2015
Without any customization, how to incrementally compile the CIL from the command line
With Dynamics AX 2012, you can start an incremental CIL compile from the command line (or Power Shell) without any customization. This is useful if you have any automated processes that import XPOs frequently and you don't want to constantly build the full CIL.
Create an XML file with this data:
Then save it in a place that is accessible from the AOS service account. I saved it as "C:\IncrementalCIL.xml" on the AOS machine.
Then run this command, subbing in for your environment:
"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Ax32.exe" \\MyNetworkShare\AOS.axc -startupcmd=autorun_C:\IncrementalCIL.xml
Some notes about the XML. The version attribute must not be greater than your system's build, which can be found from calling xInfo::releaseVersion().
There are a TON more autorun features available, and you can use the following links or just dig into \Classes\SysAutoRun.
More information can be found at:
Create an XML file with this data:
<?xml version="1.0" ?> <AxaptaAutoRun exitWhenDone="true" version="6.2" logFile="C:\AxaptaAutorun.log"> <CompileIL incremental="true" /> </AxaptaAutoRun>
Then save it in a place that is accessible from the AOS service account. I saved it as "C:\IncrementalCIL.xml" on the AOS machine.
Then run this command, subbing in for your environment:
"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Ax32.exe" \\MyNetworkShare\AOS.axc -startupcmd=autorun_C:\IncrementalCIL.xml
Some notes about the XML. The version attribute must not be greater than your system's build, which can be found from calling xInfo::releaseVersion().
There are a TON more autorun features available, and you can use the following links or just dig into \Classes\SysAutoRun.
More information can be found at:
- https://msdn.microsoft.com/en-us/library/sysautorun.aspx
- https://msdn.microsoft.com/en-us/library/aa569641.aspx
Thursday, September 10, 2015
How to export all private or shared projects with or without project definitions from a specified layer
I often have to switch development machines, and when I do, I lose all of my private or shared projects. This is a good way to backup your projects and/or their definitions.
static void AKExportProjects(Args _args) { #AotExport TreeNodeIterator tni; ProjectNode projectNode; int exportFlag; Dialog dialog = new Dialog(); DialogField folderName; DialogField projectDefinitionOnly; DialogField exportFromLayer; DialogField projectType; UtilEntryLevel layer; dialog.addText("This will export all projects (shared or private) that exist in a selected model."); projectType = dialog.addFieldValue(enumStr(ProjectSharedPrivate), ProjectSharedPrivate::ProjPrivate); projectDefinitionOnly = dialog.addField(extendedTypeStr(NoYesId), 'Project Definition Only'); folderName = dialog.addField(extendedTypeStr(FilePath)); exportFromLayer = dialog.addField(enumStr(UtilEntryLevel), 'Projects from layer'); dialog.run(); if (dialog.closedOk()) { if (!folderName.value()) throw error("Missing folder"); exportFlag = #export; if (projectDefinitionOnly.value()) exportFlag += #expProjectOnly; layer = exportFromLayer.value(); switch (projectType.value()) { case ProjectSharedPrivate::ProjPrivate: tni = SysTreeNode::getPrivateProject().AOTiterator(); break; case ProjectSharedPrivate::ProjShared: tni = SysTreeNode::getSharedProject().AOTiterator(); break; } projectNode = tni.next() as ProjectNode; while (projectNode) { if (projectNode.AOTLayer() == layer) projectNode.treeNodeExport(folderName.value() + '\\' + projectNode.name() + '.xpo', exportFlag); projectNode = tni.next() as ProjectNode; } } else warning("No action taken..."); }
Monday, August 3, 2015
How to check if a Base Enum has a valid value
Base enums can use integer assignment to be set, but you can set it 0 to any positive valid integer up to 255 inclusive, and that does not mean it's a valid enum.
Take for example the base enum "ABC" (\Data Dictionary\Base Enums\ABC). You can assign ABC=555, and it will store an integer value of 255 with no issue.
To check if an enum value is valid, you can use this method:
Here is a sample job that will demonstrate how this can be an issue:
Take for example the base enum "ABC" (\Data Dictionary\Base Enums\ABC). You can assign ABC=555, and it will store an integer value of 255 with no issue.
To check if an enum value is valid, you can use this method:
static boolean checkABCEnum(ABC _abc) { return new DictEnum(enumNum(ABC)).value2Symbol(_abc)); }
Here is a sample job that will demonstrate how this can be an issue:
static void CheckIfEnumIsValid(Args _args) { // Possible enum values 0, 1, 2, 3 ABC abcValid, abcInvalid; // Valid enum abcValid = ABC::C; info(strFmt("%1, %2", enum2int(abcValid), abcValid)); // Invalid enum, but integer assignment works and is stored abcInvalid = 555; info(strFmt("%1, %2", enum2int(abcInvalid), abcInvalid)); if(new DictEnum(enumNum(ABC)).value2Symbol(abcValid)) info(strFmt("Enum with type %1 and integer value %2 (%3) is valid", typeOf(abcValid), enum2int(abcValid), abcValid)); else error(strFmt("Enum with type %1 and value %2 is invalid", typeOf(abcValid), enum2int(abcValid))); if(new DictEnum(enumNum(ABC)).value2Symbol(abcInvalid)) info(strFmt("Enum with type %1 and integer value %2 (%3) is valid", typeOf(abcInvalid), enum2int(abcInvalid), abcInvalid)); else error(strFmt("Enum with type %1 and value %2 is invalid", typeOf(abcInvalid), enum2int(abcInvalid))); /* Output: 3, C 4, Enum with type Enum and integer value 3 (C) is valid Enum with type Enum and value 4 is invalid */ }
Subscribe to:
Posts (Atom)


