.Net Runtime Library for Delphi
Close
Using ClrObject Interface
This is an interface that is capable of containing almost any .net object as well as data type value, along with type information for that value. It provides the ability to store the value, and type information of an instance of any type. It is designed to ease the access to fields, properties, methods and events of the stored value.
System_CAPS_note Note

All the Runtime Library interfaces have a method called AsClrObject which returns the ClrObject interface.

The CoClass for the ClrObject interface (CoClrObject) has overload static methods called CreateInstance and Wrap which allows you to create instance of the ClrObject type and cast a variant to ClrObject type respectively.

Example 1

Delphi
program ClrObjectDemo; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Core; var Console: _Console; ClrObject, InvObject: _ClrObject; AnyVar: Variant; begin //Create ClrObject Instance of System.Object type ClrObject := CoClrObject.CreateInstance; //Create Static ClrObject Type of System.Convert ClrObject := ClrHostManager.DefaultInterface.CreateStaticObject(TClrAssembly.GetType('System.Convert')); //Create ClrObject Instance of System.String Data Value 'ClrObject holds String Value' ClrObject := CoClrObject.CreateInstance('ClrObject holds String Value'); //Create ClrObject Instance of System.Boolean Data Value True ClrObject := CoClrObject.CreateInstance(True); //Create ClrObject Instance of System.Int32 Data Value 12345 ClrObject := CoClrObject.CreateInstance(12345); //Create ClrObject Instance of System.Char Data Value 'c' ClrObject := CoClrObject.CreateInstance(TClrCharHelper.ToClrChar('c')); //Create ClrObject Instance of System.Decimal Data Value 4565.98676 ClrObject := CoClrObject.CreateInstance(TClrConvert.ToDecimal(4565.98676)); //Create ClrObject Instance of System.Double Data Value 4565.98676 ClrObject := CoClrObject.CreateInstance(4565.98676); //Assuming this InvObject object contains a method called GetUD and the return //type of this method is an instance of another class. // ClrObject := InvObject.InvokeMethod_('GetUD'); //Or Use the InvokeMethod which will return an IUnKnown variant ClrObject := CoClrObject.CreateInstance(InvObject.InvokeMethod('GetUD')); AnyVar := Integer(@ClrObject);// Any value type; ClrObject := CoClrObject.CreateInstance(AnyVar); //Create ClrObject Instance of System.Guid(String guid) type ClrObject := CoClrObject.CreateInstance(TClrAssembly.GetType('System.Guid'), TClrArrayHelper.ToObjectArray(['{7C0AE73C-599F-4EFD-A287-ECA434B1BC40}'])); //Create ClrObject Instance of System.Collection.Generic.List<Int32> Type ClrObject := CoClrObject.CreateInstance(TClrAssembly.GetType('System.Collection.Generic.List<>'), TClrArrayHelper.ToTypeArray(['System.Int32']), nil); //Cast the Variant Value to ClrObject ClrObject := CoClrObject.Wrap(AnyVar); end.



























































ClrObject allows you to create and hold object of .net generic type. It has generic conversions for other types (Asxxx ). Example: If you call this method AsByte , the stored value will return a byte which is a conversion of the stored value, an error may be triggered if the value does not support such conversion.

Getting the stored data can be done by calling the Unwrap method.

Example 2

Delphi
program UsingUnwrapMethod; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Core; var Console: _Console; ClrObject: _ClrObject; IntVal, UnwrapVal: Integer; begin Console := CoConsole.CreateInstance; IntVal := 12345; Console.WriteLine_15('Original Data: {0}', IntVal); //Create ClrObject Instance of System.Int32 Data Value 12345 ClrObject := CoClrObject.CreateInstance(IntVal); UnwrapVal := ClrObject.Unwrap; Console.WriteLine_15('Unwrap Data: {0}', UnwrapVal); Console.WriteLine; Console.WriteLine_15('Same Data: {0}', IntVal = UnwrapVal); end. //Output //Original Data: 12345 //Unwrap Data: 12345 // //Same Data: True






























 

Access Object Fields

Examples

