Using the Windows API from within Axapta

It seems that calling Windows API functions from Axapta is viewed as some
sort of mysterious magic by most Axapta developers. Unfortunately,
there are times when you need to do something that requires tight
integration with functionality that exists outside of the system. While
there are other methods of interfacing between bits of code, including
COM, XML-RPC, or even .NET, using the Windows API can sometimes be very
efficient, and powerful.

For beginner WinAPI coders who are also Axapta programmers, with at least some programming experience in C or C++,
I felt it might be better to answer a question from my colleagues about
calling WinAPI calls from Axapta, outside of those already wrapped by
the WinAPI:: or WinGDI:: classes.

Axapta
provides basic functionality to open a handle to a library (DLL file),
and a handle to a function within that library. The functionality is
handled within two classes, DLL:: and DLLFunction:: respectively, and allows you to call practically any piece of code within any dynamic link library.

The most common use for this is naturally to make calls into the
Windows API, but there’s no reason why you can’t provide your own
functions within a DLL and call them from Axapta, but be aware that the
DLL file must be installed on either the client or server, depending on
where the code will execute.

The DLL:: class opens a DLL file and acts as a handle for DLLFunction::
to use when defining handles for calls. If the DLL isn’t already opened
by Axapta internally (as would be the case with the standard Windows
API DLL files), it will also silently handle the opening,
initialisation, and closure of the DLL in conjunction with the garbage
collector.

The DLLFunction:: class defines a handle to a function’s
symbol within the DLL, and defines the return value and the parameters
of the function. This is important to define correctly, as defined by
the C/C++ headers; otherwise you will head straight into problems with
Axapta crashing, either during the call, or later on. Don’t worry, this
is easily determined, and I’ll explain how this works. If you don’t
have the header files (*.h) with a windows compiler, you can look at Google Code Search.

Before going into detail with how this works, you might need to know
what’s going on behind the scenes, especially how functions call other
functions from the computer’s perspective. Since that’s not really in
scope here, I’ll very quickly describe only the elements of this you
need to know to make your calls work from Axapta. If you already know
this, you can skip past it.

When you call a function, its parameters get pushed onto the stack, which is a FILO
data model. The function being called then takes these items off the
stack and can use them as variables. The calling function knows
precisely how many items it needs to put onto the stack, and the
function itself knows how many items it needs to take off the stack.

As you know, once a called function has done its work, it can return a value. The process works the same way in reverse, pushing the return value onto the stack for the caller to pop from the stack.

Sometimes an example helps, so let’s consider the following simplistic example in C:

int foo(int bar, char* baz)
{
printf(“Qux: %s”, baz);

return (bar * 2);
}

void quux(void)
{
int corge = foo(10, “Grault”);
}

Consider when the quux() function calls foo(), it will push the value 10 and a pointer to the null-terminated string “Grault” onto the stack, and jump to the location in memory where the code for foo() exists. Subsequently, foo() will pop these two values from the stack to be able to use them. When foo() returns, it will push the value of 20 onto the stack, and quux() will pop that from the stack and use it as the value for corge.

Of course, there’s a lot more going on in the stack, in fact there are a few stacks, but we don’t need to cover that here.

To be able to call your function, you will need to know which DLL
your function is in, and the real name of the function as exported by
the DLL’s symbols. I mention this because it seems obvious, but you
should remember that Microsoft generally provide two versions of
functions if they use strings, for ANSI and wide-characters, normally
with an “A” or “W” suffix respectively. The headers
for these functions often hide this fact, as does MSDN. Opt for the
ANSI version – Axapta will convert strings based on the locale.

To cover the subject quickly, let’s build a small example form that
uses the Windows API to draw the appropriate icon for a specified file
within a window pane.

Build a form with a Window control. In this example, I have named the window Icon. The window control needs have the width and height both set to 32 pixels (icon size), and the AutoDeclaration property enabled.

The form needs some code. Add some variables we will need to the class declaration of the form:

public class FormRun extends ObjectRun
{
// DLL file handles
DLL shellDLL; // shell32.dll
DLL userDLL; // user32.dll

// Function call handles
DLLFunction extractAssociatedIconFunc;
DLLFunction destroyIconFunc;
DLLFunction drawIconFunc;

// Our icon handle, taken from windows
int hIcon;

// These are from the windows headers, used later in the code!
#define.DI_MASK(0x01)
#define.DI_IMAGE(0x02)
#define.DI_COMPAT(0x04)
}

We need to initialise many of these variables first. Using the init()
function on the form, we can initialise the DLL handles, and the
functions. The DLL handles only need the name of the DLL file,
obviously, but the DLL function handles need to be configured with the
primitive types that will be appearing on the stack. Even parameters or
the return value are not used, you must specify them, or the stack will
be out of synchronisation.

