Welcome to MSDN Blogs Sign in | Join | Help

I wrote some moths ago an article about the GC and highlighted some particularities (unsafe and unmanaged code, disposing and finalizing, etc).

When you are developing with .Net you will be sometimes faced to memory or resource leaks. Especially if you are using the BC.Net, since the BC.Net is using unsafe and unmanaged code. But even with X++ you will be more and more faced to such problems. So it is very useful to know as much as possible about causes, identification and resolving.

Fabrice Marguerie (co-author of LINQ in Action) wrote a great article on MSDN (his blog entry for the trackback) about this subject. Enjoy this article!

As I already wrote it in the update of that blog entry in my first posing about this issue:

The origin of this issue can be found in the method 'buildEnumerationXml' of the class 'SysExpression':

Currently, a loop iterates until the last element:

for (i = sysDictEnum.firstValue(); i <= sysDictEnum.lastValue(); i = sysDictEnum.nextValue(i)) 

I you replace this (sysDictEnum.lastValue(); ) by the number of elements (sysDictEnum.values();) the issue is solved.

[UPDATE 03/09/2009]

The hotfix for this bug has been published: KB974925 (Build: 5.0.1500.1255).

You can find a very good description on our team blog (EMEA Dynamics AX Support) how to import leads and contact Xml files with Dynamics Ax 2009 here. The problem you will be faced by using this is, that you can’t import data with non Ascii characters, which might cause some difficulties. The reason for that is the way how the Xml files is proceeded during the transformation process.

Ax creates first a copy of the Xml file and the Xslt File in the temp directory of the AOS which is by default:

C:\Program Files\Microsoft Dynamics AX\50\Application\appl\DynamicsAx1\tmp\

Unfortunately this is done by reading out the Xml file into a container and then creating a new Xml file by using the method:

SysImportUtil/createFileFromContainer

Unfortunately because it is using the AsciiIo to create the file. Since this class is intended to encode the content of the container into Ascii, it is  incompatible with Xml files. The sample files are using Xml files encoded in Utf-8 which is some kind of an extension to Ascii and this is the reason why you can’t use this import for non Ascii characters:

[..] AsciiIO supports only ANSI code page (ACP) characters.

But you can fix this by replacing the AsciiIo by the TextIo class. Msdn describes it as follow:

TextIO replaces AsciiIO to provide support for non-ANSI code page file I/O. The TextIO constructor has an additional optional parameter to set the code page of the file.

This is exactly what we need. The most common codepages are:

  • 0 - CP_ACP; the ANSI code page
  • 437 - OEM code page 437
  • 850 - Code page 850
  • 1200 - UTF-16LE (default value)
  • 1201 - UTF-16BE
  • 65001 - UTF-8
  • 54936: GB-18030

So, if you want to use the Xml-files from the example without any modification you need to change the codepage to 65001, but it is easier to change the Xml-file to Utf-16. This can be done easily by using Visual Studio:

Just open the Xml-file, change the Utf-8 to Utf-16 and safe the changes. Why not using the notepad ?

Visual Studio reads out the Xml encoding information:

<?xml version="1.0" encoding="utf-16"?>

and adds the required file signature to that file (called BOM). These information can be consulted with the Visual Studio binary editor (File/Open/File/<select Xml-file>/Open as/binary editor): “EF BB BF” for Utf-8 and “FF FE” for Utf-16.

So in order to import data with non-Ascii data, you have to:

  1. replace the AsciiIo class by TextIo
  2. import Utf-16 encoded Xml data.

The Template Wizard (Administration/Periodic/Data export/import/Excel spreadsheets/Template Wizard) creates unfortunately SheetNames that might exceed 30 characters, which is not allowed by Excel and results in annoying message boxes that are requesting a new SheetName with less than 31 characters.