The following example uses the GetField overload methods to get the field-related information from the FieldInfo interface, and then displays field attributes and get and set their values.

Delphi
//Assuming the c# code below is compiled into a dll and loaded by the Runtime Library (* public class FieldInfoClass { public int myField1; protected string myField2; public FieldInfoClass() { myField1 = 0; myField2 = null; } } *) program AccessFieldInfo1; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core, CNClrLib.Enums, CNClrLib.Core.Intf; var Console: _Console; FieldInfoObj: _ClrObject; myField1: _FieldInfo; myField2: _FieldInfo; procedure DisplayFieldInfo(ATarget: Variant; AField: _FieldInfo; AChangeFieldValue: Variant); begin with AField do begin Console.WriteLine_15('Name : {0}', Name); Console.WriteLine_15('Declaring Type : {0}', DeclaringType); Console.WriteLine_15('IsPublic : {0}', IsPublic); Console.WriteLine_15('MemberType : {0}', MemberType); Console.WriteLine_15('FieldType : {0}', FieldType); Console.WriteLine_15('IsFamily : {0}', IsFamily); Console.WriteLine_15('GetValue : {0}', GetValue(FieldInfoObj)); Console.WriteLine_15('Set Field Value to ''{0}''', AChangeFieldValue); SetValue_2(ATarget, AChangeFieldValue); Console.WriteLine_15(GetValue : {0}', GetValue(FieldInfoObj)); Console.WriteLine; end; end; begin Console := CoConsole.CreateInstance; FieldInfoObj := CoClrObject.CreateInstance(TClrAssembly.GetType('FieldInfoClass'), nil); myField1 := FieldInfoObj.GetField_1('myField1', BindingFlags_Instance or BindingFlags_Public); myField2 := FieldInfoObj.GetField_1('myField2', BindingFlags_Instance or BindingFlags_NonPublic); DisplayFieldInfo(FieldInfoObj, myField1, 2000); DisplayFieldInfo(FieldInfoObj, myField2, 'My New String'); Console.ReadKey; end.


































































The following example uses the GetValue method to retrieve the value of a static field.

Delphi
//Assuming the c# code below is compiled into a dll and loaded by the Runtime Library (* public class FieldInfoClass { public static String val = "test"; } *) program AccessFieldInfo2; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core; var Console: _Console; FieldInfoObj: _ClrObject; val: string; begin Console := CoConsole.CreateInstance; FieldInfoObj := ClrHostManager.DefaultInterface.CreateStaticObject(TClrAssembly.GetType('FieldInfoClass')); val := FieldInfoObj.GetFieldValue('val'); Console.WriteLine_14(val); FieldInfoObj.SetFieldValue('val', 'hi'); Console.WriteLine_14(val); end. // The example displays the following output: // test // hi






































 

Access Object Properties

There are methods on this interface for providing access to the stored object's property metadata and also for returning the value of a property supported by the object.

Examples

The following example uses the GetProperty overload methods to get the property-related information from the PropertyInfo interface, and then displays property attributes and get and set their values.

Delphi
//Assuming the c# code below is compiled into a dll and loaded by the Runtime Library (* public class PropertyInfoClass { public int myProperty { get; set; } public PropertyInfoClass() { myProperty = 0; } } *) program AccessObjectProperty1; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core, CNClrLib.Enums, CNClrLib.Core.Intf; var Console: _Console; PropertyInfoObj: _ClrObject; myProperty: _PropertyInfo; begin Console := CoConsole.CreateInstance; PropertyInfoObj := CoClrObject.CreateInstance(TClrAssembly.GetType('PropertyInfoClass'), nil); myProperty := PropertyInfoObj.GetProperty('myProperty'); Console.WriteLine_15('Name : {0}', myProperty.Name); Console.WriteLine_15('Declaring Type : {0}', myProperty.DeclaringType); Console.WriteLine_15('MemberType : {0}', myProperty.MemberType); Console.WriteLine_15('PropertyType : {0}', myProperty.PropertyType); Console.WriteLine_15('GetValue : {0}', myProperty.GetValue(PropertyInfoObj)); Console.WriteLine_14('Set Property Value to ''123455'''); myProperty.SetValue_2(PropertyInfoObj, 123455); Console.WriteLine_15('GetValue : {0}', myProperty.GetValue(PropertyInfoObj)); Console.ReadKey; end.
















































