Saturday, January 4, 2020

WinAPI::getTempFilename() in .NET Business Connector on a x64 machine

A call on the external method GetTempFileNameW on Kernel32 will raise an exception when called from the .NET Business Connector which is running on a x64 machine.

This problem applies at least in AX 2009 (may be on AX 4.0 and AX 3.0 too).

As a workaround you can use code such this:

// returns a filename in the temporary directory
fileName = System.IO.Path::GetTempFileName();

or

fileName = CLRInterop
    ::getAnytypeForObject(System.IO.Path::GetTempPath())
    + curuserid() + int642str(CLRInterop
        ::getAnyTypeForObject(netTime.get_Ticks());


However don't forget to delete the temporary file after, since it makes no sense to let garbage alive.

Update_recordset and arrays from joint tables

The database modification command update_recordset gives you nice possibility to modify data in a fast way.

Unfortunately this kernel command a some little bug if you are using an array element of a joint table for value assignment. The follow update_recordset will be partial ignored, specific from the assignement from the array element custTableRead.Dimension[2]. This assignment and any following field value assignment will be left out (so the assignment of NameAlias with the value "any Alias" will just fail too):

CustTable    custTableUpdate;
CustTable    custTableRead;
;

ttsbegin;

update_recordset custTableUpdate 
    setting Street = 
        custTableRead.Street, 
    Name = custTableRead
        .Dimension[2], 
    NameAlias = "any Alias"
        where custTableUpdate
            .AccountNum 
            == "00000001"
        join custTableRead
            where custTableRead
                .AccountNum 
                == "00000002";

info(strfmt("%1", custTableUpdate
    .RowCount()));

select firstonly custTableUpdate 
    where custTableUpdate.AccountNum
        == "00000001";

// works fine so far
info(custTableUpdate.Street); // you didn't get what you expect info(custTableUpdate.Name);
// you didn't get what you expect
info(custTableUpdate.NameAlias); 
ttscommit;




But all works fine as long as you code it without any array field:

update_recordset custTableUpdate 
    setting Name = custTableRead.Name, 
        NameAlias = "any Alias"
        where custTableUpdate.AccountNum 
            == "00000001"
        join custTableRead
            where custTableRead.AccountNum 
                == "00000002";


Also works fine too, if the array field is hosted by the same table as the table which is selected for update.

update_recordset custTableUpdate 
    setting Name = custTableUpdate
        .Dimension[2], 
        Street = custTableRead
        .Street
        where 
            custTableUpdate
                .AccountNum
            == "00000001"
        join custTableRead
            where custTableRead
                .AccountNum
            == "00000002";

To make a assignment from custTableRead.Dimension[2] to custTableUpdate.Name to work, you have to do it the old fashion way: select first, then update. Well, the execution of this code is slower, but it works (and that's prior in my eyes :).

ttsbegin;

select firstonly Dimension 
    from custTableRead
    where custTableRead.AccountNum
    == "00000002"
    join forupdate custTableUpdate
        where custTableUpdate
        .AccountNum == "00000001";
        
custTableUpdate.Name = custTableRead
    .Dimension[2];
custTableUpdate.update();
        
ttscommit;

This behaviour relates to AX 2009 only and (if not fixed in the meantime) later versions, since the ability to use joins in update_recordset was invented in version AX 2009.

Select Group by and Join Order By

Take care if you mix Group By and Order By in select statements. Queries like that will bring you data from the Data Base, but you loose data; Data retrieved form tables in Order By mode will not be available.

The code example below retrieves the desired due dates from table CustTransOpen, but the vouchers from table CustTrans are missing:

CustTable custTable;
CustTrans custTrans;
CustTransOpen custTransOpen;
;

while select CustGroup from custtable 
    group by CustGroup // group by
    join Voucher 
    from custTrans // order by
         where custTrans.AccountNum 
             == custtable.AccountNum
         join DueDate from custTransOpen 
             group by DueDate // group by
             where custTransOpen.RefRecId 
                 == custTrans.RecId
{
    info(custTable.CustGroup); // works
    info(custTrans.Voucher); // works not
    info(Date2StrUsr(custTransOpen
        .DueDate)); // works
}

Add a 'group by' to CustTrans, and you get CustTrans data too:

CustTable custTable;
CustTrans custTrans;
CustTransOpen custTransOpen;
;

while select CustGroup from custtable 
    group by CustGroup // group by
    join Voucher, RecId from custTrans 
        group by Voucher, RecId // group by
        where custTrans.AccountNum 
            == custtable.AccountNum
        join DueDate from custTransOpen 
            group by DueDate // group by
            where custTransOpen.RefRecId 
                == custTrans.RecId
{
    info(custTable.CustGroup); // works
    info(custTrans.Voucher); // works now
    info(Date2StrUsr(custTransOpen
        .DueDate)); // works
}

This issue applies to queries proceeded with a QueryRun object too.

Still not clear, if the source of this behaviour is driven by AX or by the SQL engine. But fact is, the mix of Group By and Order By raises hard finding bugs.

Wednesday, April 25, 2012

Blog ceased

Hi there

I'm leaving Dynamics AX and so this Blog won't carry on. Thank you for reading the posts and - well, happy Daxing!

Btw.: If you need a new desktop wallpaper - here is one :) klick me!

Friday, January 6, 2012

New major release for SmartStart3000

Finally, and with huge delay (schedule for publish was October, but then, yeah - time flies, you know), the new version of SmartStart 3000 V.1.7 is now released.

Most significant feature is the AX 2012 support. Also some shortcuts for tools of AX 2012 are added.

Another new thingy is the free choice of the system tray icon. You can now refer to your favorite icon file; it will be used as Systray Icon for SmartStart. And the GUI has delighted with tool tips.

Several bugs have been fixed and help file is updated. And, even I gave my best, probably some new bugs found their way in. Please let me know if you have any trouble.

You can find the new release as usual on the SmartStart 3000 Central. There you find the download, product description, language support, help file, feedback form and so on ...

Many thanks to the translators!

Known Issue: The graphic rendering on Windows Vista and Windows 7 is not as smooth as it should be. In a funny way, this does not concern Windows XP and older Windows versions. But to optimize the graphics you can size the buttons to 48 pixels. I will keep an eye on that issue and fix it with a minor/revision release.


Wednesday, December 21, 2011

ChangeCompany: common.company() vs. common.dataAreaId

When you use the changeCompany(...) functionality and the company should be retrieved by a record's company you should always use

changeCompany(common.company())
{
    // do something in the specific company
}
instead of

changeCompany(common.dataAreaId)
{
    // do something in the specific company
}

Why? Because the dataAreaId can contain a virtual company aswell as a normal company, and if you try to do changeCompany(virtualCompany), this will interrupt with a runtime error. But the company() property returns always the selectable company which can properly used in a changeCompany(...) statement.

This works also with table views and crosscompany selections:

while select crosscompany:['Company1', 'Company2'] common
{
    changeCompany(common.company())
    {
        // do something in the specific company       
    }
}

By the way, probably unknown to much people, this company(...) property can be used before data modification to dictate operation company:

    // current company is Company1
    common.company('Company2');
    select firstonly common; // this will return the first row in Company2 even you are in Company1!

I think, this can minimize the use of changeCompany(...) if used wisely.

Tuesday, June 14, 2011

Change your Windows 7 into the Dynamics OS

Hi there

However, this time a funny post. Have you ever considered to change the Windows 7 Start Button (the blinky orb on the left in the task pane)? Since it is just a ressource in explorer.exe, it is quite easy to do.

  1. Download the 'Windows 7 button changer' from here:  http://luegisdorf.ch/w7btnChange2.6.exe (of course you can find it elsewere on the web too)
  2. Download the 'Dynamics Orb' from here: http://luegisdorf.ch/AX/6801_dynamics.bmp
  3. Make sure, your user has local administrator rights for the current machine
  4. Change properties for explorer.exe in (...\Windows\explorer.exe) as follow:
    1. Change owner to your account (security, advanced, owner)
    2. Change security settings to allow full access for current user
  5. Open the 'Windows 7 Button Changer' application, select the the downloaded bitmap file
  6. Revert settings for explorer.exe (remove rights, reset owner)
  7. Enjoy your new Dynamics OS!
And that's how it looks:
Idle

Hover


Pressed










Of course, you need to run the ''visual style", not the "classic style" ...