The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Win32::CLR - Use .NET Framework facilities in Perl

SYNOPSIS

    use Win32::CLR;
    use utf8;

    # binmode STDOUT, ":encoding(Shift_JIS)"; # japanese character set

    # creating instance
    my $dt1 = Win32::CLR->create_instance("System.DateTime", 2007, 8, 9, 10, 11, 12);

    # getting property
    print $dt1->get_property("Year"), "\n"; # 2007

    # calling method
    my $dt2 = $dt1->call_method("AddYears", 3);
    print $dt2->get_property("Year"), "\n"; # 2010

    my $asm = "System.Windows.Forms, Version=2.0.0.0,
    Culture=neutral, PublicKeyToken=b77a5c561934e089";

    # loading assembly by name
    Win32::CLR->load($asm);

    # after loading assembly, System.Windows.Forms.* classes can be used
    Win32::CLR->call_method(
        "System.Windows.Forms.MessageBox", # type
        "Show",    # method
        "Message", # parameter
        "Title"    # parameter
    );

    # creating generic instance
    my $generic = "System.Collections.Generic.Dictionary<System.String, System.Int32>";
    my $dict = Win32::CLR->create_instance($generic);
    $dict->set_property("Item", "ABC", 4321); # dict["ABC"] = 4321;
    print $dict->get_property("Item", "ABC"), "\n"; # 4321

DESCRIPTION

Win32::CLR provides utility methods to using Microsoft .NET Framework, also known as Common Language Runtime. It is available for creating object, calling method, accessing field or property, converting perl subroutine to delegate, loading assembly.

CLASS METHODS

Win32::CLR->create_instance("TYPE", @PARAMS)

Creates .NET Framework object of TYPE. The constructor that matches @PARAMS is used.

    my $dt = Win32::CLR->create_instance(
        "System.DateTime",
        2007, 8, 9, 10, 11, 12
    );
Win32::CLR->call_method("TYPE", "NAME", @PARAMS)

Calls the static method declared in TYPE. @PARAMS must be primitive value or Win32::CLR instance. If @PARAMS contain array reference, it is converted to System.Array.

    # in .net
    # static void MyType::Foo(String^ param);

    # in perl
    Win32::CLR->call_method("MyType", "Foo", "foo");

    # static void MyType::Foo2(String^ param1, Int32 param2);
    Win32::CLR->call_method("MyType", "Foo2", "foo", 4321);

    # static void MyType::Foo3(array<Object^>^ params);
    Win32::CLR->call_method("MyType", "Foo3", ["foo", "bar", ...]);
Win32::CLR->get_field("TYPE", "NAME", [$INDEX])

Returns the static field declared in TYPE. If field has index, optional parameter $INDEX can be used.

    # in .net
    # field = MyType::Foo;

    Win32::CLR->get_field("MyType", "Foo");

    # MyType::Foo[0]
    Win32::CLR->get_field("MyType", "Foo", 0);
Win32::CLR->set_field("TYPE", "NAME", [$INDEX, ] $PARAM)

Sets $PARAM in the static field declared in TYPE. $PARAM must be primitive value or Win32::CLR instance. If field has index, optional parameter $INDEX can be used.

Win32::CLR->get_property("TYPE", "NAME", [$INDEX])

Same as get_field, returns the static property declared in TYPE. Optionally, $INDEX can be used.

    # in .net
    # dict["ABC"]
    $dict->get_property("Item", "ABC");
Win32::CLR->set_property("TYPE", "NAME", [$INDEX, ] $PARAM)

Same as set_field, sets $PARAM in the static property declared in TYPE. Optionally, $INDEX can be used.

    # in .net
    # dict["ABC"] = 4321;
    $dict->set_property("Item", "ABC", 4321);
Win32::CLR->get_value("TYPE", "NAME", [$INDEX])

If property exists in TYPE, calls get_property, or otherwise calls get_field.

Win32::CLR->set_value("TYPE", "NAME", [$INDEX, ] $PARAM)

