Wednesday, December 3, 2008

NAV Automation Object

It took me a good while, but I finally managed to get my first automation object created, compiled, and made visible in NAV. Stefano Demiliani provided most of the guidance that I needed on this, as well as a few postings on mibuso, but there were a few other tricks I picked up on in the process that I figured I'd share here. Also, since I couldn't find one anywhere else, I figured I'd post a complete Visual Studio sample project.

You can download the sample project at http://bitsofinsanity.blob.core.windows.net/public/NavAutomation.zip

Please note that I am far from an expert on COM objects - I figured out just enough to get my code to compile and work, but it's highly likely that my work is suboptimal, so if anyone has any ideas for improvement, please share them.

I suppose I should explain what I'm talking about. Microsoft Dynamics NAV is an ERP system that contains and manages our Finances, Accounting, HR, Events, and more. It has tables of data (backed by SQL Server), forms to display the data, reports, and more. Each of these object types can have code behind it controlling what it does. However, the extensibility is limited to using COM objects, which are called 'Automation Objects' in NAV. But so long as you can create a COM object properly, you can insert it into a table or form and, through that COM object, you can do whatever the .Net framework allows you to do.

In working to create a COM object, I already had a class and and assembly that performed the functionality I wanted. I wanted to wrap it with COM so that I could see my class and its methods and functions in Nav. I first tried by adding the COM tags to my existing class, and I could kindof-sortof get it to work, but it never really worked completely. So I ended up creating a separate assembly and a separate class that contained the COM wrapper. All it does is call the real class, which does the hard work (the sample code doesn't call any other classes, but you could easily change it to do so).


Notes on creating an Automation Object

Your assembly will need to be strongly named. To do this:
  1. Right-click on your project name, click 'Properties', click the 'Signing' tab on the left
  2. Check the 'Sign the assembly' checkbox'
  3. In the 'Choose a strong name key file' drop down, choose 'New'. Give it a file name (anything you want), and uncheck the 'Protect my key file with a password' checkbox. Click 'OK'.

Please note the Post-build events in the sample code (under the project Properties). Make sure the paths to gacutil and regasm are correct. I don't know why, but for some reason I found I had to run the regasm and gacutil commands twice to make the COM object visible in Nav, hence the duplication of the lines below. These should register it on your development box so that you can see it in Nav. If you want to use the automation object elsewhere, you'll have to register the assembly in a similar manner on the other client machines. The post build events are:
SET GACUTIL="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
SET REGASM="C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe"


%REGASM% $(TargetFileName) /tlb:NavAutomation.tlb

%GACUTIL% /i $(TargetFileName)


%REGASM% $(TargetFileName) /tlb:NavAutomation.tlb

%GACUTIL% /i $(TargetFileName)

After registering the assembly and type library, open Nav, open an object, and create a variable of type 'Automation.' For the subtype, click on the '...' then click on the drill-up arrow by 'Automation Server.' That will list all the COM objects loaded in your system, and you should be able to find your custom Automation object by the Assembly name (for the sample code, it should be called NavAutomation).


Be sure to create your own GUIDs for your project. The GUIDs you need to change are the ones on the interface and class definition in the cs file.

The sample project is built in Visual Studio 2008 with C# on .Net 3.5, and I tested using it on Nav 5.0 SP1, all running on Windows Server 2003 with SP2. It should work with other versions of Visual Studio, .Net, Nav, and Windows, but I haven't tested those out.

If you have an overloaded function in C#, the COM object representation in Nav will change it around a little. For example, if you have overloaded a function called 'test' with 3 different definitions, it will show up as 'test', 'test_2', and 'test_3.' This is done automatically. See the Nav symbol menu to find out what the names actually come up as.

There is no automatic type conversion between Decimal values in .Net and Decimal values in Nav, so you'll have to do a work around if you need to use decimal for a parameter or return value. I used a string object, casting it appropriately on both sides.

The Nav side can't handle constructors that require parameters. To get around this, you can create a constructor with no parameters and a function called 'init' that handles the actual construction logic.