The following code change in the method SysDataExcelDef/buildTmpExcelWorksheet will resolve this problem:

   1:          //modifications done for working with long tablenames
   2:          //    str tempSheetName;
   3:          //    str suffixName;
   4:          //    int suffixlength;
   5:          //original code:
   6:          //tmpExcelWorksheet.SheetName = tmpExcelWorksheet.TableName + '_' + int2str(sysExpImpTable.OccurrenceId) + '-1';
   7:          
   8:          tempSheetName = tmpExcelWorksheet.TableName + '_' + int2str(sysExpImpTable.OccurrenceId) + '-1';
   9:   
  10:          if (strlen(tempSheetName) > 30)
  11:          {
  12:              tempSheetName = tmpExcelWorksheet.TableName;
  13:              suffixName =  '_' + int2str(sysExpImpTable.OccurrenceId) + '-1'; //as this is done in the original code
  14:              suffixLength = strlen(suffixName);
  15:              tempSheetName = substr(tempSheetName, 0, 30 - suffixLength);
  16:              //assigning the new name
  17:              tmpExcelWorksheet.SheetName = tempSheetName + suffixName;
  18:          }
  19:          else
  20:          {
  21:              tmpExcelWorksheet.SheetName = tempSheetName;
  22:          }

If you are faced to 'large' Xml-documents with AIF-WebServices, you might be faced to difficulties for Inbound and OutBound services.
The following two points should be applied for the client app.config, if the client uses the WCF for communicating with the AIF-WebService (Inbound-Services - you are sending large Xml-Documents to the AIF-WebService), and on the server web.config (Outbound-Services - you are receiving large Xml-documents from the WebService)

1)
Changing the web.config by adding the httpRuntime Xml-element:

<configuration>
  <system.web>
    <httpRuntime executionTimeout="timeout in seconds"  maxRequestLength="size in kilobytes" />
  </system.web>
</configuration>

And choose a value for the Xml-Attribute executionTimeout that fits to the execution duration of the process.

The maxRequestLength (default value is 4096 KB) should not exceed a maximum file size of 20 MB to restrict possible denial of service attacks. Keep this value as low as possible nevertheless.

2)
Increase the maxReceivedMessageSize (for the asmx-compatible and WS-binding)  value for:

      <wsHttpBinding>
        <binding name = "wsHttpWindowsAif"
                 maxReceivedMessageSize ="size in kilobytes">
        </binding>
      </wsHttpBinding> 

By default this value is 4096 KB and for most Xml-documents not enough. You might have tried the MaxMessageSize as described here, but this attribute has been replaces by maxReceivedMessageSize.

Numeric values that are 0 (not null) are not serialized and don't appear in the Xml file. The reason for this is, that the numeric types in Dynamics Ax are not nullable (must have a value) and do interpret the value 0 as a "null value" (0 as a ‘magic number’) during the serialization.

The only possibility is to declare these columns as mandatory, so that they are serialized even with 0 as value. Another way is to inform the consumer application (which might be BizTalk) that non mentioned values should be deserialized with a default value (in this case with 0).

When executing an AIF-service with the COM and .Net Business Connector the client will not be notified about exceptions during the execution. The reason for this is, that AIF-services are always executed within a transaction (ttslevel = 1) even when no ttsBegin has been executed. And as described on the X++ blog, exceptions are not caught within the transaction (sample is taken from the X++ blog):

   1:  try 
   2:  { 
   3:      MyTableType t; 
   4:      ttsBegin;    // Start a transaction 
   5:      try  
   6:      { 
   7:           throw error(“Something bad happened”); 
   8:      } 
   9:      catch 
  10:      { 
  11:          // Innermost catch block 
  12:      } 
  13:      update_recordset t … // Must run within a transaction 
  14:      ttsCommit;    // Commit the transaction 
  15:  } 
  16:  catch 
  17:  { 
  18:      Info (“In outermost exception handler”); 
  19:  } 
  20:    

The exception thrown in line 7 would be caught in line 16.

If you need to handle the exceptions on the client-side, you can create a workaround by calling a ttsAbort before executing the AIF-service code. The best way to implement this workaround is to create a new AIF-service that executes the ttsAbort before calling the original AIF-service code. In that case the original AIF-service can stay “as is”.

When you get the following exception while executing a report on the SSRS or within Ax:

System.Security.SecurityException was unhandled
Message="Request for the permission of type
'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.”

this is probably because the permission set in the config file of the SSRS - which you can normally find here (SQL 2005):

C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer

is not configured correctly. The exception might be thrown for example because of the following line:

System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();

To resolve that problem you have to look for the PermissionSet that is defined for the assembly that caused the exception. In this case this is System.Security-assembly.

In the web.config you can find the link to the rssrvpolicy.config file

   1:      <securityPolicy>
   2:        <trustLevel name="RosettaSrv" policyFile="rssrvpolicy.config" />
   3:      </securityPolicy>

