Question

I'm attempting to deflate a .NET GZIPStream in X++ but I'm running into a bug. The .NET code to create the string is pretty simple:

    private string CompressString()
    {
        string stringToCompress = "Some data here";
        string result = string.Empty;

        using (MemoryStream output = new MemoryStream())
        using (GZipStream gzip = new GZipStream(output, CompressionMode.Compress, true))
        using (StreamWriter writer = new StreamWriter(gzip))
        {
            writer.Write(stringToCompress);
            writer.Close();
            result = Convert.ToBase64String(output.ToArray());
        }

        return result;
    }

The AX side will get the compressed string via some web service call. The X++ code I have currently is below, but I'm getting the error "Object 'CLRObject' could not be created" when creating the StreamWriter.

static void Job2(Args _args)
{
   System.String decodedString;
   System.Byte[] buffer;
   System.IO.Compression.GZipStream gzip;
   System.IO.StreamWriter writer;
   System.IO.MemoryStream output;
   InteropPermission permission;
   CLRObject ex;

   str compressedString ="Compressed data here";
   ;

   ttsBegin;
   permission = new InteropPermission(InteropKind::ClrInterop);
   permission.assert();

   buffer = System.Convert::FromBase64String(compressedString);
   output = new System.IO.MemoryStream(buffer);
   gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode::Decompress);

   try {
       //Error here: "Object 'CLRObject' could not be created"
       writer = new System.IO.StreamWriter(gzip);
       writer.Write(decodedString);
       writer.Close();

       CodeAccessPermission::revertAssert();
   } 
   catch (Exception::CLRError) {
       //Code never executes past this point 
       ex = CLRInterop::getLastException();

       while(ex != null) {
           error(ex.ToString());
           ex = ex.get_InnerException();
       }
   }

   ttsCommit;
   info(decodedString);
}

Edit: building on @robert-allen 's answer below, the correct code to accomplish this in AX is:

static void Job2(Args _args)
{
   System.String decodedString;
   System.Byte[] buffer;
   System.IO.Compression.GZipStream gzip;
   System.IO.StreamReader reader; //<-- Reader instead of writer
   System.IO.MemoryStream output;
   InteropPermission permission;
   CLRObject ex;

   str compressedString ="Compressed data here";
   ;

   ttsBegin;
   permission = new InteropPermission(InteropKind::ClrInterop);
   permission.assert();

   buffer = System.Convert::FromBase64String(compressedString);
   output = new System.IO.MemoryStream(buffer);
   gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode::Decompress);

   try {
       //Reader code changes
       reader = new System.IO.StreamReader(gzip);
       decodedString = reader.ReadToEnd();
       reader.Close();
       //End reader code changes

       CodeAccessPermission::revertAssert();
   } 
   catch (Exception::CLRError) {
       //Code never executes past this point 
       ex = CLRInterop::getLastException();

       while(ex != null) {
           error(ex.ToString());
           ex = ex.get_InnerException();
       }
   }

   ttsCommit;
   info(decodedString);
}
Was it helpful?

Solution 2

The problem with Kenny Saelen's approach is that many CLRInterop errors are of such a level that they break through the Dynamics AX X++ catch clause regardless of it being specified to catch an Exception::CLRError. I've encountered this before when using the System.IO methods such as file moves/deletes.

One way to better see your error (since you cant catch it properly in X++) is to rewrite your job in .NET code and use a .NET catch block which is perfectly capable of catching that error. Build the objects in a C# console application and recreate the same behavior encapsulated in a .NET try/catch.

Something like this:

static void Main(string[] args)
{
    System.String decodedString;
    System.Byte[] buffer;
    System.IO.Compression.GZipStream gzip;
    System.IO.StreamWriter writer;
    System.IO.MemoryStream output;

    string compressedString = "sample";

    buffer = System.Convert.FromBase64String(compressedString);
    output = new System.IO.MemoryStream(buffer);
    gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode.Decompress);

    try
    {
        writer = new System.IO.StreamWriter(gzip);
        writer.Write(decodedString);
        writer.Close();

        Console.Write(decodedString);
        Console.ReadKey();
    }
    catch (Exception ex)
    {
        Console.Write(ex.Message);
        Console.ReadKey();
    }
}

Let me know if that works for you Spankachu.

Cheers

OTHER TIPS

Can you add a try catch around your code to see the exact message that you get, because there are 2 errors in Ax that don't really tell you what is going on:

  • An exception has been thrown by the target of an invocation
  • CLRObject could not be created

The first one is .net having trouble in a constructor of the .NET type and can have various reasons so the exception details should be able to help you along.

The second one can be more difficult because it can be assemblies that cannot be found. But in that case you might have information as to what file is being looked for and should lead you to the place where Ax expects them to be. (And for that reason it is certainly a good idea to start by putting your assembly in the Global Assembly case.

To get more information out of the exception, you can do the following:

catch (Exception::CLRError)
{
     ex = ClrInterop::getLastException();
     if (ex != null)
     {
        ex = ex.get_InnerException();
        while (ex != null)
        {
            error(ex.ToString());
            ex = ex.get_InnerException();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top