Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MetadataTypedef attribute does not work #1575

Closed
kennykerr opened this issue May 11, 2023 · 17 comments
Closed

MetadataTypedef attribute does not work #1575

kennykerr opened this issue May 11, 2023 · 17 comments
Assignees

Comments

@kennykerr
Copy link
Contributor

kennykerr commented May 11, 2023

I took the MetadataTypedef attribute for a spin but it's useless as the metadata doesn't actually tell me what it replaced. I don't know for example that CreateCompatibleDC actually returns an HDC. Ideally the metadata would use the native types (e.g. HDC in this case) and use attributes to indicate alternatives that may optionally be used but as it stands, we can tell that something's changed but not what it changed from.

Originally posted by @kennykerr in #561 (comment)

@kennykerr
Copy link
Contributor Author

Has this been addressed? Seems like 3608e3f just removed that one metadata type but didn't address the original issue where there are various such types where the native type is not preserved.

@mikebattista
Copy link
Collaborator

The native types all seem to be preserved unless you're referring to scenarios where the MetadataTypedef wraps a NativeTypedef like HANDLE. Today, the NativeTypedef would be lost. Is that what you're referring to?

@kennykerr
Copy link
Contributor Author

Yes.

@mikebattista
Copy link
Collaborator

Ok. I'll reactivate and see if we can set the value of the MetadataTypedef to a NativeTypedef.

@mikebattista mikebattista reopened this May 18, 2023
@mikebattista mikebattista self-assigned this May 18, 2023
@mikebattista
Copy link
Collaborator

We could update the Value field like below, but if you expose just HANDLE, that might suggest that you should close the handle with CloseHandle which would be incorrect. I know you ignore RAIIFree right now @kennykerr, but how would you imagine handling this if you did?

[RAIIFree("DeleteTimerQueueEx")]
[InvalidHandleValue(0L)]
[MetadataTypedef]
public struct TimerQueueHandle
{
	public HANDLE Value;
}

[RAIIFree("CloseHandle")]
[InvalidHandleValue(-1L)]
[InvalidHandleValue(0L)]
[NativeTypedef]
public struct HANDLE
{
	public IntPtr Value;
}

@kennykerr
Copy link
Contributor Author

I'd associate the free function with the function rather than the return type.

@mikebattista
Copy link
Collaborator

I was thinking the same. I'll try that.

@riverar
Copy link
Collaborator

riverar commented May 18, 2023

Alternatively, we could establish a rule that states RAIIFree shall not appear on NativeTypedef types. APIs that accept or return HANDLE values that can be closed would need to use a specialized ClosableHandle metadata typedef instead.

@riverar
Copy link
Collaborator

riverar commented May 18, 2023

Example:

// Theoretical APIs
public unsafe HANDLE GetASharedHandle();
public unsafe ClosableHandle GetAFreshHandle();
public unsafe TimerQueueHandle GetATimerQueueHandle();

// Untouched "raw" typedef
[NativeTypedef]
public struct HANDLE
{
	public IntPtr Value;
}

// A specialization with a free function
[RAIIFree("CloseHandle")]
[InvalidHandleValue(-1L)]
[InvalidHandleValue(0L)]
[MetadataTypedef]
public struct ClosableHandle
{
	public HANDLE Value;
}

// A specialization with an overriding free function
[RAIIFree("DeleteTimerQueueEx")]
[InvalidHandleValue(0L)]
[MetadataTypedef]
public struct TimerQueueHandle
{
	public HANDLE Value;
}

@kennykerr
Copy link
Contributor Author

Just my 2 cents, but I think that's too prescriptive. It's not the metadata's job to invent types and associations that don't exist in the API. CreateFileA returns a HANDLE type that should be closed with CloseHandle and returns INVALID_HANDLE_VALUE on failure. All of that is information that describes CreateFileA and thus can and should be associated with the CreateFileA function in metadata, rather than other real or invented types. 😊

@riverar
Copy link
Collaborator

riverar commented May 18, 2023

So it sounds like Kenny's proposal is to delete MetadataTypedefs and produce the following instead:

[return: RAIIFree("CloseHandle")]
public unsafe static extern HANDLE CreateFileA(...);

public unsafe static extern BOOL CreateTimerQueueTimer(
  [Out]
  [RAIIFree("DeleteTimerQueueEx")]
  [InvalidHandleValue(0L)]
  HANDLE* phNewTimer,

  [Optional]
  [In]
  [InvalidHandleValue(0L)]
  HANDLE TimerQueue,

   ...
);

@kennykerr
Copy link
Contributor Author

I'd put the invalid value and the free function on the same attribute since they're inextricably linked.

@mikebattista
Copy link
Collaborator

I was thinking the HANDLE NativeTypedef has CloseHandle and InvalidHandleValues like it does today. For scenarios where HANDLEs must be closed with a more specific function, that is attributed on the HANDLE return value or out parameter like you showed above.

@kennykerr
Copy link
Contributor Author

It seems a little convoluted to have to support two different approaches to retrieve the same information.

@mikebattista
Copy link
Collaborator

It seems redundant to decorate every HANDLE reference in the SDK with mostly the same attributes. I'd rather only decorate the deviations from the norm.

@riverar
Copy link
Collaborator

riverar commented May 22, 2023

I was thinking the HANDLE NativeTypedef has CloseHandle and InvalidHandleValues like it does today. For scenarios where HANDLEs must be closed with a more specific function, that is attributed on the HANDLE return value or out parameter like you showed above.

So you're proposing something like this:

[RAIIFree("CloseHandle")]
[InvalidHandleValue(-1L)]
[InvalidHandleValue(0L)]
[NativeTypedef]
public struct HANDLE
{
	public IntPtr Value;
}

public unsafe static extern HANDLE CreateFileA(...);

public unsafe static extern BOOL CreateTimerQueueTimer(
  [Out]
  [RAIIFree("DeleteTimerQueueEx")]
  HANDLE* phNewTimer,
  [Optional]
  [In]
  HANDLE TimerQueue,
   ...
);

Hm. I see what you're trying to do there, but am wondering if some APIs may return a HANDLE that should not be freed at all. If one is found, we'll need another form of RAIIFree to signal to projection authors "do not free this handle". At which point, we have to ask ourselves if maybe the RAIIFree on the HANDLE itself is the right design.

@mikebattista
Copy link
Collaborator

but am wondering if some APIs may return a HANDLE that should not be freed at all. If one is found, we'll need another form of RAIIFree to signal to projection authors "do not free this handle". At which point, we have to ask ourselves if maybe the RAIIFree on the HANDLE itself is the right design.

We have [DoNotRelease] for scenarios like that. GetProcessHeap(s) have that attribute today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants