Solved: Visual Studio Tools Addins fail after importing packages

David Meego - Click for blog homepageI have been fairly quiet recently on the blog and forums as I have been concentrating on developing the next build of GP Power Tools. All I can say is that it is awesome and you will love it. I have a “Sneak Peak” article coming soon.

While working on the development and testing process, I had some unusual behaviour from my .Net Visual Studio Tools Addins and spent an entire day troubleshooting the issue so I could identify the cause and work out a solution. Read on for the gory details….

The Scenario

GP Power Tools has two Visual Studio Tools Addin DLLs; one written in Visual C# and the other written in Visual Basic.Net. These DLLs perform many different functions and are tied into my Dexterity based code using the placeholder functions method. This method works by creating a global or form level function in the Dexterity dictionary with all the parameters needed to get the required data in and out of the .Net code. The Dexterity function itself only has enough code to return an error status code (-1) and/or error message (“Not Installed”).

At the .Net Addin level, an InvokeAfterOriginal event is registered against the function and then the .Net code can perform whatever task needed to return the results using the InvokeEventArgs arguments for that appropriate function. This method is clean and simple and can be used to easily extend Dexterity with .Net functionality and works for all versions of GP.

Note: The .Net Interop functionality was only added for GP 2015 or later, whereas the placeholder method works from GP v9 onwards.

The Problem

I was working on a process which had to import a modified window using the Tools >> Customization >> Customization Maintenance window.

After importing the Customization Maintenance package file and utilising a feature in GP Power Tools that uses a placeholder function to call some .Net code, it failed with a “Not Installed” error.  When I checked my About window, rather than returning the version numbers for my two DLLs, it confirmed that the system believed both DLLs were not installed. Restarting GP and checking the About window correctly showed the Addin DLLs as installed. Importing a package again and rechecking the About window, they then showed as Not Installed again. I was also able to confirm that going into Modifier or Report Writer and clicking cancel at the product selection dialog to return to GP made the Addins active again.

So at this stage, it appeared that importing a package file disabled the .Net Addins entirely and only an application restart (or equivalent) could reactivate them.

The Breakthrough

I discussed these findings with my friend Arthur Achilleos (who manages the Dynamics GP system at an Australian customer site and also is a beta tester for GP Power Tools) and he mentioned that he had never noticed this issue and has a number of Visual Studio Tools addins on his system. He was able to test and confirm that one of his addins which registered an event against a field was still active after importing a package.

This made sense, as my testing showed that this issue happened for all versions of GP (at least GP 2013 v12 onwards), so why hadn’t the problem been found before now. The reasoning Arthur and I came up with was that most .Net addins use field level events and not functions or procedures.

Next, I created a small test project in C# and registered events on; a function, a procedure, a before modal dialog and a field change script. After confirming that each of the events fired and displayed a dialog (I used Dynamics.Forms.SyVisualStudioHelper.Functions.DexWarning.Invoke() rather than MessageBox.Show() as it looks better), I imported a package and retested.

Of the four events registered, only the field event was still active. But one active event was all that was needed to create a work around solution.

The Solution – Introduction

The idea for the solution was to use a field event handler to re-register the failed events after importing a package. Making this happen required a few steps.

However, because the Customization Maintenance window is part of the Dexterity Runtime and is contained in the Dex.dic dictionary file, there is no way to register a .Net event directly against the OK Button on the Import Package window.

This can be overcome using Dexterity. The trick is to register a cross dictionary trigger against the Dex.dic (which has a dictionary or product ID of 1). The handler for this trigger will be a procedure. But we cannot use a .Net event on that procedure as we know it will fail after importing a package.

The procedure will need to call a field change script using a run script command against a field on a window on an open form. Any form which has its windows marked as AutoOpen=False, to create a hidden form can be used. Most Dexterity products already have a hidden form perfect for this purpose, their Command Form (which used for their menu navigation integration).

Note: If you are a .Net only developer, don’t panic, there is a solution for you without you needing to use Dexterity.

The Solution – Dexterity Component

The first steps are as follows:

  1. Create a new window in the Command Form called CustomizationMaintenanceFix and marking it as AutoOpen=False
  2. Create a local push button field called ‘(L) PackageImported’ and drop it onto the window.
  3. Add some static text to explain why this window and field exist. There is no code added to the window or pushbutton.

The screenshot and example code included in this article is from the free Visual Studio Integration Toolkit product, which is why it is prefixed with VSTM (Visual Studio Tools Menus). Feel free to change the prefix to your standard product prefix.

The next step is to create a procedure as a trigger handler to make sure the Command Form is open and to “run script” on the button in the newly added hidden window. This will execute a field change script (if one exists) and trigger any events registered against the change script (even if no script exists).

