saltosystems / winrt-go

MIT License
31 stars 13 forks source link

0xc0000005 exception when calling static functions #97

Open jagobagascon opened 1 month ago

jagobagascon commented 1 month ago
          I encountered a similar issue with a very simple test:

https://github.com/balazsgrill/winrt-go/blob/main/storage_test.go

However I doubt that this time it's caused by the GC as it still fails when I disable it

Exception 0xc0000005 0x1 0x50 0x7ffa3fa96d43
PC=0x7ffa3fa96d43

Originally posted by @balazsgrill in https://github.com/saltosystems/winrt-go/issues/94#issuecomment-2294852767

jagobagascon commented 1 month ago

@balazsgrill I did a quick test and it seems that we are not correctly calling static methods.

We are always passing a first NULL parameter:

hr, _, _ := syscall.SyscallN(
    v.VTable().StorageFolderGetFolderFromPathAsync,
    uintptr(0),                    // this is a static func, so there's no this
    uintptr(pathHStr),             // in string
    uintptr(unsafe.Pointer(&out)), // out foundation.IAsyncOperation
)

And even though I can't find anything in the runtime docs related to this (https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system), it looks like the Rust implementation always passes a pointer to the current parent interface.

So I replaced the first 0 (null) parameter with a pointer to the iStorageFolderStatics instance and it now works:

v := (*iStorageFolderStatics)(unsafe.Pointer(inspectable))
hr, _, _ := syscall.SyscallN(
    v.VTable().StorageFolderGetFolderFromPathAsync,
    uintptr(unsafe.Pointer(v)),    // this is a static func, but we are adding iStorageFolderStatics as "this"
    uintptr(pathHStr),             // in string
    uintptr(unsafe.Pointer(&out)), // out foundation.IAsyncOperation
)
jagobagascon commented 1 month ago

Passing NULL or an instance pointer seems to have zero impact on most static methods (Buffer.Create for example works in both cases), but that's not the case for StorageFolder.GetFolderFromPathAsync.

balazsgrill commented 1 month ago

Thank you for investigating this. It took some time for me to figure out how async operations are working but with this change the test runs successfully https://github.com/balazsgrill/winrt-go/commit/7cb59d11d218986ed025821ae17cbbfe865e86a0

balazsgrill commented 1 month ago

it looks like the Rust implementation always passes a pointer to the current parent interface.

I suppose then modifying the template to always generate "this" is the appropriate solution (https://github.com/balazsgrill/winrt-go/commit/ed5621d1dfca9e0d8ab08e1830426c6455669507#diff-15f31657e55dbae4403357fb11617c4118c705bf171961ef5d9ae9635fbc2c14)

jagobagascon commented 2 weeks ago

I suppose then modifying the template to always generate "this" is the appropriate solution

Probably. It still bothers me that most of the code just works without adding it. And I cannot find any mention to it in the docs. So I would like to thoroughly test this with the https://github.com/tinygo-org/bluetooth library before adding any PR.