The following example displays the index parameters of the specified property. Assuming the c# code below is compiled into a dll and loaded by the Runtime Library:

C#
using System; using System.Reflection; // A class that contains some properties. public class MyProperty { // Define a simple string property. private string caption = "A Default caption"; public string Caption { get{return caption;} set {if(caption!=value) {caption = value;} } } // A very limited indexer that gets or sets one of four // strings. private string[] strings = {"abc", "def", "ghi", "jkl"}; public string this[int Index] { get { return strings[Index]; } set { strings[Index] = value; } } }































Delphi
program AccessObjectProperty2; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core, CNClrLib.Enums, CNClrLib.Core.Intf; var Console: _Console; PropertyInfoObj: _ClrObject; piProperty: _PropertyInfo; Parms: _ParameterInfoArray; I: Integer; begin Console := CoConsole.CreateInstance; PropertyInfoObj := CoClrObject.CreateInstance(TClrAssembly.GetType('MyProperty'), nil); piProperty := PropertyInfoObj.GetProperty('Caption'); // Get the public GetIndexParameters method. Parms := piProperty.GetIndexParameters; Console.WriteLine_14(PropertyInfoObj.GetType.FullName + '.' + piProperty.Name + ' has ' + IntToStr(Parms.GetLength(0)) + ' parameters.'); // Display a property that has parameters. The default name of an indexer is "Item". piProperty := PropertyInfoObj.GetProperty('Item'); Parms := piProperty.GetIndexParameters; Console.WriteLine_14(PropertyInfoObj.GetType.FullName + '.' + piProperty.Name + ' has ' + IntToStr(Parms.GetLength(0)) + ' parameters.'); for I := 0 to Parms.Length - 1 do Console.WriteLine_14(' Parameter: ' + Parms[I].Name); Console.WriteLine_14('Get Value at Index 1'); Console.WriteLine_15('Value at Index 1: {0}', PropertyInfoObj.GetPropertyValue_1('Item', 1)); Console.WriteLine_14('Set Value at Index 1 to ''efg'''); PropertyInfoObj.SetPropertyValue_7('Item', 'efg', 1); Console.WriteLine_15('Value at Index 1: {0}', PropertyInfoObj.GetPropertyValue_1('Item', 1)); Console.ReadKey; end. (* This example produces the following output: MyProperty.Caption has 0 parameters. MyProperty.Item has 1 parameters. Parameter: Index Get Value at Index 1 Value at Index 1: def Set Value at Index 1 to 'efg' Value at Index 1: efg *)

























































 

Access and Invoke Object Methods

There are methods on this interface for providing access to the stored object's method metadata and also for invoking the method supported by the object. The following code example demonstrates dynamic method lookup.

Examples

Assuming the c# code below is compiled into a dll and loaded by the Runtime Library:

C#
using System; using System.Reflection; public class MagicClass { private int magicBaseValue; public MagicClass() { magicBaseValue = 9; } public int ItsMagic(int preMagic) { return preMagic * magicBaseValue; } }








Delphi
program AccessObjectMethod; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core, CNClrLib.Enums, CNClrLib.Core.Intf; var Console: _Console; MethodObj: _ClrObject; magicValue: Variant; begin Console := CoConsole.CreateInstance; MethodObj := CoClrObject.CreateInstance(TClrAssembly.GetType('MagicClass'), nil); // Invoke ItsMagic method with a parameter value of 100 magicValue := MethodObj.InvokeMethod_1('', 'System.Int32', TClrArrayHelper.ToObjectArray([100])); Console.WriteLine_14('ClrObject.InvokeMethod() Example'); Console.WriteLine; Console.WriteLine_15('MagicClass.ItsMagic() returned: {0}', magicValue); Console.ReadKey; end. // The example program gives the following output: // // MethodInfo.Invoke() Example // // MagicClass.ItsMagic() returned: 900


























 

