Infolog stack trace

August 20, 2008

download (7K) | This is an extension allowing to see stacktrace for each infolog item.

Installation

  • import Dev_StackTrace.xpo
  • recompile
  • add the following code to the \Classes\Info\add:

// RPT DevTools bmi 060915 --->
if(hasSecuritykeyAccess(securityKeyNum(AdminMisc), AccessType::Edit))
{
_sysInfoAction = new SysInfoAction_StackTrace(_txt, xSession::xppCallStack(), _sysInfoAction);
}
// RPT DevTools bmi 060915 <---
  • compile the info class

Usage

When infolog record is outputed this extension adds an action to it
named “Stack trace” (it is only for users with admin rights). Just
press the button and see stack trace.

If the record has an action already, you can see stacktrace by holding Ctrl button while clicking on the action button.

Advertisements

Image Class

August 20, 2008

Image is a system class for handling image data.

The class can construct and manipulate Image objects of the following file types:

  • Raster (bitmap) formats – .bmp, .gif, .jpg, .png, .tiff, and .exif
  • Vector formats – .emf and .wmf

Because of security reasons the class in Dynamics Ax can not be run from the server and is bound to the client.

The image object can be created from scratch, captured from the screen
or loaded from a file. It can then be cropped, resized, flipped,
rotated etc…

Load an image from file and place it in a table:

Image      image;str        fileName;;

filename = myImagePath + myImageFile;

if (Image::canLoad(Filename))
{    
   image.loadImage(Filename);    
   myTable.imageContainer = image.getData();
}

…and to display the image on a form:

Image   Image;container imageContainer;;

imageContainer = myTable.imageContainer;

Image = new Image();
Image.setData(imageContainer);

imageWindow.image(Image);
imageWindow.widthValue(image.width());
imageWindow.heightValue(image.height());

Specifying empty date values

August 19, 2008

If you want to set a date field to an empty date or need to compare if a date field is empty, you can use the datenull() function. It is a method of the Global class and will simply return 011\1900.

This leads to more things:

  1. You can write dates in Axapta X++ editor using backslashes, e.g. 144\2006
  2. An empty date in Axapta is specified as January 1st, 1900.
  3. You
    can use methods from the Global class without having to write
    “Global::”. So, you can write “if date == datenull()” and you do not
    have to write “if date == Global::datenull()”. This works with all the
    methods from the Global class.

Using global search for virtual tables

August 19, 2008

The global search is a feature in Dynamics Ax 4.0 which allows you to
search for a string in several tables (like a full text search). You
setup some tables and fields to be searched in and start a data crawler
that collects the data to be searched.

Now, if some of your
tables that are searched belong to a virtual company, the data crawler
will mark them as records of the company it is running in. That means
that if you want to search for your virtual data from a different
company, it will return no results.

Example: you have the
companies xxx and yyy and a virtual company vir. Table CustTable is
virtual (dataareaid of the records is vir). The data crawler runs in
company xxx and will mark the CustTable records as belonging to company
xxx .
Now, if you start a search in company yyy, it will not find
the Custtable records as they seem to belong to a different company.
You could only set up a second data crwaler for company yyy which would
collect exactly the same records and you would need to store them twice
in your database.

The following changes will circumvent that:
you will be able to see data from different companies. There are some
drawbacks, however: you will be able to see search results from your
“data crawler company”. But it the data is from a non-virtual table,
you will not be able to see the results. But I hope it will lead you to
a way where you can make your own modifications to get the best out of
the global search. Remember: all changes you make are at your own risk.

Here are the changes you have to do:

Class SysGSSearchStart, method startSearch
comment the following line:

infolog.add(Exception::Warning,”@SYS98793″);

With that, there will be no warning if you are working in a company where the data crawler is not running.

Class SysSearch, method search:

at line 28, just after “if (!searchname)” add:

select firstonly RecId from sysSearchName
where sysSearchName.Design == ‘SDS_xxx_default’
&& sysSearchName.LanguageId == this.languageId();

if (!sysSearchName)

replace the xxx in the ‘SDS_xxx_default’ with the company id where the data crawler is running.

Class SysSearch, methods searchWord and searchExactWord:

at line 11, replace the “where sysSearchName.Design == this.design() &&” with:

where (sysSearchName.Design == this.design()
sysSearchName.Design == ‘SDS_xxx_default’) &&

again, replace the xxx in the ‘SDS_xxx_default’ with the company id where the data crawler is running.

Class SysSearchDoDataSearch, method buildItemListXML:

at line 11, after a while select indextable from sysDataSearch block, add the following code:

changecompany(‘xxx’)
{
sysDataSearch = null;
while select IndexTable from sysDataSearch
{
dictTable = new DictTable(sysDataSearch.IndexTable);
if (dictTable.rights() != AccessType::NoAccess)
searchTableMap.insert(dictTable.id(),0);
}
}
sysDataSearch = null;

Replace ‘xxx’ with the company id where the data crawler is running.

At line 77, after select sysDataSearch where sysDataSearch.SearchGroupId == m_eSearchGroupDef && sysDataSearch.IndexTable == tableid; add the following code:

if (!sysDataSearch)
{
changecompany(‘xxx’)
{
sysDataSearch = null;
select sysDataSearch
where sysDataSearch.SearchGroupId == SearchGroupDef
&& sysDataSearch.IndexTable == tableid;
}
}

Replace ‘xxx’ with the company id where the data crawler is running.


Using the Windows API from within Axapta

August 19, 2008

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;
}


Configuration in Title bar

July 22, 2008
Configuration in title bar

One common complain with Dynamics Ax 4.0 is that it is not possible for the user to determine in which environment they are working. Read the rest of this entry »


reset temporary table

July 22, 2008

You can use 3 ways: 1 is slow, 2 are fast.
Read the rest of this entry »