Friday, December 1. 2006Using the Windows API from within AxaptaIt 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:
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:
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:
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:
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:
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:
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:
Compile and run the form, and you should have the correct icon for the file you specified. If you were too lazy to build it as we went, you can download my example and import it into your own Axapta system. Trackbacks
Trackback specific URI for this entry
Comments
Display comments as
(Linear | Threaded)
priyan on :The author does not allow comments to this entry
|
Calendar
Creative Commons |