saltosystems / winrt-go

MIT License
36 stars 14 forks source link

Initializing IEnumberable<T> that implements Iterable<T> #100

Open hu13 opened 2 weeks ago

hu13 commented 2 weeks ago

hi there.

i am trying to use this api https://learn.microsoft.com/en-us/uwp/api/windows.storage.provider.storageprovideritemproperties.setasync?view=winrt-26100#windows-storage-provider-storageprovideritemproperties-setasync(windows-storage-istorageitem-windows-foundation-collections-iiterable((windows-storage-provider-storageprovideritemproperty)))

public static IAsyncAction SetAsync(IStorageItem item, IEnumerable<StorageProviderItemProperty> itemProperties);

and it requires initializing a list of StorageProviderItemProperty that implements Iterable. I looked at the interfaces that go generate came up for

//go:generate go run github.com/saltosystems/winrt-go/cmd/winrt-go-gen -debug -class Windows.Foundation.Collections.IIterable`1
//go:generate go run github.com/saltosystems/winrt-go/cmd/winrt-go-gen -debug -class Windows.Foundation.Collections.IIterator`1

but i can't seem to see a pattern on how to generate a Iterable<StorageProviderItemProperty> from that. I am wondering if you have any experience interacting with Iterable interface from winrt-go before and can provide some insight. Thanks.

apologize if this is a bit off topics.

jagobagascon commented 2 weeks ago

We've worked with buffers and vectors, but always as readers, we never had to deal with creating an iterable.

And it seems that there is no specific class in Windows.Foundation.Collections that implements the IIterable interface. So the only solution I can think of is to provide your own implementation.

I will try to look into it, but I'll have to find some free time to do so because I think this is going to be quite complex to do.

In the meantime you can check how the delegates do this by defining their own functions VTable using syscall.NewCallback.

hu13 commented 2 weeks ago

@jagobagascon thanks. i will take a look at delegates.go

jagobagascon commented 1 week ago

I wanted to help a little bit more so I made a quick test that seems to be working. I've uploaded it as a gist: https://gist.github.com/jagobagascon/fd807800ee5c658955a81f0dd1c40bb8

You should be able to run it after generating the iterable and iterator structs.

It's far from perfect and required quite a bit of black magic to make it work:

func moveNext(inst, out unsafe.Pointer) uintptr {
    offset := unsafe.Offsetof(collectionsIterator{}.IIterator)
        // good old C pointer magic tricks
    it := (*collectionsIterator)(unsafe.Pointer(uintptr(inst) - offset))

    it.index++
    return getHasCurrent(inst, out)
}

I hope this helps.

[!NOTE] I don't think we will have time to work on this in the short term, so PRs are welcome 😉

hu13 commented 1 week ago

@jagobagascon i truly appreciate your help. I will test it out asap.

hu13 commented 1 week ago

@jagobagascon i think the gist is missing the iunknown's RegisterInstance() logics. could you share that please?

hu13 commented 1 week ago

i have tried this

var (
    ole32                     = syscall.NewLazyDLL("ole32.dll")
    procCoRegisterClassObject = ole32.NewProc("CoRegisterClassObject")
)

// RegisterInstance registers the COM class object with the system
func RegisterInstance(clsid *ole.GUID, obj interface{}) (unk *ole.IUnknown, err error) {
    ret, _, err := procCoRegisterClassObject.Call(
        uintptr(unsafe.Pointer(clsid)),
        uintptr(unsafe.Pointer(&obj)),
        // uintptr(context),
        // uintptr(flags),
        uintptr(unsafe.Pointer(&unk)),
    )
    if ret != 0 {
        err = ole.NewError(ret)
    }
    return
}
    // Initialize all properties: the malloc may contain garbage
    iid := ole.NewGUID(winrt.ParameterizedInstanceGUID(GUIDIIterable, itemSignature))

    // create type instance
    size := unsafe.Sizeof(*(*arrayIterable)(nil))
    instPtr := kernel32.Malloc(size)
    inst := (*arrayIterable)(instPtr)

    callbacks, err := RegisterInstance(iid, inst)
    if err != nil {
        panic(err)
    }

but it is not quite correct yet. I got

--- FAIL: Test_GetCurrent (0.00s)
panic: The parameter is incorrect. [recovered]
    panic: The parameter is incorrect.
hu13 commented 1 week ago

I got it working now at least with your test of int iterable. i need to use // get the callbacks for the VTable callbacks := delegate.RegisterCallbacks(instPtr, inst) and also implement Invoke() for the Iterator and Iterable struct we registered.

hu13 commented 1 week ago

i ran into invalid memory access issues when i try to set it up for StorageProviderItemProperty. If possible, could you take a look at this gist https://gist.github.com/hu13/4ce6e6f9019ff5c448071998128dac79 that contains what my set up?

jagobagascon commented 1 week ago

You are right, sorry. I refactored the delegates and extracted the code shared among all structs to the iunknown package. That way you won't need to implement Invoke (which is only required by delegates).

I pushed the changes to this branch: https://github.com/saltosystems/winrt-go/tree/feature/slice-iterable-testing-prototype

With them the main.go added to the Gist should work as expected.

hu13 commented 1 week ago

This branch is working for the any type and the number of items returned from getMany is also correct. Did you change any logics from the gist?

jagobagascon commented 1 week ago

I fixed the item assignment using reflection: https://gist.github.com/jagobagascon/fd807800ee5c658955a81f0dd1c40bb8#file-array-go-L356