How to create IEnumerable<Int64> for DllCall?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

How to create IEnumerable<Int64> for DllCall?

23 Nov 2021, 01:06

[Moderator's note: Topic split from .NET Framework Interop (CLR, C#, VB).]

I need to send IEnumerable<Int64> as parameter to dllcall.
How can I create it?
This code does not work.
It writes
System.InvalidOperationException
HResult=0x80131509

Code: Select all

c# =
(
using System;
using System.Collections.Generic;
class test
{
    public IEnumerable<long> Main()
    {
        IEnumerable<long> Numbers = new List<long> {1, 2, 3};
        return Numbers;
    }
}
)
asm := CLR_CompileC#(c#, "System.dll")
obj := CLR_CreateObject(asm, "test")
list := obj.Main()
May be I can create IEnumerable<Int64> without using .NET Framework Interop?
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

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

23 Nov 2021, 04:00

malcev wrote:I need to send IEnumerable<Int64> as parameter to dllcall.
That doesn't make any sense. Only managed methods take generic interfaces as parameters. You can't call managed methods via DllCall, although you can call COM callable wrappers.

As far as I can tell, COM interop does not support generic interfaces. See my previous post about this for more details.
May be I can create IEnumerable<Int64> without using .NET Framework Interop?
If whatever you are using doesn't already require .NET Framework Interop, you are probably barking up the wrong tree. Maybe what you want is not the IEnumerable<Int64> managed interface at all, but something else.

What API are you trying to call?
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

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

23 Nov 2021, 06:49

I try to call Create(IIterable<Int64>).
https://docs.microsoft.com/en-us/uwp/ap ... stem_Int64__
https://github.com/825126369/UCRT/blob/8853304fdc2a5c216658d08b6dbbe716aa2a7b1f/Include/10.0.18362.0/winrt/Windows.AI.MachineLearning.h#L4739
I can use StringMap Class, but it creates only dictionaries.
https://docs.microsoft.com/en-us/uwp/api/windows.foundation.collections.stringmap?view=winrt-22000
Example:

Code: Select all

CreateClass("Windows.AI.MachineLearning.TensorFloat", ITensorFloatStatics := "{DBCD395B-3BA3-452F-B10D-3C135E573FA9}", TensorFloatStatics)
; works ok
msgbox % DllCall(NumGet(NumGet(TensorFloatStatics+0)+6*A_PtrSize), "ptr", TensorFloatStatics, "ptr*", TensorFloat)   ; TensorFloatStatics.Create
; returns error
msgbox % DllCall(NumGet(NumGet(TensorFloatStatics+0)+7*A_PtrSize), "ptr", TensorFloatStatics, "ptr", IEnumerable, "ptr*", TensorFloat)   ; TensorFloatStatics.Create2


CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to create IEnumerable<Int64> for DllCall?

25 Nov 2021, 04:15

That's what I figured. I have split the topic.

WinRT is not .NET. IIterable is not IEnumerable. If you are using WinRT from C#, the managed method generated by the language projection will accept IEnumerable, but will actually pass IIterable to the real WinRT API. Even if you had a COM callable wrapper for an IEnumerable, it would be the wrong interface.

If there are no WinRT classes that implement IIterable<Int64> (as I suspect), you would need to implement the interface yourself. It is fundamentally the same as implementing any other COM interface in AutoHotkey. There is one additional catch: the IID is calculated from a base GUID and the type names used to instantiate the generic interface. You may use RoGetParameterizedTypeInstanceIID for this, but it is not simple. Microsoft expect developers of language projections to do their own caching of metadata, so you can't just pass in a type name like IEnumerable<Int64> and get back an IID. The relevant parts of the process for you are:
  • Get the PIID (Parameterized IID) of IIterable from metadata or documentation.
  • Make an array of pointers to names containing "IIterable" and "Int64".
  • Implement your own IRoMetaDataLocator. The documentation incorrectly declares it as a struct with a single non-virtual method - it's actually a virtual method. In other words, it is a struct containing a pointer to a method table with exactly one method (it does not derive from IUnknown or IInspectable). If you just create a buffer and put the callback address into it, you can then pass the address of the buffer (which you might call the method table) as "ptr*", so the temporary variable created by DllCall implicitly becomes the object. (In short, a pointer to a pointer to a callback = an object with one virtual method.)
  • Call RoGetParameterizedTypeInstanceIID. It will call your callback once for each non-fundamental type name, passing the name and a pointer to a IRoSimpleMetaDataBuilder. This is another unconventional interface which does not derive from IUnknown or IInspectable.
  • Because Int64 is a fundamental type, your callback will only be called for "IIterable". Your callback must then call IRoSimpleMetaDataBuilder::SetParameterizedInterface, passing the PIID of IInspectable.
As discussed elsewhere, I have been working on a "WinRT language projection" written in and for AutoHotkey v2 (but I haven't shared it yet). It already does most things, including automatically wrapping instances of generic interfaces, but has only part of the code needed for implementing interfaces. I'm not interested in writing any complex scripts (like this) for AutoHotkey v1, but it could be used to do most of the legwork, such as calculating method indices and IIDs.

Anyway, the IID of IIterable<Int64> should be {7784427E-F9CC-518D-964B-E50D5CE727F1}.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to create IEnumerable<Int64> for DllCall?

26 Nov 2021, 02:09

Thank You, lexikos.
I get this interface.
But what should I do with this interface?
How can I execute its methods without class?

Code: Select all

name1 := "IIterable"
name2 := "Int64"

DllCall("LoadLibrary", "str", "api-ms-win-core-winrt-roparameterizediid-l1-1-0.dll", "ptr") 
call := RegisterCallback("metaDataLocator", "Fast")
varsetcapacity(callback, A_PtrSize, 0)
numput(call, callback, 0)
varsetcapacity(nameElements, A_PtrSize*2, 0)
loop 2
{
   varsetcapacity(var%A_Index%, strput(name%A_Index%, "utf-16")*2, 0)
   strput(name%A_Index%, &var%A_Index%, "utf-16")
   numput(&var%A_Index%, nameElements, (A_Index-1)*A_PtrSize)
}
VarSetCapacity(GUID, 16)
DllCall("api-ms-win-core-winrt-roparameterizediid-l1-1-0.dll\RoGetParameterizedTypeInstanceIID", "uint", 2, "ptr", &nameElements, "ptr*", &callback, "ptr", &GUID, "ptr*", ROPARAMIIDHANDLE)
DllCall("ole32\StringFromCLSID", "ptr", &GUID, "ptr*", lplpsz)
IID := StrGet(lplpsz, "utf-16")
DllCall("ole32\CoTaskMemFree", "ptr", lplpsz)
msgbox % IID


metaDataLocator(this, name, IRoSimpleMetaDataBuilder)
{
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", IIterable := "{faa585ea-6214-4217-afda-7f46de5869b3}", "ptr", &GUID)
   if (A_PtrSize = 4)
      DllCall(NumGet(NumGet(IRoSimpleMetaDataBuilder+0)+8*A_PtrSize), "ptr", IRoSimpleMetaDataBuilder, "int64", NumGet(GUID, 0, "int64"), "int64", NumGet(GUID, 8, "int64"), "uint", 1)   ; IRoSimpleMetaDataBuilder.SetParameterizedInterface
   else
      DllCall(NumGet(NumGet(IRoSimpleMetaDataBuilder+0)+8*A_PtrSize), "ptr", IRoSimpleMetaDataBuilder, "ptr", &GUID, "uint", 1)   ; IRoSimpleMetaDataBuilder.SetParameterizedInterface
}
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to create IEnumerable<Int64> for DllCall?

26 Nov 2021, 20:09

The most important part of my post was:
If there are no WinRT classes that implement IIterable<Int64> (as I suspect), you would need to implement the interface yourself. It is fundamentally the same as implementing any other COM interface in AutoHotkey.
I did not go into detail because I have seen it done several times, and thought you may have done it yourself with other interfaces.
  • Create a callback for each method.
  • Create the method table - an array of callback addresses in the appropriate order as defined by the interface.
  • Create a struct and place the address of the method table as the first member of the struct.
  • The address of the struct is the "interface pointer".
I get this interface.
You get what interface? The end result of your script is an interface identifier (IID). Normally you would need it in order to query an object for that interface, but you also need to know what it is when you are the one implementing the object, so that you can respond correctly when your QueryInterface method is called.

For an example of implementing a COM interface and responding to QueryInterface, you could refer to SetWBClientSite() in the AutoHotkey installer (although that one implements multiple interfaces), or try searching for combinations like RegisterCallback and AddRef. You would need to implement all of the methods of IIterable, including those inherited from IInspectable.

For IInspectable::GetRuntimeClass, the convention I have observed for generic collection types is to return an interface name like "Windows.Foundation.Collections.IIterable`1<Int64>" (but you would write `` for a literal `). It might be the case that this method is never called by a WinRT API, since it's probably only useful for language projections.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to create IEnumerable<Int64> for DllCall?

27 Nov 2021, 15:45

I tried but without success.
I do not see that TensorFloatStatics.Create queries for IIterable interface.

Code: Select all

CreateClass("Windows.AI.MachineLearning.TensorFloat", ITensorFloatStatics := "{DBCD395B-3BA3-452F-B10D-3C135E573FA9}", TensorFloatStatics)

Iterable := IIterable_new()
global Iterator := IIterator_new()
msgbox % DllCall(NumGet(NumGet(TensorFloatStatics+0)+7*A_PtrSize), "ptr", TensorFloatStatics, "ptr", Iterable, "ptr*", TensorFloat)   ; TensorFloatStatics.Create

IIterable_new() {
   static VTBL := [ "QueryInterface"
                  , "AddRef"
                  , "Release"
                  , "GetIids" 
                  , "GetRuntimeClassName"
                  , "GetTrustLevel"
                  , "First" ]
        , heapSize := A_PtrSize*10
        , heapOffset := A_PtrSize*9
        
        , flags := (HEAP_GENERATE_EXCEPTIONS := 0x4) | (HEAP_NO_SERIALIZE := 0x1)
        , HEAP_ZERO_MEMORY := 0x8
   
   hHeap := DllCall("HeapCreate", "UInt", flags, "Ptr", 0, "Ptr", 0, "Ptr")
   addr := IIterable := DllCall("HeapAlloc", "Ptr", hHeap, "UInt", HEAP_ZERO_MEMORY, "Ptr", heapSize, "Ptr")
   addr := NumPut(addr + A_PtrSize, addr + 0)
   for k, v in VTBL
      addr := NumPut(RegisterCallback("IIterable_" . v), addr + 0 )
   NumPut(hHeap, IIterable + heapOffset)
   Return IIterable
}

IIterable_QueryInterface(this, riid, ppvObject)
{
   msgbox query IIterable
   static IID_IUnknown, IID_IIterable
   if (!VarSetCapacity(IID_IUnknown))
   {
      VarSetCapacity(IID_IUnknown, 16), VarSetCapacity(IID_IIterable, 16)
      DllCall("ole32\CLSIDFromString", "WStr", "{00000000-0000-0000-C000-000000000046}", "Ptr", &IID_IUnknown)
      DllCall("ole32\CLSIDFromString", "WStr", "{7784427E-F9CC-518D-964B-E50D5CE727F1}", "Ptr", &IID_IIterable)
   }
   if (DllCall("ole32\IsEqualGUID", "Ptr", riid, "Ptr", &IID_IIterable) || DllCall("ole32\IsEqualGUID", "Ptr", riid, "Ptr", &IID_IUnknown))
   {
      NumPut(this, ppvObject+0, "Ptr")
      IIterable_AddRef(this)
      return 0 ; S_OK
   }
   NumPut(0, ppvObject+0, "Ptr")
   return 0x80004002 ; E_NOINTERFACE
}

IIterable_AddRef(this) {
   static refOffset := A_PtrSize*8
   NumPut(refCount := NumGet(this + refOffset, "UInt") + 1, this + refOffset, "UInt")
   Return refCount
}

IIterable_Release(this) {
   static refOffset := A_PtrSize*8
        , heapOffset := A_PtrSize*9
   NumPut(refCount := NumGet(this + refOffset, "UInt") - 1, this + refOffset, "UInt")
   if (refCount = 0) {
      hHeap := NumGet(this + heapOffset)
      DllCall("HeapDestroy", "Ptr", hHeap)
   }
   Return refCount
}

IIterable_GetIids(this, iidCount, IID)
{
   msgbox iid
   return
}

IIterable_GetRuntimeClassName(this, className)
{
   msgbox class
   return
}

IIterable_GetTrustLevel(this, TrustLevel)
{
   msgbox TrustLevel
   return
}

IIterable_First(this, first)
{
   msgbox first
   NumPut(Iterator, first+0, 0, "ptr")
   return 
}

IIterator_new() {
   static VTBL := [ "QueryInterface"
                  , "AddRef"
                  , "Release"
                  , "GetIids" 
                  , "GetRuntimeClassName"
                  , "GetTrustLevel"
                  , "get_Current"
                  , "get_HasCurrent"
                  , "MoveNext"
                  , "GetMany" ]
        , heapSize := A_PtrSize*20
        , heapOffset := A_PtrSize*19
        
        , flags := (HEAP_GENERATE_EXCEPTIONS := 0x4) | (HEAP_NO_SERIALIZE := 0x1)
        , HEAP_ZERO_MEMORY := 0x8
   
   hHeap := DllCall("HeapCreate", "UInt", flags, "Ptr", 0, "Ptr", 0, "Ptr")
   addr := IIterator := DllCall("HeapAlloc", "Ptr", hHeap, "UInt", HEAP_ZERO_MEMORY, "Ptr", heapSize, "Ptr")
   addr := NumPut(addr + A_PtrSize, addr + 0)
   for k, v in VTBL
      addr := NumPut(RegisterCallback("IIterator_" . v), addr + 0 )
   NumPut(hHeap, IIterator + heapOffset)
   Return IIterator
}

IIterator_QueryInterface(this, riid, ppvObject)
{
   msgbox query IIterator
   static IID_IUnknown, IID_IIterator
   if (!VarSetCapacity(IID_IUnknown))
   {
      VarSetCapacity(IID_IUnknown, 16), VarSetCapacity(IID_IIterator, 16)
      DllCall("ole32\CLSIDFromString", "WStr", "{00000000-0000-0000-C000-000000000046}", "Ptr", &IID_IUnknown)
      DllCall("ole32\CLSIDFromString", "WStr", "{FB98034C-86B7-581F-8CD9-5AD0692201A9}", "Ptr", &IID_IIterator)
   }
   if (DllCall("ole32\IsEqualGUID", "Ptr", riid, "Ptr", &IID_IIterator) || DllCall("ole32\IsEqualGUID", "Ptr", riid, "Ptr", &IID_IUnknown))
   {
      NumPut(this, ppvObject+0, "Ptr")
      IIterator_AddRef(this)
      return 0 ; S_OK
   }
   NumPut(0, ppvObject+0, "Ptr")
   return 0x80004002 ; E_NOINTERFACE
}

IIterator_AddRef(this) {
   static refOffset := A_PtrSize*18
   NumPut(refCount := NumGet(this + refOffset, "UInt") + 1, this + refOffset, "UInt")
   Return refCount
}

IIterator_Release(this) {
   static refOffset := A_PtrSize*18
        , heapOffset := A_PtrSize*19
   NumPut(refCount := NumGet(this + refOffset, "UInt") - 1, this + refOffset, "UInt")
   if (refCount = 0) {
      hHeap := NumGet(this + heapOffset)
      DllCall("HeapDestroy", "Ptr", hHeap)
   }
   Return refCount
}

IIterator_GetIids(this, iidCount, IID)
{
   msgbox iid
   return
}

IIterator_GetRuntimeClassName(this, className)
{
   msgbox class
   return
}

IIterator_GetTrustLevel(this, TrustLevel)
{
   msgbox TrustLevel
   return
}

IIterator_get_Current(this, current)
{
   msgbox get_Current
   numput(1000, current+0, 0, "int64")
   return
}

IIterator_get_HasCurrent(this, hasCurrent)
{
   msgbox get_HasCurrent
   numput(1, hasCurrent+0, 0, "int")   ; true
   return
}

IIterator_MoveNext(this, hasCurrent)
{
   msgbox MoveNext
   numput(0, hasCurrent+0, 0, "int")   ; false
   return
}

IIterator_GetMany(this, capacity, value, actual)
{
   msgbox GetMany
   return 
}


CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
   DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to create IEnumerable<Int64> for DllCall?

29 Nov 2021, 04:05

What is the expected result, and what is your actual result?

When I run your code, I get multiple message boxes:
  • first
  • get_HasCurrent
  • get_Current
  • MoveNext
  • (blank)
I guess that means the iterator was called as expected, although the last one is blank because an access violation occurred (exception 0xC0000005).

QueryInterface doesn't need to be called because the method is declared as accepting IIterable; i.e. it already has the exact interface it needs. You might be able to get away with not implementing it in this case, but in some cases the object will get passed around to different internal components, and some of them might query for the interface you originally gave it. (So in general, it's best not to cut corners.)


There are two significant problems with your reference counting:
  1. You start the reference count at 0. An object with 0 references should not exist.
  2. You return interface pointers without incrementing the reference count.
If you pass a zero-reference pointer to a function and it needs to create another reference, it will call AddRef. When it no longer needs its second (or first) reference, it will call Release. At that point the object will be deleted because its reference count reached (or came back to) zero - but the function still has another pointer that it thinks is valid.

If you return an interface pointer via an output parameter, the caller will generally release it at some point. If it does so without duplicating the reference (i.e. without calling AddRef), and the reference count is still 0, it will become 4294967296 (because you used UInt). If the caller duplicates the reference and then releases, the object will be deleted even though there are still pointers to it that are assumed to be valid.

It is for these reasons that your code causes an access violation.

To do it correctly:
  1. Always start an object's reference count at 1. If you aren't keeping your own reference, release it after you have passed it to the function. On the other hand, if your reference was in a local variable and being returned, you can skip both the AddRef and Release as the caller will take ownership.
  2. When you "create" a reference from an already existing pointer, you must correct the reference count - AddRef. Do this when returning a reference via an output parameter.
If you're not sure when to AddRef, think about what the code that receives your pointer is going to do. The only way for the pointer to be guaranteed valid until that code is finished using it, is for that code to own a reference, which it will Release when finished.

numput(0, hasCurrent+0, 0, "int")
I don't think the boolean type is 32-bit. The documentation isn't clear, and I found it hard to confirm within the SDK headers, but there is this:
Boolean maps to "b1"
...
The above names are case sensitive. With the exception of String, the type name uses a single character to suggest the kind of data, followed by its size in bytes.
Source: The Windows Runtime (WinRT) type system - Windows UWP applications | Microsoft Docs
Writing 4 bytes to a variable that's only 1 byte could be bad, depending on whether it's followed by something meaningful or just padding due to alignment requirements.

hHeap := DllCall("HeapCreate", "UInt", flags, "Ptr", 0, "Ptr", 0, "Ptr")
This is not what heaps are for! It is incredibly inefficient to create a new heap for each new object you want to allocate. The heap allocates or reserves entire pages (i.e. multiples of 4KB), and manages individual allocations within those.

I would suggest using one of the following instead:
  • GetProcessHeap and HeapAlloc
  • msvcrt\malloc, msvcrt\free
  • LocalAlloc, LocalFree
  • CoTaskMemAlloc, CoTaskMemFree (but note it really isn't necessary to use the COM allocator to create COM objects)
  • v1: ObjSetCapacity and ObjGetAddress (use ObjAddRef and ObjRelease to manage lifetime)
    v2: Buffer (use ObjAddRef/ObjPtrAddRef and ObjRelease to manage lifetime)
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to create IEnumerable<Int64> for DllCall?

30 Nov 2021, 03:04

Thank You for clarification.
Will be corrected the next code?
I see that there is no memory leaking.
Also as I see from Your example SetWBClientSite(), You dont use ObjAddRef/ObjRelease.

Code: Select all

setbatchlines -1
CreateClass("Windows.AI.MachineLearning.TensorFloat", ITensorFloatStatics := "{DBCD395B-3BA3-452F-B10D-3C135E573FA9}", TensorFloatStatics)
global bufs := {}, shapeClone
Iterable := IIterable_new()
global Iterator := IIterator_new()
shape := [1, 1000, 1, 1]
loop 1000000
{
   shapeClone := shape.clone()
   hr := DllCall(NumGet(NumGet(TensorFloatStatics+0)+7*A_PtrSize), "ptr", TensorFloatStatics, "ptr", Iterable, "ptr*", TensorFloat)   ; TensorFloatStatics.Create
   ObjRelease(TensorFloat)
   if (hr != 0)
      msgbox error
}
msgbox done



IIterable_new()
{
   VTBL := ["QueryInterface", "AddRef", "Release", "GetIids", "GetRuntimeClassName", "GetTrustLevel", "First"]
   bufs.SetCapacity(1, (VTBL.maxindex()+1)*A_PtrSize)
   buf := bufs.GetAddress(1)
   for k, v in VTBL
   {
      if (k = 7)
         NumPut(RegisterCallback("IIterable_" v), buf + k*A_PtrSize)
   }
   NumPut(buf + A_PtrSize, buf + 0)
   return buf
}

IIterable_First(this, first)
{
   NumPut(Iterator, first+0, 0, "ptr")
   return 
}

IIterator_new()
{
   VTBL := ["QueryInterface", "AddRef", "Release", "GetIids", "GetRuntimeClassName", "GetTrustLevel", "get_Current", "get_HasCurrent", "MoveNext"]
   bufs.SetCapacity(2, (VTBL.maxindex()+1)*A_PtrSize)
   buf := bufs.GetAddress(2)
   for k, v in VTBL
   {
      if k in 2,3,7,8,9
         NumPut(RegisterCallback("IIterator_" v), buf + k*A_PtrSize)
   }
   NumPut(buf + A_PtrSize, buf + 0)
   return buf
}

IIterator_AddRef(this)
{
   return
}

IIterator_Release(this)
{
   return
}

IIterator_get_Current(this, current)
{
   numput(shapeClone.RemoveAt(1), current+0, 0, "int64")
   return
}

IIterator_get_HasCurrent(this, hasCurrent)
{
   numput(1, hasCurrent+0, 0, "char")
   return
}

IIterator_MoveNext(this, hasCurrent)
{
   if (shapeClone.Length() = 0)
      numput(0, hasCurrent+0, 0, "char")
   else
      numput(1, hasCurrent+0, 0, "char")
   return
}

CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
   DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to create IEnumerable<Int64> for DllCall?

30 Nov 2021, 03:50

malcev wrote:Also as I see from Your example SetWBClientSite(), You dont use ObjAddRef/ObjRelease.
The client site is required for the duration of the program, so I did not implement reference counting at all. I could call ObjAddRef or ObjRelease, and it would do nothing.
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to create IEnumerable<Int64> for DllCall?

30 Nov 2021, 04:33

Your approach is not scalable - it will only work for a single global IIterable and a single global IIterator. If that's all you need, it's fine; but it is not suitable for a general implementation of IIterable/IIterator.

The advantage of SetCapacity/GetAddress over VarSetCapacity is that you can create an object and then refer to it by pointer, pass it to native code or store it in a struct, and you are not required to retain a reference in the original unique variable. If you are only referencing a single object via a global variable and never freeing either of the two buffers you allocate, you may as well use two global variables and no objects. Even better, use two static variables.

Your interface methods do nothing more than call NumPut. There are no memory allocations or deallocations, so of course it isn't going to leak any memory, no matter how many times you call it.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Billykid, Google [Bot] and 186 guests