Same as get_value, sets $PARAM in static property or field.

Win32::CLR->add_event("TYPE", "NAME", $DELEG)

Sets event handler in the static event declared in TYPE. $DELEG must be System.Delegate or subroutine reference.

    my $deleg = Win32::CLR->create_delegate(
        "System.EventHandler",
        sub {print "do something"},
    );
    $button->add_event("Click", $deleg);
    $button->remove_event("Click", $deleg);

    # directly, but addition only!
    $button->add_event("Click", sub {print "do something"});
Win32::CLR->remove_event("TYPE", "NAME", $DELEG)

Removes event handler $DELEG from TYPE. $DELEG must be System.Delegate.

Win32::CLR->create_delegate("TYPE", $CODE)

Creates System.Delegate from perl subroutine. $CODE can contain "sub_name" or \&sub_ref. TYPE is delegate type (ex. System.EventHandler).

    my $deleg = Win32::CLR->create_delegate(
        "System.EventHandler",
        sub {
            my ($obj, $event_args) = @_;
            # do something ...
        }
    );
Win32::CLR->create_array("TYPE", @PARAMS)

Creates TYPE of System.Array. @PARAMS is setted.

    my $array = Win32::CLR->create_array("System.String", "A", "B", "C");
    $array->call_method("GetValue", 0); # A
    $array->call_method("GetValue", 1); # B
    $array->call_method("GetValue", 2); # C
Win32::CLR->create_enum("TYPE", "VALUE1, VALUE2, ...")

Creates System.Enum value. TYPE is enum type, VALUES contain list of named constants delimited by commas.

    my $binding_flags = Win32::CLR->create_enum(
        "System.Reflection.BindingFlags",
        "InvokeMethod, NonPublic"
    );
Win32::CLR->load("NAME")

Loads assembly by AssemblyQualifiedName. If assembly loaded once, it comes to be able to use the type in assembly. Loaded assembly is cached in memory, so reloading assembly is not required. NAME must be long form of the assembly name. It returns loaded System.Reflection.Assembly object.

    my $name =
        "System.Windows.Forms, Version=2.0.0.0,
        Culture=neutral, PublicKeyToken=b77a5c561934e089";
    # $asm can be ignored
    my $asm = Win32::CLR->load($name);
    my $button = Win32::CLR->create_instance("System.Windows.Forms.Button");
Win32::CLR->load_from("PATH")

Loads assembly from file. PATH is path to assembly file. It returns loaded System.Reflection.Assembly object.

Win32::CLR->has_member("TYPE", "NAME", [$MEMBER_TYPE])

Checks TYPE has member NAME. $MEMBER_TYPE is System.Reflection.MemberTypes constants delimited by commas. Default is Method, Field, Property, Event.

    if ( Win32::CLR->has_member("System.String", "Length", "Field, Property") ) {
        print "System.String has Length\n";
    }
Win32::CLR->get_type_name($OBJ)

Returns type name in CLR.

Win32::CLR->get_qualified_type($OBJ)

Returns full qualified type name in CLR.

INSTANCE METHODS

Win32::CLR instance has similar methods to class methods.

    $obj->call_method("NAME", @PARAMS)
    $obj->get_field("NAME", [$INDEX])
    $obj->set_field("NAME", [$INDEX, ] $PARAM)
    $obj->get_property("NAME", [$INDEX])
    $obj->set_property("NAME", [$INDEX, ] $PARAM)
    $obj->get_value("NAME", [$INDEX])
    $obj->set_value("NAME", [$INDEX, ] $PARAM)
    $obj->add_event("NAME", $DELEG)
    $obj->remove_event("NAME", $DELEG)
    $obj->has_member("NAME", [$MEMBER_TYPE])
    $obj->get_type_name()
    $obj->get_qualified_type()
$obj->get_addr()

Returns object address. Can be used for Inside-out class.

$obj->derived_from("TYPE")

Like UNIVERSAL::isa, returns $obj is derived from TYPE.

