.NET Framework Interop (CLR, C#, VB)

Post your working scripts, libraries and tools
metaprogram
Posts: 4
Joined: 16 Dec 2020, 16:31

Re: .NET Framework Interop (CLR, C#, VB)

16 Dec 2020, 17:25

I think there is a bug in this script when returning arrays of length 0. For example:

Code: Select all

    ; Return a string[] type
    cSharp =
    (
        class ArrayReturnValueTest
        {
            public string[] Foo()
            {
                return new string[0]{};
            }
        }
    )

    obj := CLR_CreateObject( CLR_CompileC#( cSharp ), "ArrayReturnValueTest")
    result := obj.Foo()
    For found in result
    {
        
    }
complains about an invalid memory read/write. Anyone know how to fix this?
lexikos
Posts: 7194
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 02:05

The script just calls the compiler and instantiates the class. It is not responsible for marshaling the method call or return value, or implementing the method or enumeration (for-loop).

The bug is with enumeration of an empty SafeArray by AutoHotkey, and can potentially be reproduced by for a in ComObjArray(0x8, 0) { ... }.
serzh82saratov
Posts: 130
Joined: 01 Jul 2017, 03:04

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 11:00

Hello!
Is it possible using the CLR to create a Windows.ApplicationModel.DataTransfer object, and then use it in AutoHotkey?
Here is a sample code, not convenient and difficult to use for each DllCall method (NumGet (NumGet (.
Would like something like here JavaScript is easy to use an object.

Code: Select all

; Author - teadrinker
; http://forum.script-coding.com/viewtopic.php?pid=145546#p145546

; puts the second item from the Windows 10 clipboard history into the Clipboard and returns a success status
MsgBox, % (new ClipboardHistory).PutHistoryItemIntoClipboard(2)

; get the text of the elements of the clipboard history by index
ClipHist := new ClipboardHistory
Loop % ClipHist.Count
   MsgBox,, Item %A_Index%, % ClipHist.GetHistoryItemText(A_Index), .7

class ClipboardHistory
{
; https://is.gd/bYyogJ ; Clipboard Class (MSDN)
; https://is.gd/2z2Y9G ; Windows.ApplicationModel.DataTransfer.0.h (GitHub)
; https://is.gd/T4Lb7b ; asyncinfo.h (GitHub)
   __New() {
      static IID_IClipboardStatics2 := "{D2AC1B6A-D29F-554B-B303-F0452345FE02}"
      if !(A_OSVersion ~= "^\d") {
         MsgBox, This class requires Windows 10 or later!
         Return
      }
      riid := CLSIDFromString(IID_IClipboardStatics2, _)

      WinStr := new WindowsString("Windows.ApplicationModel.DataTransfer.Clipboard")
      WinStr.CreateInterface(riid, pIClipboardStatics2)
      WinStr := ""

      this.ClipboardStatics2 := new IClipboardStatics2( pIClipboardStatics2 )
   }
   
   __Delete() {
      this.ClipboardStatics2 := ""
   }
   
   IsHistoryEnabled[] {
      get {
         Return this.ClipboardStatics2.IsHistoryEnabled
      }
   }
   
   Count[] {
      get {
         Return this._GetClipboardHistoryItems(ReadOnlyList)
      }
   }
   
   ClearHistory() {
      Return this.ClipboardStatics2.ClearHistory()
   }
   
   GetHistoryItemText(index) { ; 1 based
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      pIDataPackageView := ClipboardHistoryItem.Content
      DataPackageView := new IDataPackageView( pIDataPackageView )
      pIReadOnlyList := DataPackageView.AvailableFormats
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Loop % ReadOnlyList.Count
         if StrGet(ReadOnlyList.Item[A_Index - 1] + 20 + A_PtrSize, "UTF-16") = "Text" && textFound := true
            break
      if !textFound
         Return
      DataPackageView.GetTextAsync(pIAsyncOperation)
      pString := this._AsyncOperationGetResults(pIAsyncOperation)
      Return StrGet(pString + 20 + A_PtrSize, "UTF-16")
   }
   
   PutHistoryItemIntoClipboard(index) { ; 1 based
      static SetHistoryItemAsContentStatus := ["Success", "AccessDenied", "ItemDeleted"]
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      status := this.ClipboardStatics2.SetHistoryItemAsContent( ClipboardHistoryItem.ptr )
      Return SetHistoryItemAsContentStatus[ status + 1 ]
   }
   
   _GetClipboardHistoryItemByIndex(index) { ; 1 based
      count := this._GetClipboardHistoryItems(ReadOnlyList)
      if (count < index) {
         MsgBox, 48, % " ", Index "%index%" exceeds items count!
         Return
      }
      Return pIClipboardHistoryItem := ReadOnlyList.Item[index - 1]
   }
   
   _GetClipboardHistoryItems(ByRef ReadOnlyList) {
      this.ClipboardStatics2.GetHistoryItemsAsync( pIAsyncOperation )
      pIClipboardHistoryItemsResult := this._AsyncOperationGetResults(pIAsyncOperation)
      ClipboardHistoryItemsResult := new IClipboardHistoryItemsResult( pIClipboardHistoryItemsResult )
      pIReadOnlyList := ClipboardHistoryItemsResult.Items
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Return ReadOnlyList.Count
   }
   
   _AsyncOperationGetResults(pIAsyncOperation) {
      static AsyncStatus  := ["Started", "Completed", "Canceled", "Error"]
      AsyncOperation := new IAsyncOperation( pIAsyncOperation )
      AsyncOperation.QueryIAsyncInfo(pIAsyncInfo)
      AsyncInfo := new IAsyncInfo( pIAsyncInfo )
      Loop {
         Sleep, 10
         status := AsyncStatus[ AsyncInfo.Status + 1 ]
      } until status != "Started" || status = ""
      if (status != "Completed")
         throw "AsyncInfo error, status: """ . status . """"
      AsyncOperation.GetResults( pResult )
      Return pResult
   }
}

class IClipboardStatics2 extends _InterfaceBase
{
   GetHistoryItemsAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(6), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
   ClearHistory() {
      hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   SetHistoryItemAsContent(pIClipboardHistoryItem) {
      hr := DllCall(this.VTable(9), "Ptr", this.ptr, "Ptr", pIClipboardHistoryItem, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   IsHistoryEnabled[] {
      get {
         hr := DllCall(this.VTable(10), "Ptr", this.ptr, "UIntP", res)
         this.IsError(A_ThisFunc, hr)
         Return res
      }
   }
}

class IClipboardHistoryItemsResult extends _InterfaceBase
{
   Items[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
}

class IClipboardHistoryItem extends _InterfaceBase
{
   Content[] {
      get {
         hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pIDataPackageView)
         this.IsError(A_ThisFunc, hr)
         Return pIDataPackageView
      }
   }
}

class IDataPackageView extends _InterfaceBase
{
   AvailableFormats[] {
      get {
         hr := DllCall(this.VTable(9), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
   GetTextAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(12), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
}

class IReadOnlyList extends _InterfaceBase
{
   Item[index] {
      get {
         hr := DllCall(this.VTable(6), "Ptr", this.ptr, "Int", index, "PtrP", pItem)
         this.IsError(A_ThisFunc, hr)
         Return pItem
      }
   }
   Count[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", count)
         this.IsError(A_ThisFunc, hr)
         Return count
      }
   }
}

class IAsyncOperation extends _InterfaceBase
{
   QueryIAsyncInfo(ByRef pIAsyncInfo) {
      static IID_IAsyncInfo := "{00000036-0000-0000-C000-000000000046}"
      pIAsyncInfo := ComObjQuery(this.ptr, IID_IAsyncInfo)
   }
   GetResults(ByRef pResult) {
      hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pResult)
      this.IsError(A_ThisFunc, hr)
   }
}

class IAsyncInfo extends _InterfaceBase
{
   Status[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", status)
         this.IsError(A_ThisFunc, hr)
         Return status
      }
   }
}

class _InterfaceBase {
   __New(ptr) {
      this.ptr := ptr
   }
   __Delete() {
      ObjRelease(this.ptr)
   }
   VTable(idx) {
      Return NumGet(NumGet(this.ptr + 0) + A_PtrSize*idx)
   }
   IsError(method, result, exc := true) {
      if (result = 0)
         Return 0
      error := StrReplace(method, ".", "::") . " failed.`nResult: "
                              . ( result = "" ? "No result" : SysError(Format("{:#x}", result & 0xFFFFFFFF)) )
                              . "`nErrorLevel: " . ErrorLevel
      if !exc
         Return error
      throw error
   }
}

class WindowsString {
   __New(string, isHandle := false) {
      if isHandle
         this.hString := string
      else {
         DllCall("Combase\WindowsCreateString", "WStr", string, "UInt", StrLen(string), "PtrP", hString)
         this.hString := hString
      }
   }
   __Delete() {
      DllCall("Combase\WindowsDeleteString", "Ptr", this.hString)
   }
   Get() {
      pBuff := DllCall("Combase\WindowsGetStringRawBuffer", "Ptr", this.hString, "UIntP", len, "Ptr")
      Return StrGet(pBuff, len, "UTF-16")
   }
   CreateInterface(riid, ByRef pInterface) {
      hr := DllCall("Combase\RoGetActivationFactory", "Ptr", this.hString, "Ptr", riid, "PtrP", pInterface)
      if (hr != 0)
         throw SysError(hr)
   }
}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if hr := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw "CLSIDFromString failed. Error: " . Format("{:#x}", hr)
   Return &CLSID
}

SysError(ErrorNum = "")
{ 
   static flag := (FORMAT_MESSAGE_ALLOCATE_BUFFER := 0x100) | (FORMAT_MESSAGE_FROM_SYSTEM := 0x1000)
   (ErrorNum = "" && ErrorNum := A_LastError)
   DllCall("FormatMessage", "UInt", flag, "UInt", 0, "UInt", ErrorNum, "UInt", 0, "PtrP", pBuff, "UInt", 512, "Str", "")
   Return (str := StrGet(pBuff)) ? str : ErrorNum
}
serzh82saratov
Posts: 130
Joined: 01 Jul 2017, 03:04

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 11:57

I try so, error.
Spoiler
Last edited by serzh82saratov on 30 Jan 2021, 13:45, edited 1 time in total.
serzh82saratov
Posts: 130
Joined: 01 Jul 2017, 03:04

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 13:44

More precisely, something like this. The point is to get the root object and manipulate it from AutoHotkey.
This is an approximate VB code, maybe you suggest C # and it will be better.

Code: Select all

vb =
( 
   Imports System
   Imports Windows
   Class Foo
      Function GetWA() as Object
         Return Windows.ApplicationModel
     End Function
   End Class
)
asm := CLR_CompileVB(vb, "System.dll | Windows.dll")
Foo := CLR_CreateObject(asm, "Foo")
WA := Foo.GetWA()
WA.DataTransfer.Clipboard.clear()
lexikos
Posts: 7194
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 19:29

You can do whatever you want in C# or VB. This script is just for loading the CLR, invoking the C# and VB compilers and creating instances of classes directly. Once you have an instance, you use it via .NET COM interop (as in, the COM interfaces provided by the library or the CLR itself) and AutoHotkey's COM support, which are not part of this script.
serzh82saratov
Posts: 130
Joined: 01 Jul 2017, 03:04

Re: .NET Framework Interop (CLR, C#, VB)

30 Jan 2021, 19:49

Yes, but it is with Windows.ApplicationModel that it fails.
lexikos
Posts: 7194
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: .NET Framework Interop (CLR, C#, VB)

31 Jan 2021, 00:48

What's your point? Mine is that the failure is outside the scope of this script.

Return to “Scripts and Functions”

Who is online

Users browsing this forum: Jinpachi, neogna2 and 30 guests