Access and Invoke Object's Generic Methods

Examples

Assuming you have the following C# Codes which has generic method ShowType<T> and is compiled into a dll and loaded by the Runtime Library:

C#
public class Test { public void ShowType<T>() { Console.WriteLine(typeof(T)); } }



The below example in Delphi shows how to invoke this generic method:

Delphi
program InvokeGenericMethod; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core; var MethodObj: _ClrObject; begin MethodObj := CoClrObject.CreateInstance(TClrAssembly.GetType('Test'), nil); MethodObj.InvokeGenericMethod('ShowType', TClrArrayHelper.ToTypeArray(['System.Int32'])); MethodObj.InvokeGenericMethod('ShowType', TClrArrayHelper.ToTypeArray(['System.Double'])); MethodObj.InvokeGenericMethod('ShowType', TClrArrayHelper.ToTypeArray(['System.Object'])); end. // The example program gives the following output: // // System.Int32 // System.Double // System.Object



















Register/UnRegister Events

To register or unregister events use the RegisterEventCallBack or UnRegisterEventCallBack methods respectively if the eventhandler method signature is the same signature of the Clr function pointer TClrEventhandler or TClrEventHandlerR. If the Signatures are different then use RegisterEventCallBackDirect or UnRegisterEventCallBackDirect.

 

1. Using RegisterEventCallBack/UnRegisterEventCallBack Methods

Assuming you have a C# class as shown below which is compiled to Counter.dll.

C#
namespace CounterEventhandler { public class Counter { private int threshold; private int total; public Counter(int passedThreshold) { threshold = passedThreshold; } public void Add(int x) { total += x; if (total >= threshold) { OnThresholdReached(EventArgs.Empty); } } protected virtual void OnThresholdReached(EventArgs e) { EventHandler handler = ThresholdReached; if (handler != null) { handler(this, e); } } public event EventHandler ThresholdReached; } }



























The following Delphi Code demonstrate how to register the event from the C# Class using the standard Clr function pointers and also how to unregister the events

Delphi
program Register_UnRegisterEvents; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Core; var Console: _Console; clrCounter: _ClrObject; ARandom: _Random; OnThresholdReached : TClrEventHandler; procedure c_ThresholdReached(ASender: _ClrObject; AEventArgs: _ClrEventArgs); stdcall; begin Console.WriteLine_14('The threshold was reached.'); Abort; end; begin Console := CoConsole.CreateInstance; TClrAssembly.LoadFrom('Counter.dll'); ARandom := CoRandom.CreateInstance; clrCounter := CoClrObject.CreateInstance(TClrAssembly.GetType('CounterEventhandler.Counter'), TClrArrayHelper.ToObjectArray([ARandom.Next_2(10)])); //Register Event OnThresholdReached := c_ThresholdReached; clrCounter.RegisterEventCallBack('ThresholdReached', TClrConvert.ToManagedPointer(@OnThresholdReached)); Console.WriteLine_14('press ''a'' key to increase total'); while TClrCharHelper.ToChar(Console.ReadKey_1(True).KeyChar) = 'a' do begin Console.WriteLine_14('adding one'); clrCounter.InvokeMethod_1('Add', 'System.Int32', TClrArrayHelper.ToObjectArray([1])); end; //UnRegister Event clrCounter.UnRegisterEventCallBack('ThresholdReached', TClrConvert.ToManagedPointer(@OnThresholdReached)); end.












































 

2. Using RegisterEventCallBackDirect/UnRegisterEventCallBackDirect Methods

It is highly recommended to use RegisterEventCallBack or UnRegisterEventCallBack methods to register or unregister events, however situations where the .Net standard event delegate is not for the event delegate signature, these methods can be used to register/unregister the event, see the example codes below:

Assuming you have a C# class as shown below which is compiled to Counter.dll.

