[AX]AX2012 使用.NET Interop from X++

已经讲过可以通过Proxy class从C#使用X++的类,反过来从X++使用CLR的类型当然也是可以的,这在以前版本的AXAPTA 4.0、AX 2009就开始支持了,这里把要注意的问题做一简单的归纳。

X++使用CLR类的静态方法需要使用“::”而不是C#中的“.”,这和X++调用X++类的静态方法是一样的:

System.String netString=System.Convert::ToString("xxx");

从X++的数据类型到CLR的元类型或者从CLR的元类型到X++的数据类型,会自动进行隐式的marshal转化,下表是两者类型的对应关系:

X++ type

CLR type

boolean

System.Boolean

date

System.DateTime

int

System.Int32

int64

System.Int64

str

System.String

guid

System.Guid

Real

System.Single or System.Double

X++的int类型是不会转化成CLR的Int64的,CLR的Int32也是不会转成X++的int64,反过来从64位到32位整数也是一样。X++的real类型同时对应CLR的Single和Double两种,X++的real类型可以直接赋值给CLR的Single类型,在AX2009中也可以直接赋值给Double类型,但是以后的版本中可能会改变,AX2012提供函数Global::real2double(xppReal)将X++的real类型转成CLR的Double类型。

 

X++ timeofday没有对应的CLR元类型,它内部其实使用整数类型来表示数据的。同样X++的utcdatetime也没有对应的CLR类型,AX2012提供Global::utcDateTime2SystemDateTime()和Global::CLRSystemDateTime2UtcDateTime()两个方法来相互转换。AX2012还提供DateTimeUtil::tostr()从utcdatetime转换成字符串,和DateTimeUtil::parse()从字符串转换成utcdatetime,需要注意的是CLR的System.Convert.ToString()或者System.DateTime.ToString()得到字符串是不能直接被DateTimeUtil::parse()解析的,时间字符串的格式要使用ISO的标准24小时格式“yyyy-mm-ddThh:mm:ss”,比如当前的时间应该为“2012-09-11T10:01:05”,实际上这样的格式的时间是可以直接赋值给X++的utcdatetime而不需要DateTimeUtil::parse()做额外的转换,比如:

utcdatetime  xppIsoDttm = 2012-09-11T10:01:05;

 

在X++中CLR的值类型实际上是以引用保存的,这意味着除了“=”赋值操作符外,不能使用“==”或者“>”等逻辑运算符来比较两个CLR值类型,也不能使用位操作符“&”及“|”。比如两个System.Int32的变量a和b,这样判断“a>=b”是不正确的,相应的要使用System.Int32.CompareTo()方法。

 

X++可以使用CLR的数组,同样使用“[]”定义一个数组,内部创建System.Array的CLR类型,但是不能使用“[]”访问CLR数组的元素,而要使用GetValue()、SetValue()等方法:

static void JobTestNetArray()
{
    System.Int32[] iNetNumbers; // .NET Framework array
    int iXppNumbers[2]; // X++ array
    int iXppNum,iXppArrayLength,i;
    ;
    info("Next, .NET Framework array by special X++ syntax.");
    //-------- .NET Framework array by special X++ syntax ------
    iNetNumbers = new System.Int32[2](); // Note the () at end.
    iNetNumbers.SetValue(100, 0); // Resembles iNetNumbers[0]=100.
    iNetNumbers.SetValue(101, 1);
    
    iXppArrayLength = iNetNumbers.get_Length();
    for (i = 0; i < iXppArrayLength; i++)
    {
        iXppNum = iNetNumbers.GetValue(i); // Resembles iNetNumbers[i].
        info(int2str(iXppNum));
    }

    info("Next, X++ Native array.");
    //----------- X++ Native array ------
    iXppNumbers[1] = 2201;
    iXppNumbers[2] = 2202;

    for (i = 1; i <= dimOf(iXppNumbers); i++)
    {
        info(int2str(iXppNumbers[i]));
    }

}

X++数组和CLR数组有很多的不同:X++数组下标从1开始,CLR数组下标从0开始;X++数组定义了就可以使用,CLR数组必须使用new关键字构建;X++数组索引必须是X++的整数类型,CLR数组索引则可以是CLR的整数类型,也可以是X++的整数类型;X++数组是一维的,CLR数组可以是多维的;X++的数组元素只能是str或者int类型,CLR数组元素则可以是任何CLR类型,但不能是X++的类型;X++数组的长度在定义时就确定了,CLR数组的类型在new时确定,new时必须指定数组长度,否则报语法错不能编译。

 