Global Procedure: VSTM_Customization_Maintenance_Package_Imported

{ Fix for Visual Studio Tools Addins failng after importing packages }
{ Called by trigger on Dex import window }

if not isopen(form VSTM_Command_Form) then
	open form VSTM_Command_Form;
end if;
if isopen(form VSTM_Command_Form) then
	run script '(L) PackageImported' of window CustomizationMaintenanceFix of form VSTM_Command_Form;
end if;

Then add a trigger in the Startup procedure to pick up when the Import Package window’s ‘OK Button’ is pressed and call the handler procedure just added.

Global Procedure: Startup (excerpt)

pragma(disable warning LiteralStringUsed);

{ Package Import Fix }
if Trigger_RegisterFocusByName(1 {DEX.DIC}, "'OK Button' of window 'Import' of form 'Customization Maintainance'", TRIGGER_FOCUS_CHANGE, TRIGGER_AFTER_ORIGINAL, script VSTM_Customization_Maintenance_Package_Imported) <> OKAY then
	warning "'OK Button' of window 'Import' of form 'Customization Maintainance' change focus trigger registration failed.";
end if;

pragma(enable warning LiteralStringUsed);

Note: The spelling mistake in the name of the ‘Customization Maintainance’ form is intentional as it must match the spelling mistake in the Dexterity runtime!

The final step is to create a placeholder function to check if the system thinks the Visual Studio Tools Addin is installed or not.

Global Function: VSTM_Addin_Available

{ Placeholder function for Addin to trigger on }
function returns boolean OUT_Available;

OUT_Available = false;

pragma(disable warning UnusedVariable);

You will now need to compile your dictionary, create a chunk, install the chunk and run the DAG.EXE tool on your dictionary before you can perform the next part of the solution using Visual Studio.

The Solution – Visual Studio Component

Now that the Dexterity component of the solution is in place it can be used to complete the workaround solution.

Note: If you are a Visual Studio only developer and don’t have a Dexterity dictionary to add the Dexterity part of the solution to, don’t worry. Instead, please install the free Visual Studio Integration Toolkit (if you don’t have it already) and use its Dexterity components to create a fix for your code. The Visual Studio Integration Toolkit is designed for Visual Studio developers to utilize so they can add menus and areas pages to Dynamics GP and have access to over 220 Dexterity only functions. Click the link above for more information.

For the solution to work, an event handler script is needed for our VTSM_Addin_Available placeholder function to return a true value. This is used to check if the events need re-registering and to avoid having them registered multiple times.

        // Handler for Available Function
        void VisualStudioIntegrationToolkitAvailable(object sender, VstmAddinAvailableFunction.InvokeEventArgs e)
        {
            e.result = true;
        }

The next part of the solution is to move all event registrations that fail after importing packages (which in my testing was anything other than field events) from the Initialize() function into an new InitalizeSub() function.

        public void InitializeSub()
        {
            try
            {
                // Register Events for Available
                MenusForVisualStudioTools.Functions.VstmAddinAvailable.InvokeAfterOriginal += new VstmAddinAvailableFunction.InvokeEventHandler(VisualStudioIntegrationToolkitAvailable);

                // Register your events here
 
            }
            catch
            {
            }
        }

Then we can update the Initialize() function to call InitalizeSub() and also register our field event that is the key to this workaround solution.

        public void Initialize()
        {
            try
            {
                // Register events
                InitializeSub();

                // Re-register events after importing customization maintenance package
                MenusForVisualStudioTools.Forms.VstmCommandForm.CustomizationMaintenanceFix.LocalPackageImported.ClickAfterOriginal += new EventHandler(VisualStudioIntegrationToolkitCustomizationMaintenancePackageImported);
            }
            catch
            {
            }
        }

The final piece of the puzzle is the handler for our field event. This will check if the system thinks the Visual Studio Tools Addin is installed and if not, it will call InitializeSub() again to re-register all the events that have failed.

        private void VisualStudioIntegrationToolkitCustomizationMaintenancePackageImported(object sender, EventArgs e)
        {
            // Re-register events after importing customization maintenance package
            if (!MenusForVisualStudioTools.Functions.VstmAddinAvailable.Invoke())
            {
                InitializeSub();
            }
        }

There you have it. After this workaround was implemented, none of my Visual Studio Addin DLLs fail after a Customization Maintenance package file is imported.

 

This issue has been reported to the Microsoft Dynamics GP development team in Fargo.

Hope you find this information useful.

And, Oh yes…. May the Fourth Be With You…. Always.

David

This article was originally posted on http://www.winthropdc.com/blog.

Please post feedback or comments

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.