and in the rssrvpolicy.config the definition of the PermissionSets:

   1:            <NamedPermissionSets>
   2:              <PermissionSet class="NamedPermissionSet" version="1" Unrestricted="true" Name="FullTrust" Description="Allows full access to all resources" />
   3:              <PermissionSet class="NamedPermissionSet" version="1" Name="Nothing" Description="Denies all resources, including the right to execute" />
   4:              <PermissionSet class="NamedPermissionSet" version="1" Name="Execution">
   5:                <IPermission class="SecurityPermission" version="1" Flags="Execution" />
   6:              </PermissionSet>
   7:              <PermissionSet class="NamedPermissionSet" version="1" Name="AxSessionPermissionSet">
   8:                <IPermission class="AxSessionPermission" version="1" Unrestricted="true" />
   9:                <IPermission class="SecurityPermission" version="1" Flags="Assertion" />
  10:              </PermissionSet>
  11:            </NamedPermissionSets>

In the CodeGroup-section are defined:

   1:              <CodeGroup class="FirstMatchCodeGroup" version="1" PermissionSetName="Execution" Description="This code group grants MyComputer code Execution permission. ">
   2:                <IMembershipCondition class="ZoneMembershipCondition" version="1" Zone="MyComputer" />
   3:                <CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust" Name="Microsoft_Strong_Name" Description="This code group grants code signed with the Microsoft strong name full trust. ">
   4:                  <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="002400000480000094000000060200000024000052534131000400000100010007D1FA57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD236132102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC093344D5AD293" />
   5:                </CodeGroup>
   6:                <CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust" Name="Ecma_Strong_Name" Description="This code group grants code signed with the ECMA strong name full trust. ">
   7:                  <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="00000000000000000400000000000000" />
   8:                </CodeGroup>

The tool CasPol.exe (part of the .Net SDK) will help you to identify the CodeGroup that the System.Security assembly belongs to. In the GAC folder of this assembly I did a CasPol –rsg (resolvesGroup) on this assembly:

image

Now we can see that this assembly belongs to the Microsoft_Strong_Name (line 3 in the CodeGroup excerpt) and is configured for FullTrust. With the –rsp (resolvesPermissions) you can visualize all permissions that are granted to this assembly (as defined in the FullTrust named PermissionSet):

image

When the exception was raised, this assembly was configured to ‘Nothing’ (line 3 of the NamedPermissionSets excerpt). The problem was resolved by granting this assembly FullTrust.

You can find a good article about CAS (Code Access Security) for the SSRS on msdn here.

A partner asked me why the base64Encode method of the Image class does always return an empty string. In the following example:

   1:  Image image;
   2:  str sBase64Encode;
   3:  ;
   4:  image = new Image();
   5:  image.loadImage(@'C:\sample.bmp');
   6:   
   7:  sBase64Encode = image.base64Encode();
   8:  info(sBase64Encode);

will display an empty string for the variable sBase64Encode in the InfoLog window.

This is because the base64Encode method is inherited from the BinData class and encodes the file object in the Image class and not the image object which is loaded with the loadImage method. Unfortunately the file object is not initialized implicitly when loading a file, so that you have to call the method loadFile which initializes the file object. Now you have to initialize the image object and this can be done by creating an image object with the method createImage:

   1:  Image image;
   2:  str sBase64Encode;
   3:  ;
   4:  image = new Image();
   5:  image.loadFile(@'C:\sample.bmp');
   6:  image.createImage(10,10,10); //sample values 
   7:   
   8:  sBase64Encode = image.base64Encode();
   9:  info(sBase64Encode);

or by loading the image file:

   1:  Image image;
   2:  str sBase64Encode;
   3:  ;
   4:  image = new Image();
   5:  image.loadImage(@'C:\sample.bmp');
   6:  image.loadFile(@'C:\sample.bmp');
   7:   
   8:  sBase64Encode = image.base64Encode();
   9:  info(sBase64Encode);

The same behavior is true for the class ImageList if you load resources from a dll (for example shell32.dll) with the loadIcons-method into an ImageList-class. In that case it is unfortunately not possible to encode the images. You have to extract the icons and load each icon separately instead. 

You can open the information about employees with the Global Address Book (Basic/Global Address Book) and with the Employees form (Basic/Employees).

