Windows and Availability API

I've run into an issue where the representation of code in WinSDK is mangled when using Windows SDK later than 10.0.19041. I'm not sure exactly which version it starts at yet.

I've been using the official getting started guide which just says to use 10.0.19041 explicitly, but I started using @compnerd's fantastic GitHub action for CI, and that uses a newer sdk and I got build errors. So I installed a newer sdk on my dev box and sure enough, it produces the same mangled APIs.

Of the modules I use, the errors only come from WinSDK.DirectX.Direct3D12. And every single one of them is func GetDesc() but for different classes and returning different values.

So there's a bug, and I'm trying to work around it like this:

if #available(Windows 10.0.22000, *) {
    var desc = WinSDK.D3D12_COMMAND_QUEUE_DESC()
    _ = pThis.pointee.lpVtbl.pointee.GetDesc(pThis, &desc)
    return D3DCommandQueueDescription(desc)
}else{
    let v = pThis.pointee.lpVtbl.pointee.GetDesc(pThis)
    return D3DCommandQueueDescription(v)
}

But no matter what version, even just #available(Windows 10, *), it never enters first branch and the second branch is wrong and throws build errors.

I couldn't find any examples of if #available being used anywhere for Windows, just deprecated and unavailable.

Does if #available work on Windows?
Or is there another way I could work around this?
Or am I going to have to restrict my users to a specific windows sdk?

1 Like

And the build errors are here for reference.

I don’t know much about Swift on Windows, but I can say if #available is a run-time check, not a compile-time check, so it’s not something you can use to guard against certain SDK versions. Unfortunately you’ll have to wait for compnerd or someone else more knowledgeable for what the correct solution is to your problem.

1 Like

Yeah, I didn't even think of that. This isn't a decorated API change so it was never going to compile.

The only way I can think of is to create a C target wrapping the API manually. But maintaining that would turn into a nightmare so I guess I'll just require sdk 10.0.19041 for now.

If I remembered correctly, even on macOS there’s basically no direct way to check the SDK version. We often use workarounds like checking if canImport a library specific to newer SDKs. This does not work well on Windows.

I didn’t try that before, but on Windows similar results might be achieved by checking version-related SDK macros in sdkddkver.h, eg. WDK_NTDDI_VERSION >= 0x0A00000B or defined(NTDDI_WIN10_CO). Or, if this has runtime requirements, check NTDDI_VERSION >= 0x0A00000B instead. Note that you may need to do this in C and pass it as a compilation flag to Swift.

OS/SDK/MSVC version availability is another unsolved puzzle on Windows (and it’s rather complicated). The #available() check seems to be unimplemented, and it could possibly be achieved but we need more investigation, discussion on design and then implement and roll it out. It doesn’t help with the problem though.

#available unfortunately is not wired up with the version information yet. That is something that I would like to get working at some point, but the focus on the other higher priority items currently has left this unimplemented. I believe that I had filed an issue to track this on the GH issues. As @jrose mentioned, that check is a runtime check, and you should be able to emulate that with a call to get the OS version.

I considered this solution, and realized I could just wrap the broken API myself. I had to do something similar for a couple variadic C functions on Linux.

That's good to know. I just noticed @available(unavailable) was used in a few places and assumed it worked.

@compnerd
Should I file a issue for the mangled API?

// Windows SDK 10.0.19041 <- correct
func GetDesc(pThis) ->  StructType?
// Windows SDK later then 10.0.19041 <- mangled
func GetDesc(pThis, UnsafeMutablePointer<StructType>?) -> UnsafeMutablePointer<StructType>?

This shows up on several vtbls in WinSDK.DirectX.Direct3D12. All of them are a GetDesc() func.
Swift 5.8 Release. I haven't tried any snapshots yet.

I guess the question that comes to mind is, what do you mean by "mangled" here?

The importing here is not Windows specific, it is a generic shared code path and would impact other platforms.

func GetDesc(pThis, UnsafeMutablePointer<StructType>?) -> UnsafeMutablePointer<StructType>?

I think that this might be related to C++ Interop?

D3D12_RESOURCE_DESC ID3D12Resource::GetDesc();

This is likely going to be a stret function, so, this is from an ABI perspective:

void ID3D12Resource_GetDesc(ID3D12Resource *, D3D12_RESOURCE_DESC *);

That signature does match the signature that you are seeing.

CC: @Alex_L @egor.zhdan @zoecarver

By mangled I just mean the newer SDK signature seems incorrectly interpreted.

Interesting. I haven't enabled C++ interop.
And it's only functions named GetDesc that are doing it, so it does seem like a bug.

It is because they are the only ones that are stret functions IIRC. It has been a bit since I looked at the API, but I don't remember any other stret functions in the API. I think its the exact opposite - it is a bug fix rather than a bug.

There was a second case that this occurs in IIRC, which is the ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart function.
swift-com/ID3D12DescriptorHeap.swift at 8b2a7b50d9bcec07f0ae84d2be7f9909e73223d3 · compnerd/swift-com (github.com)

1 Like