X++的类型可以作为函数参数传递给CLR的函数,比如CLR函数参数原型为System.String,可以将X++的str传递给这个函数,但是反过来是不可以的,不能把CLR的类型传递给需要X++类型参数的函数。对于C# ref、out标识的引用参数,X++提供byref关键字表示引用参数传递。需要注意的是那种值传递的对象类型,在传递给被调用函数前会将对象类型的指针复制一份传递,看这样一个例子:

 class MyClass //X++
    {

        public static void CallerMethodByValueObject() // X++
        {
            TestProxyClass.MyEntity meTest1;
            TestProxyClass.MyEntity meTest2;
            int i;

            ;
            meTest1 = new TestProxyClass.MyEntity(1);
            meTest2 = new TestProxyClass.MyEntity(33);
            MyClass::CalledMethodByValueObject(meTest1, meTest2);
    
            i=meTest1.GetCounter();
            if (i == 1)
            {
                info("Good, == 1");
            }
            i=meTest2.GetCounter();
            if (i == 36)
            {
                info("Good, == 36");
            }
    
            TestProxyClass.MyEntity::CalledMethodByValueObject(meTest1,meTest2); //X++
            i=meTest1.GetCounter();
            if (i == 1)
            {
                info("Good, == 1");
            }
            i=meTest2.GetCounter();
            if (i == 39)
            {
                info("Good, == 39");
            }
    
        }

        static public void CalledMethodByValueObject // X++
                (
                 TestProxyClass.MyEntity meTest1  // by value
                , TestProxyClass.MyEntity meTest2 // by value
                )
        {
            ;
            meTest1 = new TestProxyClass.MyEntity(555); // Caller can not detect.
            meTest2.AddToCounter(3); // Caller can detect.
        }


    }


public class MyEntity //C#
    {
        private int _counter;

        public MyEntity(int counter)
        {
            _counter = counter;
        }

        public int GetCounter() { return _counter; }
        public void AddToCounter(int c)
        {
            _counter += c;
        }

        static public void CalledMethodByValueObject 
        (
        MyEntity meTest1  // by value
        , MyEntity meTest2 // by value
        )
        {
            ;
            meTest1 = new MyEntity(555); // Caller can not detect.
            meTest2.AddToCounter(3); // Caller can detect.
        }
    }

MyEntity是一个C#的类,在X++的MyClass::CallerMethodByValueObject()方法中以值的方式分别传递meTest1、meTest2给X++的MyClass::CalledMethodByValueObject和C#的MyEntity.CalledMethodByValueObject两个静态方法,主程序中显示meTest1的结果都是1,这说明被调用函数中修改的meTest1只是传入变量指针的拷贝,不会影响到主程序中的变量,不管是被调用函数是X++还是C#都是一样,其实在C#中调用MyEntity.CalledMethodByValueObject也是得到相同的结果,这点上X++和C#是一致的。

 

X++使用的CLR程序集要么是在AOT的Reference节点下手工引用,或者是在GAC,在客户端AX会提示把引用的程序集放到client的bin目录下,默认C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin,服务端是在C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin,注意和我们把c# project添加到AOT后的部署路径是不同的。虽然可以使用server关键字让类的静态方法在AOS运行,但是试图从这些server方法返回一个CLR对象到client是不可以的,收到的结果是CLRojbect未初始化异常。

 

X++可以使用try...catch捕捉CLR异常,提供了专门的异常类型Exception::CLRError,在捕捉到CLR异常后可以使用ClrInterop::getLastException()获得一个CLR的异常对象System.Exception,调用它的get_InnerException()可以进一步获得此异常的详细信息。

 

X++的系统类DictMethod提供方法clrParameterType、clrReturnType、clrVarType来获取X++方法的参数CLR类型、函数返回值CLR类型和函数本地变量CLR类型。

 

更多有关.NET Interop from X++的内容见http://msdn.microsoft.com/en-us/library/cc598160

 

 

posted @ 2012-09-13 12:30  断水流  阅读(1989)  评论(0编辑  收藏  举报