Normally it is no problem to modify data in both forms, but unfortunately this is not true for the ‘gender’ property. This information is stored in the DirPersonPartyDetail (Gender) and EmplTable (EmplGender) table. The particularity here is, that the EmplGender property is of type ‘EmplGender’ which has two states (0:Male;1:Female) and the enum ‘Gender’ has three states (0:unknown;1:male;2:female).

If you know change the value for the gender in the GAB it won’t change the value for ‘EmplGender’ and if you change it through the Employees-form it won’t change the value for ‘Gender’.

This is why the gender information of employees must be modified with the Employees-form and not with the GAB.

If you need to synchronize the employees information with the GAB, you can add at the end of the method (tables\EmplTable\ValidateField), the following code :

   1:  //MODIFICATION(04/08/2009): Empl-form is not synchronizing some values with the GAB
   2:          case(fieldnum(EmplTable, emplGender)):
   3:              dirPersonPartyDetail = DirPersonPartyDetail::find(this.PartyId, true);
   4:              if(dirPersonPartyDetail)
   5:              {
   6:                  dirPersonPartyDetail.Gender = enum2int(this.EmplGender);
   7:                  dirPersonPartyDetail.update();
   8:              }
   9:              break;
  10:  //END MODIFICATION

(Thank you to Olivier)

In forms that are using the ActiveX-control ExpressionBuilder (like TrvPolicyForm) you might be faced to the following error message:

Method 'initialize' in COM object of class 'IDynamicsAXExpressionBuilder' returned error code 0x80131509 (<unknown>) which means: End enum not found.

This is caused by an enumerator that is used by the ActiveX-control. The method "initialize" of that ActiveX-control requires in its first parameter the expression in a Xml-document. That Xml-document contains also a list of enumerators.

Find the method, get the content of this Xml-document with the debugger and then list the enumerators that this Xml-document contains. Verify that there is no empty Enum like  for example <enum name="HRMSalutation" />. If you do find such an empty Xml-element, verify the order of the enum-elements in the AOT. Change the order of the elements in the AOT, so that the elements are in the order of its values.

(Update 07/08/2009):

Since I was asked where to find the initailize calls, here the lines:

WorkflowConfigConditionControl.setData:
 conditionControl.initialize(data.parmConditionDesignTimeProjection(), expressionTable.ExpressionDefinition);

WorkflowConfigConditionControl.clicked:
 conditionControl.initialize(data.parmConditionDesignTimeProjection(), expressionTable.ExpressionDefinition);

If this happens in the TrvPolicyForm, just search for 'initialize'.

Just get the Xml-expression for data.parmConditionDesignTimeProjection() and copy and pate the xmlString into any Xml editor (Visual Studio or Xml-Spy). You will find the enumerations used by this expression at the end of the Xml document in the Xml-Element 'enumerations'.

(Update 18/08/2009):

This problems seems to be related to a verification for the last element. So at the end this problem occurs if the last element is not the expected one... Well at least it is now clear how to evoid this strange bahavior.

 (Update 20/08/2009):

The origine of this issue can be found in the method 'buildEnumerationXml' of the class 'SysExpression':

Currently, a loop iterates until the last element:

for (i = sysDictEnum.firstValue(); i <= sysDictEnum.lastValue(); i = sysDictEnum.nextValue(i))

I you replace this (sysDictEnum.lastValue(); ) by the number of elements (sysDictEnum.values();) the issue is solved.

I will do a short Webcast about .Net basics for X++ developers which is based on the articles I posted here recently. During 1 hour I’ll talk about the basics of .Net and how to do’s. If you are a X++ programmer and you are new to .Net, this Webcast might interest you. The Webcast take place on Tuesday April 2nd 2009 at 16:00 GMT+1.

Update: I'll publish the registration link as soon as it will be available. This should be done in the next few days.

Update: Here are the missing information:

Course Name: .net Programming for Dynamics AX developers  

ABOUT THE TOPIC:
-    What’s .Net:
o    The CLR, BCL and FCL
o    Managed code, unsafe code and managed code.
o    GC
o    C#, CIL (aka MSIL) and JIT
o    The assembly and signing assemblies
o    GAC
-    .Net running in 32bit and 64bit
-    .Net with X++
-    Calling X++ with .Net
-    Best practices and anti-pattern

REGISTRATION:
Delegates can book their places on this webcast via the Microsoft Partner Learning Centre using a .NET passport/ Windows Live ID on:
https://training.partner.microsoft.com/plc/details.aspx?publisher=12&delivery=265146

