Thursday, December 21, 2006

Memory usage of axapta task

On a job client we had some troubles with memory wastage.
The reason was an external application with which we communicate across an OCX.
On each call the application needs more and more memory.
Our experience shows, on about 100MB Axapta crashed.
To solve this problem, our idea was, to monitor the used memory and if axapta reaches an defined limit, to shut down the axapta client.
With windows task manager it should be restartet automatic.

Now, after approximately three weeks in production system, we can say it works without problems, it works marvelously.


Solution:
Three new WinAPI-functions:


getProcessMemoryInfo
Retrieves information about the memory usage of the specified process.
Link to ms...


static container getProcessMemoryInfo(int _processHandle)
{
#define.structSize(4*10)
DLLFunction _getProcessMemoryInfo ;
Binary struct = new Binary(#structSize);
DLL _dLL = new DLL(#PSAPIDLL);
_getProcessMemoryInfo = new DLLFunction(_DLL, 'GetProcessMemoryInfo');

_getProcessMemoryInfo.returns(ExtTypes::DWord);

_getProcessMemoryInfo.arg(ExtTypes::DWord,
ExtTypes::Pointer, ExtTypes::DWord);

struct.dWord (#offset0, 0 );
struct.dWord (#offset4, #structSize );
struct.dWord (#offset8, 0 );
struct.dWord (#offset12, 0 );
struct.dWord (#offset16, 0 );
struct.dWord (#offset20, 0 );
struct.dWord (#offset24, 0 );
struct.dWord (#offset28, 0 );
struct.dWord (#offset32, 0 );
struct.dWord (#offset36, 0 );

if (_getProcessMemoryInfo.call(_processHandle, struct, #structSize))
{
return [struct.dWord(#offset0),
struct.dWord(#offset4),
struct.dWord(#offset8),

struct.dWord(#offset12),
struct.dWord(#offset16),
struct.dWord(#offset20),
struct.dWord(#offset24),
struct.dWord(#offset28),
struct.dWord(#offset32),
struct.dWord(#offset36)];
}

return conNull();

}


getCurrentProcessId
Retrieves the process identifier of the calling process.
Link to ms...

client server static int getCurrentProcessId()
{
//The return value is the process identifier of the calling process.

DLL winApiDLL = new DLL(#KERNELDLL);
DLLFunction getCurrentProcessId = new DLLFunction(winApiDLL, 'GetCurrentProcessId');

getCurrentProcessId.returns(ExtTypes::DWord);
return getCurrentProcessId.call();
}

OpenProcess
Opens an existing local process object.
Link to ms...

static int openProcess(boolean _openForQuery = true, int _processId = winapi::getCurrentProcessId())
{
//If the function succeeds, the return value is an open handle of the specified process.

#define.PROCESS_QUERY_INFORMATION (1024) //&h400
#define.PROCESS_VM_READ (16) //&h10
#define.PROCESS_ALL_ACCESS (2035711) //&H1F0FFF
DLL _winApiDLL = new DLL('KERNEL32');
DLLFunction _openProcess = new DLLFunction(_winApiDLL, 'OpenProcess');
int call = _openForQuery ? #PROCESS_QUERY_INFORMATION : #PROCESS_ALL_ACCESS;
;
_openProcess.returns(ExtTypes::DWord);
_openProcess.arg(ExtTypes::DWord, ExtTypes::DWord, ExtTypes::DWord);
return _openProcess.call(call, 0, _processId);
}

Usage:
Simple job:
static int getCurrentMemoryUsage(boolean _kiloByte = true)
{
hwnd curhwnd = winapi::openProcess();
container memInfo;
;
memInfo = winapi::getProcessMemoryInfo(curhwnd);
winapi::closeHandle(curhwnd);
if(_kiloByte)
return conpeek(memInfo, 4) / 1024;
else
return conpeek(memInfo, 4);
}

Thursday, December 07, 2006

RunBuf (Evaluate), a wonderful function

"RunBuf" or in other development environment "evaluate" is a very nice function,
it helps me to save a lot of time for to develop a complex parameter table stucture

Call the function below with something like this
evaluateFormularWithCommon(SalesTable::find("252"), "SalesTable::find(_common.(fieldnum(SalesTable, SalesId))).SalesName")
you get the value of the field "Name" of the salesTable!

This looks like an stupid idea, but in some situation it could be very helpfully!
In our situation i have an common as table variable and an string which comes from an parameter table.
So the function returns the value of the formular-string in the Parameter-table. Very Nice!



static container evaluateFormularWithCommon(Common _common,
str _formular)
{
/*
franz aigner 20061127
returnvalue:
[Result (str), ReturnState (boolean)]
ReturnState: False --> Error, True --> OK

Instruction:
Sampleformula:
SalesTable::find(_common.(fieldnum(SalesTable, SalesId))).SalesName


in this sample, in the formula the reference of the source table must be the string '_common'
no semicolon at the end of the formular
the return value must be a string
*/
XPPCompiler comp = new XPPCompiler();
str formular;

str toStr(AnyType _value)
{
;
if(typeOf(_value)==Types::String || typeOf(_value)==Types::VarString)
{
return strFmt("%1", _value);
}
return strFmt("%1", _value);
}
;
formular = 'AnyType evaluateFormularWithCommon(Common _common){return '+_formular+";}";

if(comp.compile(formular))
return [tostr(runbuf(formular, _common)), True];
else
return ["", false];
}



Navison Axapta Help:
RunBuf

Anytype RunBuf(str job, ...)

Description
RunBuf executes the X++ job represented as text in job.
Job is a complete piece of X++ code and arguments can be specified by
appending them as arguments to RunBuf.
If the job returns a value, this value is the return value from RunBuf.
Otherwise RunBuf returns 0 (zero).

Example
str myjob = "int myfunc(int i){ return i+7;}";
print runbuf(myjob,5);
will print the number 12 when executed.


Info:
This idea comes from tabax X++calculator!!