The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.


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


    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=,
    Culture=neutral, PublicKeyToken=b77a5c561934e089";

    # loading assembly by name

    # after loading assembly, System.Windows.Forms.* classes can be used
        "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


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.


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(
        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(
        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(
        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(
        "InvokeMethod, NonPublic"

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=,
        Culture=neutral, PublicKeyToken=b77a5c561934e089";
    # $asm can be ignored
    my $asm = Win32::CLR->load($name);
    my $button = Win32::CLR->create_instance("System.Windows.Forms.Button");

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";

Returns type name in CLR.


Returns full qualified type name in CLR.


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])

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


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


Converts $obj to perl primitive string.


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< Generic<Type1, Type2>, Type3 >"   # recursive
    "Generic< Generic`2[Type1, Type2], Type3 >" # mixing

    my $type = <<"TYPE"; # assembly qualified form
            System.String, mscorlib, Version=,
            Culture=neutral, PublicKeyToken=b77a5c561934e089
    >, mscorlib, Version=, Culture=neutral,

    # creating generic delegate
    my $deleg = Win32::CLR->create_delegate(
        sub { print $_[0] }


Like creating generic type, generic method can be used.

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


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)


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


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()


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)


More tests and documents are required.


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.