Friday, January 1, 2010

Let Lonely Dynamics GP Talk to Your Windows Apps!

Hey guys, it compiled! Guys? Hey, where is everyone? Wait a minute, you mean not everyone is developing prototypes of Dynamics GP integrations on New Year's Eve? Clearly I'm the only one who likes to have real fun.

Speaking of being alone, imagine how poor old Dynamics GP feels, all by its lonesome. Sure, Integration Manager occasionally soaks it with a fire hose of data, and occasionally an arrogant VS Tools AddIn pushes it around, but when was the last time that GP got to talk to another app? I mean REALLY TALK.

Well, the last several hours I've been tinkering around to see if I could get GP to interact with another windows application. This idea started based on a question posted to Experts Exchange. The member asked if he could access the SOP number on the SOP Transaction Entry window so that he could use it to automatically display data in his .NET application. Well, rather than have a .NET application try and figure out what SOP number is being accessed in GP by a particular user, why not have GP talk to your .NET app in real time and tell it what is going on?

The basic question is how can you get two windows applications to talk to each other? This took a bit of research, as there are many different approaches. Some people recommending writing to a text file, and having the receiving app detect the presence of a new file. Others talked about passing data via the registry, a database, etc. These allow you to pass data, but don't really let two apps talk directly to each other.

On the other end of the spectrum, there are recommendations to have the two apps communicate via network protocols: sockets, TCP/IP, etc. This approach is required when you need two apps on different machines to talk across a network, but it's a bit more than I wanted. I was just looking to get GP to talk to another windows app on the same machine.

Fortunately, there are Windows APIs that allow two windows apps to communicate in a simple manner. The apps can pass integers and strings, and can immediately respond to the receipt of those messages. The approach I chose was to use the SendMessage Win32 API.

This excellent blog post gave me the code to handle sending and receiving messages between two windows applications, and I was able to relatively quickly pass messages between two separate .NET test apps.



Okay, but how can I get GP to send messages to a .NET application?

Well, let's say that I want a user to be able to click a button on the SOP Transaction Entry window, or Transaction Inquiry Zoom window, that will automatically pull up a corresponding window or record in my application. And I want to have GP send my custom app the following information:

1) Company database (INTERID)
2) SOP Type
3) SOP Number
4) Customer ID

There are probably a few alternatives, but the approach I came up with is:

1) Use Modifier to add a custom button to the SOP Transaction window
2) Use VBA to send the required GP data to a COM assembly
3) Have the COM assembly issue the SendMessage call to my application

Simple right? Well, sort of.

Standing on the shoulders of many people much smarter than I (i.e. blatantly copying code from their blog posts), I was able to cobble together a .NET assembly and some VBA that gets the job done.

I'll discuss the 3 steps above in reverse order.

First, you'll need to create a .NET Class Library to provide the SendMessage functionality. I called my project GPMessage, and my class MessageHelper. Using the SendMessage code from the blog post I referenced earlier, plus several tweaks required to get it to work, you'll have a very simple assembly that does a great job of sending messages to other apps.

But, in order to use it with VBA, you'll need to make it visible to COM. If you're not familiar with this process, it's a bit arcane, with alot of bad advice out there, but here is a good post on how to do it.

And here are two time saving caveats that are only mentioned in passing in most articles on the topic.

In Visual Studio 2005, enable the following options:

Properties --> Build --> Register for COM interop
Properties --> Signing --> Sign the assembly (setup a key file)

Then just compile, and violà, you now have your COM visible assembly! Wasn't that easy?

Next, use Modifier to add your custom button to the SOP transaction window (I called mine SendToApp) and add the SOP window, your new button, the "SOP Type Database" field (read this great article for background), SOP number, and Customer ID fields to VB.



Then, open the VB Editor in GP and pull up the code window for your SendToApp button.

Since you selected "Register for COM interop", Visual Studio did you the favor of making your new COM component visible in the References list. To add the reference, click on Tools -> References and select the GPMessage reference.