$obj->to_string()

Converts $obj to perl primitive string.

CREATING GENERIC INSTANCE

If you want to create generic instance, use optional parameter type enclosed by "<>".

    my $name = "System.Collections.Generic.Dictionary<System.String, System.Int32>";
    my $dict = Win32::CLR->create_instance($name);

Also System.Type.GetType form can be used.

    my $name = "System.Collections.Generic.Dictionary`2[System.String, System.Int32]";
    my $dict = Win32::CLR->create_instance($name);

GENERIC TYPE EXAMPLE

    "Generic< Generic<Type1, Type2>, Type3 >"   # recursive
    "Generic< Generic`2[Type1, Type2], Type3 >" # mixing

    my $type = <<"TYPE"; # assembly qualified form
    System.Collections.Generic.Dictionary<
        [
            System.String, mscorlib, Version=2.0.0.0,
            Culture=neutral, PublicKeyToken=b77a5c561934e089
        ],
        System.Int32
    >, mscorlib, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089
    TYPE

    # creating generic delegate
    my $deleg = Win32::CLR->create_delegate(
        "System.Action<System.String>",
        sub { print $_[0] }
    );

CALLING GENERIC METHOD

Like creating generic type, generic method can be used.

    $obj->call_method("method<System.String>", @params);

TYPE CONVERSION

Win32::CLR automatically converts primitive value between .net and perl.

    .net -> perl
        Boolean                      -> perl bool
        SByte, Int16, Int32, Int64   -> perl int
        Byte, UInt16, UInt32, UInt64 -> perl unsigned int
        Single, Double               -> perl double
        Char, String, Decimal        -> perl string(utf8 flag on)
        null(nullptr)                -> perl undef
        other                        -> perl Win32::CLR instance

    perl -> .net
        Win32::CLR instance -> Object
        perl int            -> Int32  -> cast target
        perl unsigned int   -> UInt32 -> cast target
        perl string         -> Char, String, Decimal
        perl double         -> Double -> cast target
        perl undef          -> null(nullptr)

EXCEPTION HANDLING

When error occurred, System.Exception object is setted in $@. If you want to catch the exception, use eval-block statement. Note that exception message is utf8 encoded.

    use Win32::CLR;

    binmode STDERR, ":encoding(sjis)"; # if japanese windows

    eval {
        # Invalid arguments!
        my $dt1 = Win32::CLR->create_instance("System.DateTime", 2007, 8, 9, 10);
    };

    print STDERR $@->get_property("Message"), "\n";
    print STDERR $@->get_type_name(), "\n"; # System.MissingMethodException

OVERLOAD

Following operators are overloaded.

    "" bool == != + - * / % > >= < <= ++ --

    my $dt1 = Win32::CLR->create_instance("System.DateTime", 2007, 8, 9, 10, 11, 12);
    my $dt2 = Win32::CLR->create_instance("System.DateTime", 2008, 8, 9, 10, 11, 12);
    print "$dt1";
    $dt1 == $dt1;
    $dt1 != $dt2;
    $dt > $dt2;
    $dt < $dt2;

If you want compare instance equality, use get_addr method.

    $dt->get_addr() == $dt->get_addr()

AUTOLOAD

When calling method named /^(get|set)_\w+/, it is converted to get_value or set_value. If not /^(get|set)_/, converted to call_method. Underscore is ignored and name is ignorecase.

    $obj->get_year()    -> $obj->get_value("Year")
    $obj->get_y_e_Ar()  -> $obj->get_value("Year")
    $obj->set_year(1)   -> $obj->set_value("Year", 1)
    $obj->add_years(2)  -> $obj->call_method("AddYears", 2)
    $obj->AddYears(2)   -> $obj->call_method("AddYears", 2)
    $obj->aDd__yEaRs(2) -> $obj->call_method("AddYears", 2)

BUGS AND WARNINGS

More tests and documents are required.

COPYRIGHT & LICENSE

Copyright (C) 2008 Toshiyuki Yamato, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.