StrokesPlus.net
Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
Rob  
#1 Posted : Monday, December 14, 2020 3:23:48 AM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,359
United States
Location: Tampa, FL

Thanks: 28 times
Was thanked: 419 time(s) in 356 post(s)
Beginning in 0.4.0.6 you can now dynamically define and invoke your own types and native DLL calls from script.

Below are some examples which cover the most common scenarios. Check the Extensions and Native and Dynamic sections in Script Help for more information.


Code to define types and PInvokes:
see Action code below to call these and how to check if the type already exists
Code:
if(!NativeModules.Types)
{
    var Int32T = host.typeOf(clr.System.Int32);
    var IntPtrT = host.typeOf(clr.System.IntPtr);
    var BooleanT = host.typeOf(clr.System.Boolean);
    var umt = clr.System.Runtime.InteropServices.UnmanagedType;

    //------------------------------------------------------------------------------------------------
    // Define and create (can only add nested types) the type which will contain the structs
    //------------------------------------------------------------------------------------------------

    var typesTB = sp.NativeModule().CreateType("Types", "Class,Public,SequentialLayout,Serializable");

    typesTB.DefineNestedStruct("TestUnmanagedType", 
                               "X,Y", 
                               [Int32T,Int32T],
                               [umt.I4,umt.I4]
    ).Create();

    typesTB.DefineNestedStruct("POINT", 
                               "X,Y", 
                               [Int32T,Int32T]).Create();

    typesTB.DefineNestedStruct("RECT", 
                               "Left,Top,Right,Bottom", 
                               [Int32T,Int32T,Int32T,Int32T]).Create();

    var POINTT = host.typeOf(NativeModules.Types.POINT);
    var RECTT = host.typeOf(NativeModules.Types.RECT);

    typesTB.DefineNestedStruct("WINDOWPLACEMENT", 
                               "length,flags,showCmd,ptMinPosition,ptMaxPosition,rcNormalPosition", 
                               [Int32T,Int32T,Int32T,POINTT,POINTT,RECTT]).Create();

    typesTB.DefineNestedEnum("NestedEnum",
                             "Low,High",
                             [0,1]).Create();


    //-----------------------------------------------------------------------------------------------
    // Define the type which will contain the PInvokes
    // Type can still be modified until .Create() is called
    //-----------------------------------------------------------------------------------------------

    var user32TB = sp.NativeModule().DefineType("User32", "Class,Public,SequentialLayout,Serializable");

        var WINDOWPLACEMENT_refT = host.typeOf(NativeModules.Types.WINDOWPLACEMENT).MakeByRefType();

        user32TB.DefinePInvokeMethod("GetWindowPlacement",
                                     "user32.dll",
                                     [IntPtrT,WINDOWPLACEMENT_refT], 
                                     BooleanT, 
                                     "PreserveSig");

        user32TB.DefinePInvokeMethod("EnumWindows",
                                     "user32.dll",
                                     [IntPtrT,IntPtrT], 
                                     BooleanT, 
                                     "PreserveSig");

        //--------------------------------------------------------------------------------------------
        // Define other PInvokes here before calling user32TB.Create()
        //--------------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------------------
    // Creates the type (which cannot be changed after) and refreshes the 
    // NativeModules assembly in the script engine
    //------------------------------------------------------------------------------------------------
    user32TB.Create();


    //------------------------------------------------------------------------------------------------
    // Creates an enum at the top level of NativeModules, make sure it doesn't have 
    // same name as another type
    //------------------------------------------------------------------------------------------------
    sp.NativeModule().DefineEnum("TestEnum",
                                 "Low,High",
                                 [0,1]).Create();
}


//------------------------------------------------------------------------------------------------
// Create a custom type and method at the top level of NativeModules, make sure it 
// doesn't have same name as another type
//------------------------------------------------------------------------------------------------