C#
namespace CounterEventhandler { public class Counter { private int threshold; private int total; public Counter(int passedThreshold) { threshold = passedThreshold; } public void Add(int x) { total += x; if (total >= threshold) { OnThresholdReached(total, threshold); } } protected virtual void OnThresholdReached(int total, int threshold) { EventHandler handler = ThresholdReached; if (handler != null) { handler(this, e); } } public event Action<int,int> ThresholdReached; } }































The following Delphi Code demonstrate how to register the event from the C# Class without using Clr standard function pointers and also how to unregister the events

Delphi
program Register_UnRegisterEvents; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Core; type //Since i am not using the standard Clr Function Pointer (TClrEvenHandler or TClrEventhandlerR), //I am creating my own which will mimick the signature of th C# event i want to register. TThresholdReached = procedure(total, threshold: Integer); stdcall; var Console: _Console; clrCounter: _ClrObject; ARandom: _Random; OnThresholdReached : TThresholdReached; procedure c_ThresholdReached(total, threshold: Integer); stdcall; begin Console.WriteLine_14('The threshold was reached.'); Abort; end; begin Console := CoConsole.CreateInstance; TClrAssembly.LoadFrom('Counter.dll'); ARandom := CoRandom.CreateInstance; clrCounter := CoClrObject.CreateInstance(TClrAssembly.GetType('CounterEventhandler.Counter'), TClrArrayHelper.ToObjectArray([ARandom.Next_2(10)])); //Register Event OnThresholdReached := c_ThresholdReached; clrCounter.RegisterEventCallBackDirect('ThresholdReached', TClrConvert.ToManagedPointer(@OnThresholdReached)); Console.WriteLine_14('press ''a'' key to increase total'); while TClrCharHelper.ToChar(Console.ReadKey_1(True).KeyChar) = 'a' do begin Console.WriteLine_14('adding one'); clrCounter.InvokeMethod_1('Add', 'System.Int32', TClrArrayHelper.ToObjectArray([1])); end; //UnRegister Event clrCounter.UnRegisterEventCallBackDirect('ThresholdReached', TClrConvert.ToManagedPointer(@OnThresholdReached)); end.



















































 

ClrObject As an Array Type

The ClrObject can be used to hold array types and the functions below allows you to access the array information:

  • function GetArrayLength: Integer;
  • function GetArrayElement(Index: Integer): OleVariant;
  • function GetArrayElement_(Index: Integer): _ClrObject;
  • procedure SetArrayElement(Index: Integer; Value: OleVariant);
  • procedure SetArrayElement_1(Index: Integer; Value: _ClrObject);
  • function AsArray: _Array;
  • property IsArray: WordBool;

Example

 

Delphi
program ArrayExample; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core; var Console: _Console; ClrObject: _ClrObject; begin Console := CoConsole.CreateInstance; //Create ClrObject of an Integer array with values {0, 1, 2, 3, 4, 5} ClrObject := CoClrObject.CreateInstance(TClrArrayHelper.ToInt32Array([0, 1, 2, 3, 4, 5])); Console.WriteLine_15('Object is an Array: {0}', ClrObject.IsArray); Console.WriteLine_15('Array Length: {0}', ClrObject.GetArrayLength); Console.WriteLine_15('Array Element at index 3: {0}', ClrObject.GetArrayElement(3)); Console.WriteLine_14('Set Array Element at index 3 to 300'); ClrObject.SetArrayElement(3, 300); Console.WriteLine_15('Array Element at 3: {0}', ClrObject.GetArrayElement(3)); end. //Output //Object is an Array: True //Array Length: 6 //Array Element at index 3: 3 //Set Array Element at index 3 to 300 //Array Element at 3: 300

































Other Examples

Any C# type can be used as an ClrObject. The program shows that as an object, the reference still is an instance of the more-derived StringBuilder type.

Delphi
program StringBuilderExample; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core; var Console: _Console; ClrObject: _ClrObject; begin Console := CoConsole.CreateInstance; ClrObject := CoClrObject.CreateInstance(CoStringBuilder.CreateInstance); // OR ClrObject := CoStringBuilder.CreateInstance.AsClrObject; Console.WriteLine_12(ClrObject.GetType); end. //Output //System.Text.StringBuilder