DATES & TIMES: April 2, 2009: 4:00pm – 5:00pm CET (GMT+1), 1 hour
 
LOCATION: Microsoft Office Live Meeting 2007 (upon registration you will receive login details).
 
DELIVERY LANGUAGE: This session will be presented in English. 

IIS 7 changed fundamentally since IIS 6. Here are some links that might help you when you feel lost with all these changes ;-)

IIS 7 Links:

http://technet.microsoft.com/en-us/library/cc732976.aspx   (IIS 7.0: Operations Guide)

http://www.iis.net/ConfigReference  (IIS 7.0 Configuration Reference)

http://www.iis.net/getstarted

IIS 7 breaking changes:

http://mvolo.com/blogs/serverside/archive/2007/12/08/IIS-7.0-Breaking-Changes-ASP.NET-2.0-applications-Integrated-mode.aspx

IIS 7 configuration:

http://mvolo.com/blogs/serverside/archive/2007/07/21/Anatomy-of-an-IIS7-configuration-path.aspx

IIS 7 AppCmd.exe:

http://learn.iis.net/page.aspx/114/getting-started-with-appcmdexe  (Getting Started with AppCmd.exe)

IIS 7 authentication configuration (NTLM/Kerberos):

http://technet.microsoft.com/en-us/library/cc754628.aspx

http://www.iis.net/ConfigReference/system.webServer/security/authentication/windowsAuthentication

 

Service references are are great feature in Dynamics Ax 2009 and using WCF with Ax is now really easy. But unfortunately it is not easy to deploy this service reference. If you now need to deploy a service reference from the development environment to the evaluation environment, or from the evaluation to the production environment, there is no functionality that will automate this. In order to keep all your modification, I would suggest the following procedure:

  1. Make a backup of the app.config of your referenced service
    You will find the app.config in the
    <Dynamics application>\Appl\<instance name>\ServiceReferences\<your reference>
    directory
  2. Export the reference in a xpo-file
  3. Change the WSDL location in this xpo file (if the service has a different Url on the new environment)
  4. Import the xpo file on the new platform
  5. Generate the proxy.
  6. Copy the app.config (from your backup) into the directory of the new referenced service.
    <Dynamics application>\Appl\<instance name>\ServiceReferences\<your new reference>

You probably already noticed that error messages are sometimes not really helpful when you are trying to understand a problem. The following code creates such misleading error message:

   1:  public void run()
   2:  {
   3:  ;
   4:  if(WinApi::folderExists(@"c:\temp\"))
   5:      info("Folder exists");
   6:  else
   7:      info("Folder does not exists");
   8:  }

If you do execute this code in a batch job, it will produce the following error message:

RPC error: RPC exception 1702 occurred in session xxx

1702 means:

The binding handle is invalid.

A complete list of RPC errors is available here.

But this error message has, of course, nothing to do with the real cause of the problem. The RPC 1702 is unfortunately in Ax 2009 (RTM and SP1) always thrown when you call a client method from a batch job. If you have a look at the code of the WinApi::folderExists(str filename):

   1:  client static boolean folderExists(Filename _filename)
   2:  {
   3:      boolean ret;
   4:      ;
   5:      ret = System.IO.Directory::Exists(_filename);
   6:      return ret;
   7:  }

you see that this method can’t be executed on the AOS because of the “client” keyword. It’s true that this should not work and create an error message (since batch jobs are executed on the AOS), but it should normally be a meaningful message.

Instead of calling this client method, you should change your code, so that this call can be executed on the server. For example like this:

   1:  public void run()
   2:  {
   3:      InteropPermission   interopPerm;
   4:      boolean isFolderExist;
   5:      ;
   6:   
   7:      interopPerm = new InteropPermission(InteropKind::ClrInterop);
   8:      interopPerm.assert();
   9:      isFolderExist = System.IO.Directory::Exists(@"c:\temp\");
  10:      
  11:      if(isFolderExist)
  12:      {
  13:          info("Folder exists");
  14:      }
  15:      else
  16:      {
  17:          info("Folder does not exists");
  18:      }
  19:   
  20:      CodeAccessPermission::revertAssert();
  21:  }
 
Update (17/07/2009):
You can find a blog entry from Klaas about this subject here:
http://www.artofcreation.be/2009/04/08/winapi-rpc-1702-and-findfirstfilew/ 
More Posts Next page »
 
Page view tracker