if(!NativeModules.MyType)
{
    var StringT = host.typeOf(clr.System.String);
    var emit = clr.System.Reflection.Emit;

    myTypeTB = sp.NativeModule().DefineType("MyType", "Class,Public,SequentialLayout,Serializable");

    var myMethod = myTypeTB.NewMethod("MyMethod",
                                      [StringT], 
                                      StringT, 
                                      "Public",
                                      "Standard"
    );

    //Additional code necessary to build method, below is a simple example
    var myMethodIL = myMethod.GetILGenerator();

    myMethodIL.Emit(emit.OpCodes.Ldstr, "Hi! ");
    myMethodIL.Emit(emit.OpCodes.Ldarg_1);

    var conCatParamTypes = new List(clr.System.Type);
    conCatParamTypes.Add(StringT);
    conCatParamTypes.Add(StringT);
    var infoMethod = StringT.GetMethod("Concat", conCatParamTypes.ToArray());

    myMethodIL.Emit(emit.OpCodes.Call, infoMethod);
    myMethodIL.Emit(emit.OpCodes.Ret);

    // Create type and refresh native modules
    myTypeTB.Create();
}


Example Action script after defining the types above:
Code:
if(!NativeModules.Types || !NativeModules.User32)
{
    sp.MessageBox("No API types defined", "No Types");
}
else
{
    sp.MessageBox(NativeModules.Types.NestedEnum.High, "NativeModules.Types.NestedEnum.High");

    sp.MessageBox(NativeModules.TestEnum.Low, "NativeModules.TestEnum.Low");

    var wndP = host.newVar(NativeModules.Types.WINDOWPLACEMENT)
    var res = NativeModules.User32.GetWindowPlacement(sp.ForegroundWindow().HWnd, wndP.ref);

    sp.MessageBox("res: " + res 
                  + "\nwndP.rcNormalPosition.Top: " + wndP.rcNormalPosition.Top
                  + "\nwndP.rcNormalPosition.Left: " + wndP.rcNormalPosition.Left 
                  + "\nwndP.showCmd: " + wndP.showCmd
                  , "Window Placement");

    // Create the EnumWindows callback 
    function EnumWindowsProc(hWnd, lParam)
    {
        // Builds a string of all windows, put on clipboard after EnumWindows call
        let currentStr = sp.GetStoredString("enumWindows");
        currentStr += `Handle: ${hWnd.ToInt64()}\n`;
        currentStr += `\t Text: ${sp.WindowFromHandle(hWnd).Text}\n`;
        currentStr += `\t lParam: ${lParam.ToInt32()}\n`;
        sp.StoreString("enumWindows", currentStr);
        return true;
    }

    //Clear string for subsequent calls
    sp.StoreString("enumWindows", "");

    var BooleanT = host.typeOf(clr.System.Boolean);
    var IntPtrT = host.typeOf(clr.System.IntPtr);

    // Creates a static class, method, and delegate for script function, 
    // returns IntPtr of delegate
    // functionName, parameterTypes[], returnType (null for void)
    // After the first call, it won't create again, can be referenced via
    // var delPtr = NativeDelegates.EnumWindowsProc_C.GetPointer();
    // But you can use the same code as below, it will just return the already created pointer
    var delPtr = sp.NativeDelegate("EnumWindowsProc", [IntPtrT, IntPtrT], BooleanT);

    //Value passed to EnumWindowsProc, can be used to signal type of action to perform
    var lParam = new IntPtr(123);
    NativeModules.User32.EnumWindows(delPtr, lParam);

    //Copy results of EnumWindows string to clipboard
    clip.SetText(sp.GetStoredString("enumWindows"));

    sp.MessageBox("EnumWindows Complete - Check Clipboard", "Done");
}

if(!NativeModules.MyType)
{
    sp.MessageBox("MyType is not defined", "MyType Missing");
}
else
{
    var newType  = new NativeModules.MyType();
    var result = newType.MyMethod("Test");

    sp.MessageBox(`Result: ${result}`, "Success!")
}

Edited by user Thursday, December 17, 2020 9:40:42 PM(UTC)  | Reason: Updated scripts for better handling and demo NewMethod, updated setting of EnumWindowsProc function

thanks 1 user thanked Rob for this useful post.
niczoom on 2/10/2021(UTC)
Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.