To help you define your primitive types, Axapta has a kernel enumerator called ExtTypes,
which contains all the basic types you will need. You need to then take
what’s listed in the header file and determine what primitive type it
is. For example, windows defines many functions which return a value of
BOOL, however this is simply a type definition in C for an
integer. It’s your responsibility to match these values up logically by
working through the definitions. Don’t worry; I’ve done the hard work
for you in this example!

Override your form’s init() function with the following code:

public void init()
{
;

// Initialise the form first
super();

// Set up our call for ExtractAssociatedIcon()
extractAssociatedIconFunc =
new DLLFunction(shellDLL, “ExtractAssociatedIconA”);
extractAssociatedIconFunc.returns(ExtTypes::DWord); // HICON
extractAssociatedIconFunc.arg(ExtTypes::DWord, // HINSTANCE hInst
ExtTypes::String, // LPTSTR lpIconPath
ExtTypes::Pointer); // LPWORD lpiIcon

// We also need a function to destroy the icon handle once we’ve used it
destroyIconFunc = new DLLFunction(userDLL, “DestroyIcon”);
destroyIconFunc.returns(ExtTypes::DWord); // BOOL
destroyIconFunc.arg(ExtTypes::DWord); // HICON hIcon

// This is our call for drawing the icon onto a window
drawIconFunc = new DLLFunction(userDLL, “DrawIconEx”);
drawIconFunc.returns(ExtTypes::DWord); // BOOL
drawIconFunc.arg(ExtTypes::DWord, // HDC hDC
ExtTypes::DWord, // int xLeft
ExtTypes::DWord, // int yTop
ExtTypes::DWord, // HICON hIcon
ExtTypes::DWord, // int cxWidth
ExtTypes::DWord, // int cyWidth
ExtTypes::DWord, // uint istepIfAniCur
ExtTypes::DWord, // HBRUSH hbrFlickerFreeDraw
ExtTypes::DWord); // UINT diFlags
}

When the form runs, we want the icon to be updated with an icon from a file. To do this, we will override the run method on the form. After the form has started normally, we will make a call to ExtractAssociatedIcon() to find our hIcon (icon handle) value. We’ll save this value for later use. Once this is done, we’ll need to draw the icon for the first time.

Axapta provides one additional bit of help here for complex
structures containing primitive types, or any other place where you
need to pass a pointer on the stack. The kernel class Binary::
lets you allocate memory in the system, and have it freed by the normal
Axapta garbage collection routines. This of this as your answer to malloc(), and also your answer to creating and reading C-style structures.

Override your form’s run() method with the following, and feel free to change the file to any file on your system:

public void run()
{
Binary binary;
;

// Run the form normally
super();

/* We need to build a binary item for two bytes (a short), since windows
* has an “in/out” pointer for the icon information.
*/

binary = new Binary(2); // 2 bytes = short

// Call the ExtractAssociatedIcon() function
hIcon = extractAssociatedIconFunc.call(NULL,
“C:\\WINDOWS\\EXPLORER.EXE”,
binary);

// Make sure we got an icon (we should, if the file exists)
if (!hIcon) {
throw error(“Unable to get hIcon”);
}

// Do the initial drawing of the icon
this.drawIcon();
}

Since the Windows API will allocate stuff on its side, we need to
clean up some memory when the form is closed. To do this, we call DestroyIcon() with the icon’s handle. Override your form’s close() method with the following snippet:

public void close()
{
;

// If we have an icon handle, we need to clean up
if (hIcon) {
destroyIconFunc.call(hIcon);
}

// Close the form
super();
}

Now the fun part: We need to grab the device context of the window,
and use it to draw the icon on to the form. We will use the function DrawIconEx().
Note that we need to lock the device context of the window before
passing it outside for the library to chew on. Add the following method
to your form:

public void drawIcon()
{
;

// Lock the device context of the window so we can use it
Icon.lockDC();

// Paint the icon (we’ll use the window size to determine the icon size)
drawIconFunc.call(Icon.hDC(), 0, 0, hIcon,
Icon.widthValue(),
Icon.heightValue(),
0, NULL,
(#DI_IMAGE | #DI_MASK | #DI_COMPAT));

// Unlock the device context of the window
Icon.unlockDC();
}

Finally, you will also need to override the function paint()
on the Window control. The following code will be called by Axapta
whenever Windows asks Axapta to paint the window. This allows you to
redraw the icon when the form is covered or uncovered by another window
in the foreground, for example. Without this, your icon will disappear
if anything other than the mouse moves over it! Add this code to the Icon control:

public int paint()
{
int ret;
;

// Paint the window first
ret = super();

// Draw the icon
element.drawIcon();

return ret;
}

Advertisements

One Response to Using the Windows API from within Axapta

  1. Simon says:

    Stealing isn’t nice. You don’t even credit my work?

    I wrote this originally:

    http://simon.butcher.name/archives/2006/12/01/Using-the-Windows-API-from-within-Axapta

Leave a Reply

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: