DOT NET Interface Library v2: Unterschied zwischen den Versionen

Aus expecco Wiki (Version 2.x)
Zur Navigation springen Zur Suche springen
 
(4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 74: Zeile 74:
f.showDialog();
f.showDialog();
</pre></code>
</pre></code>

=== Casting to Specific Numeric Types ===

Smalltalk numeric data types are mapped to .NET data types as follows:

SmallInteger values between:-2147483648 and:2147483647 -> Int32
Integer values < -2147483648 or > 2147483647 -> Int64
Float -> Double
ShortFloat -> Single

If you use other numeric data types, you have to do an explicit cast. This is especially needed to match a .NET function with a specific type argument:

public static UInt32 testUInt32(UInt32 val)
{
return val;
}

you can call this function from expecco/Smalltalk with:

testType testUInt32:(0 castAs:#'System.UInt32')

Following casts are supported:

.NET Type Smalltalk expression JavaScript expression
----------------------------------------------------------------------------------------------------------------
Byte (255 castAs:#'System.Byte') 255.castAs(#'System.Byte')
SByte (-128 castAs:#'System.SByte') -128.castAs(#'System.SByte')
Int16 (-32768 castAs:#'System.Int16') -32768.castAs(#'System.Int16')
UInt16 (65535 castAs:#'System.UInt16') 65635.castAs(#'System.UInt16')
UInt32 (4294967295 castAs:#'System.UInt32') 4294967295.castAs(#'System.UInt32')
Int64 (9223372036854775808 castAs:#'System.Int64') 9223372036854775808.castAs:(#'System.Int64')
UInt64 (18446744073709551615 castAs:#'System.UInt64') 18446744073709551615.castAs:(#'System.UInt64')

=== Initializing .NET Array Objects ===

Only skalar types array "literals" (byte[], short[], float[], double[], long[], ...) are mapped directly to/from the corresponding Smalltalk array types (uint32[] <-> IntegerArray, int32[] <-> SignedIntegerArray, float[] <-> FloatArray, ....).

Arrays with non-literal elements have to be initialize explicitly. E.g. a String array is initialized the following way:

"/ create a String Array with 20 elements.
strArray := (bridge getDotNetType:'System.String[]') new:20.
"/ initialize each element
"/ NOTE! array index in .NET is 0-based!.
strArray at:0 put:'Test0'.
strArray at:1 put:'Test1'.
strArray at:2 put:'Test2'.
strArray at:19 put:'Test19'.


=== Callbacks ===
=== Callbacks ===
Zeile 167: Zeile 214:
|dotNet shell buttonPressed|
|dotNet shell buttonPressed|


dotNet := DotNet singletonInstance.
dotNet := DOTNET::DotNet newWithServer.
dotNet loadFile:'C:\Temp\IWshRuntimeLibrary.dll'.
dotNet loadFile:'C:\Temp\IWshRuntimeLibrary.dll'.
shell := dotNet WshShellClass new.
shell := dotNet WshShellClass new.

Aktuelle Version vom 5. September 2024, 12:04 Uhr

Overview[Bearbeiten]

The .NET Interface Library (".NET Bridge") contains a mechanism to access Microsoft .NET (CLR) objects of a local or remote .NET application (a so-called .NET bridge), and an API for elementary blocks and a library of blocks to interact with these objects.

Programmatic Interface[Bearbeiten]

Access to .NET objects, classes and programs is done via a framework called "dotNET-Bridge". This framework implements transparent forwarding of expecco messages (virtual function calls) from either Smalltalk or JavaScript code to .NET objects as existent in a local or remote .NET virtual machine. Also, return values, callBacks and exception information are passed back from the .NET program to expecco. A proxy-object mechanism which catches all function calls, wraps the arguments and sends a datagram to the other bridge side is used for this to be almost completely transparent to the Smalltalk/JavaScript code inside expecco.

In addition to existing blocks of the .NET Interface Library, programmatic access to dotNET objects is sometimes useful or required. So the following information is useful if you want to write your own elementary .NET-blocks, or if you have to enhance the existing library by adding application-specific interface blocks.

Initializing / Releasing the Bridge[Bearbeiten]

Before any communication can take place between expecco and any dotNet object, the .NET side of the bridge has to be started, and a communication path to be setup. All of the bridges classes are in the DOTNET namespace; the main interface class is DotNet, in this namespace:

    dotNetHandle := DOTNET::DotNet newWithServer.

or (in JavaScript):

    dotNetHandle = DOTNET::DotNet.newWithServer();

This starts the .NET-side of the bridge (the executable named "DotNetBridge.Server.exe"), and waits for a connection request from this program.

When finished, release the bridge with:

    dotNetHandle.close();

which shuts down the connection and terminates the executable.

Loading Assemblies[Bearbeiten]

Using the "loadLibrary"-function, well-known assemblies can be loaded:

    dotNetHandle.loadLibrary("System.Windows.Forms");

or:

    dotNetHandle.loadLibrary("user32.dll");

Arbitrary files which contain assemblies are loaded with:

    dotNetHandle.loadFile(pathToDLL);

or:

    dotNetHandle.loadExtension(pathToDLL);

the former loads an assembly from the machine running the bridge server (DotNetBridge.Server.exe), the latter loads it from the machine running expecco. loadExtension also makes loaded assemblies available to dynamically compiled VB/C# compilation units.

Accessing Globals[Bearbeiten]

Globals, nameSpaces and members of a namespace are accessed using message sends to the dotNet handle, where the message name is the name of the global, namespace or interface. These messages can be chained, to access a hierarchy of namespaces. For example:

    dotNetHandle.System.Reflection.Assembly.LoadFile("c:\foo\bar\myAssembly.dll");

Instantiating a Class[Bearbeiten]

Object instances are created via the "new"-message, sent to a global:

    b = dotNetHandle.Button.new();
    f = dotNetHandle.Form.new();

once instantiated, any message can be sent transparently to such an .NET object, as if it was an expecco object:

    b.text("Hello World");
    f.controls.add(b);
    f.showDialog();

Casting to Specific Numeric Types[Bearbeiten]

Smalltalk numeric data types are mapped to .NET data types as follows:

  SmallInteger values between:-2147483648 and:2147483647 -> Int32
  Integer      values < -2147483648 or > 2147483647      -> Int64
  Float      -> Double
  ShortFloat -> Single

If you use other numeric data types, you have to do an explicit cast. This is especially needed to match a .NET function with a specific type argument:

   public static UInt32 testUInt32(UInt32 val)
   {
       return val;
   }

you can call this function from expecco/Smalltalk with:

   testType testUInt32:(0 castAs:#'System.UInt32')

Following casts are supported:

   .NET Type       Smalltalk expression                              JavaScript expression
   ----------------------------------------------------------------------------------------------------------------
   Byte            (255 castAs:#'System.Byte')                       255.castAs(#'System.Byte')
   SByte           (-128 castAs:#'System.SByte')                     -128.castAs(#'System.SByte')
   Int16           (-32768 castAs:#'System.Int16')                   -32768.castAs(#'System.Int16')
   UInt16          (65535 castAs:#'System.UInt16')                   65635.castAs(#'System.UInt16')
   UInt32          (4294967295 castAs:#'System.UInt32')              4294967295.castAs(#'System.UInt32')   
   Int64           (9223372036854775808 castAs:#'System.Int64')      9223372036854775808.castAs:(#'System.Int64')
   UInt64          (18446744073709551615 castAs:#'System.UInt64')    18446744073709551615.castAs:(#'System.UInt64')

Initializing .NET Array Objects[Bearbeiten]

Only skalar types array "literals" (byte[], short[], float[], double[], long[], ...) are mapped directly to/from the corresponding Smalltalk array types (uint32[] <-> IntegerArray, int32[] <-> SignedIntegerArray, float[] <-> FloatArray, ....).

Arrays with non-literal elements have to be initialize explicitly. E.g. a String array is initialized the following way:

   "/ create a String Array with 20 elements.
   strArray := (bridge getDotNetType:'System.String[]') new:20.
   "/ initialize each element
   "/ NOTE! array index in .NET is 0-based!.
   strArray at:0 put:'Test0'.
   strArray at:1 put:'Test1'.
   strArray at:2 put:'Test2'.
   strArray at:19 put:'Test19'.

Callbacks[Bearbeiten]

Callbacks from .NET back into expecco are implemented via Smalltalk blocks (JavaScript anonymous functions). The bridge automatically installs an appropriate callback, whenever a block/function is given to a .NET component as a callback or hook.

Here is a complete example for creating a .NET form with a callback into expecco. This code could be put into an EB (Elementary Block) of an expecco activity diagram:

    var dotNet, form, button;

    // called when the .NET button is clicked
    function callBack() {
        dotNet.MessageBox.show("Hello from expecco");
    };

    dotNet = DOTNET::DotNet.newWithServer();
    dotNet.loadLibrary("System.Windows.Forms");

    button = dotNet.Button.new();
    button.text("Hello");
    button.click.add( callBack );

    form = dotNet.Form.new();
    button.dock( dotNet.DockStyle.fill );
    form.controls.add( button );

    form.showDialog();

    dotNet.close();

Remote Dynamic Code Injection[Bearbeiten]

C# and VB code can be dynamically loaded and compiled:

C#:

    typeCode := '
        using System;

        namespace SomeNamespace
        {
            class SomeClass
            {
                public static void ConsoleWrite(string text)
                {
                    Console.Write(text)
                }
            }
        }
    '.
    typeObject := dotnet getDotNetType:'SomeNamespace.SomeClass' fromCSharpSource:typeCode.
    typeObject ConsoleWrite:'Hello, World!'.

VB:


    moduleCode := '
        Imports System

        Namespace SomeNamespace
            Public Module SomeModule
                Sub ConsoleWrite(ByVal text As String)
                    Console.WriteLine (text)
                End Sub
            End Module
        End Namespace
    '.
    moduleObject := dotnet getDotNetType:'SomeNamespace.SomeModule' fromVBSource:moduleCode.
    moduleObject ConsoleWrite:'Hello, World!'.

Calling ActiveX/COM Objects[Bearbeiten]

It's possible to make calls to ActiveX/COM Objects through the bridge.

To do this, you need to create a dotNet Assembly with the types from a .tlb or .ocx file.

This can be done with the tlbimp utility which is part of VisualStudio (reachable through the VS Dev CMD Shell...)

In this shell, you can e.g. execute

tlbimp c:\windows\system32\wshom.ocx

which will create a file called IWshRuntimeLibrary.dll for the Windows Script Host Runtime Library in the current directory. This can then be used to automate the Windows Script Host Runtime Library Object.

Here an example how to use this in code:


    |dotNet shell buttonPressed|

    dotNet := DOTNET::DotNet newWithServer.
    dotNet loadFile:'C:\Temp\IWshRuntimeLibrary.dll'.
    shell := dotNet WshShellClass new.
    buttonPressed := shell popup:'Hello from Micha' secToWait:15 title:'.NET Bridge Com Demo' type:0.
    buttonPressed inspect


to be continued

See Also[Bearbeiten]

The Java Interface Plugin & Library, which implements a likewise interface for Java applications/libraries.


Back to Plugins
Back to Online Documentation.



Copyright © 2014-2024 eXept Software AG