You are now ready to code!

A few key lines from my VBA:

Dim GPMessage As New GPMessage.MessageHelper

strMessage = strINTERID & "," & intSOPType & "," & strSOPNumber & "," & strCustomerID


lngWindowID = GPMessage.GetWindowId(strNull, strAppName)

lngResult = GPMessage.SendStringMessage(lngWindowID, 0, strMessage)


blnResult = GPMessage.BringAppToFront(lngWindowID)


The VBA simply instantiates the GPMessage component, gets the window ID of your custom app, and then sends the SOP transaction information.

Finally, in your app, you add the WndProc procedure to receive the message and handle it accordingly.

protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_USER:
uiMessage.Text = m.WParam + " – " + m.LParam;
break;
case WM_COPYDATA:
COPYDATASTRUCT mystr = new COPYDATASTRUCT();
Type mytype = mystr.GetType();
mystr = (COPYDATASTRUCT)m.GetLParam(mytype);

//Your code to handle the message goes here
DisplayMessage(mystr.lpData);

break;
}

base.WndProc(ref m);
}



Here is an example of the final product. When I click on the Send To App button, my custom app instantly receives the SOP transaction information.




And that, folks, is how you make Dynamics GP a more social application!

Happy New Year!


UPDATE: After pondering this approach further, there is one caveat. This solution (with the custom button and VBA) would work well for a client that is licensed for Modifier & VBA, or a Customization Site License; however, for a client that does not have such licensing, it won't work.

If you are a partner delivering this solution to a client, you could create a GP AddIn using VS Tools and assign a shortcut key to the AddIn. So instead of the user clicking on a button, they could press a shortcut key to invoke your AddIn to send the data to the external application (or they could always use the Additional Menu). This approach also allows you to avoid the hassle of using COM, as your .NET AddIn would be able to talk directly to your .NET app. And no additional licensing required by the client.

One thing I like about the button approach is that it gives a visual cue to the user, and provides a little more consistent user experience, vs. an obscure / non-standard shortcut key, which is less obvious to most users.

But, the AddIn approach is a simpler to develop and certainly simpler and easier to deploy than a Modifier+VBA+COM DLL, so it has some benefits. Also, something I had forgotten about: AddIns can be setup with Event Handlers to automatically respond to events in GP. So, for instance, after a user selects a sales order in the SOP Transaction Entry window, the AddIn can automatically fire code to perform certain actions, which is a nice plus.

In case that appeals to you, I have created a sample AddIn project and included the link below.



Here are links to my projects and code:

Sample Send and Receive prototype apps

Project for GPMessage.dll COM assembly

GP 10 Package file with custom button and VBA

VBA Code by itself

Project for GPMessageAddIn (no Modifier or VBA required)

3 comments:

Christina Belding said...

Brilliant-simply brilliant Steve! I have been reading your blog for a while now (apologies for not commenting sooner), but this is just amazing! Don't let it bother you about it being New Years Eve either....sometimes that's the only quiet time we get right?

Your previous post on the integration manager search and destroy mission had me roaring as well. Thanks so very much for your many contributions, and happy 2010!!!

Steve Endow said...

Hi Christina,

Glad you enjoyed it. This little project one was one of the most interesting and unusual ways of integrating with GP that I've seen.

And don't get me started on that Integration Manager mess... The things Propellerheads create! They think they are being clever, but they are just creating a monster.

Edwin F. López A. said...

Hi There.

It was very interesting what you have achieved on new year's eve...

But if you want a real challenge (and find fun in the run), try this (related to this post and a customer's request we could not fulfill as requested at the time):

From the InterCompany Audit Trail Inquiry window, use the given INTERID value to open a new instance of GP if the current company differs from it, logon on that company and make that second GP instance to drill down to the given account information after a click in an added button. It is like GP talking to itself (but in a different dimension :-) )

Good Luck!
(did I mention this was a real request we had from a customer?)