-
Notifications
You must be signed in to change notification settings - Fork 116
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
CoClassAttribute for Interfaces #271
Comments
@AArnott / @sotteson1 / @BenJKuhn what are your thoughts on this? I just wrote a C#/Win32 COM sample for reference as to how that's projected. @AArnott can speak to how the projection leveraged the metadata to support this or if more metadata would be helpful, but the translation from COM code to C# was very straightforward once you understand some simple C# translations. The original COM code was from here. |
Right. I think the thing about that sample is that you're just using the As a very practical example, in my existing projection, I have a class like this, which I can only generate automatically if I know there's a relationship between these two objects (substitute NetworkListManager for SpellCheckerFactory, obviously, but you get the idea). class NetworkListManager extends INetworkListManager {
NetworkListManager(Pointer<COMObject> ptr) : super(ptr);
factory NetworkListManager.createInstance() {
final ptr = calloc<COMObject>();
final clsid = calloc<GUID>()..ref.setGUID(CLSID_NetworkListManager);
final iid = calloc<GUID>()..ref.setGUID(IID_INetworkListManager);
try {
final hr = CoCreateInstance(clsid, nullptr, CLSCTX_ALL, iid, ptr.cast());
if (FAILED(hr)) throw WindowsException(hr);
return NetworkListManager(ptr);
} finally {
calloc.free(clsid);
calloc.free(iid);
}
}
} |
Yes, I think it would be helpful to have metadata more akin to WinRT where there is a "class" that models the WinRT, or in this case COM, class that provides both the class identity and the default interface. That's why WinRT consumers don't have to call |
Got it and makes sense. I know @AArnott had some plans/aspirations to make the COM projections real interfaces like what you showed and sounds like this would help with that. |
I don't think of there being an API-level relationship between At the moment then, the docs tell you the exact names of each (the class ID to activate with, and the interface to interact with it after activation), and the metadata remains true to the interface name so that it matches the docs and headers. But the metadata does not currently reproduce the It is a deviation from the docs and header files, to be sure. And for some languages, the projections may want to recreate the CLSID_Foo constants. In C# it's not clear to me which way is better: do it the .NET way, or redefine the constants that people are looking for. Maybe both? As for enabling a |
Languages supporting WinRT including C#, C++, and Rust already do this very thing of stitching classes and their interfaces together from metadata. |
No, the scraper is not doing that. For NetworkListManager, here's what it's scraping: EXTERN_C const CLSID CLSID_NetworkListManager;
class DECLSPEC_UUID("DCB00C01-570F-4A9B-8D69-199FDBA5723B")
NetworkListManager; Notice there is no CLSID_NetworkListManager constant to even scrape. It's an external constant and the parser doesn't see its value. But the parser does see an empty class with a GUID, so it emits that. |
What I do need to add is a way to scrape these: DEFINE_GUID(CLSID_D2D1LookupTable3D, 0x349E0EDA, 0x0088, 0x4A79, 0x9C, 0xA3, 0xC7, 0xE3, 0x00, 0x20, 0x20, 0x20); That guid acts like a constant and is used like this: // Create a 3D LUT effect to render our LUT.
CComPtr<ID2D1Effect> sp3dLutEffect;
IFR(pEffectContext->CreateEffect(CLSID_D2D1LookupTable3D, &sp3dLutEffect)); So putting that on a class would be weird. It does seem like it needs to a be field that's on the Direct2D.Api class. |
Yes those aren't COM classes at all. They're just IDs. |
@kennykerr Can you elaborate on this with an example? Does the projection somehow know the full set of interfaces to implement on the class? |
Yes, WinRT classes include metadata that indicates every interface that is (publicly) implemented by the class. Here for example is |
Just to elaborate, projections take the members of these interfaces that a given class implements and makes them available directly on the class as if they were members of the class directly. This is done for all interfaces with the |
The problem is that the Win32 headers don't give us this information. Like Andrew said, there is nothing that ties a class (represented by a CLSID) in COM to the interfaces it implements. |
This is again perhaps where there's a design principle missing between "constitutional originalist" (stick to the headers, no matter what limitations it imposes on the resultant interface), and "constitutional activist" (evolve to create a better outcome than the original). |
Absolutely, it would be nice to surface the information. But there are lots of these in the headers and figuring out what to emit for them all in a non-trivial task. We are trying to backfill information like this that we don't get from the headers, but we have to prioritize what things we do because we have very limited resources (so far just me!). |
Totally :) and thanks for your work. It's definitely a step forward. Of course, if it's not done at a Win32 level, then it falls to me to do it at the Dart language projection level. So it would be more efficient to try and solve this once and for all, rather than doing it for every language individually. I wonder if there's a heuristic here though that makes this less scary than it sounds? If there's one of these structs (a |
Sure, that's one strategy. It would take some research to see how well that works. Like I said, it's a matter of prioritization. If we determine that's at the top of the things to tackle, I'll do it. But COM developers have never had that information available to them before, so if someone is porting some Win32 code from C++ to another language, I don't think the information is strictly necessary. In order to port code from C++ to dart, you would need to give the user the ability to create the object using the class id, and then let them QI (in C# it would be a cast) for the interface. They would also need to be able to QI (cast) to a different interface besides INetworkListManager, like INetworkListManager2. That doesn't exist in this example, but that's a common pattern in COM programming to later support more interfaces as functionality is added in the OS. |
By the way, a bug I reported on ILSpy was just fixed so you can use the latest CI build of ILSpy to view WinRT metadata now: |
Nice!! I was going to investigate that myself and see if I could fix it. I'm glad to see they fixed it for us. |
That would solve the 99% case when that's the only interface implemented by that object. But if there are others, then we've maybe done more harm than good by declaring a class that does only a partial job, rather than just create an IUnknown object and let the app developer cast/QI between interfaces that they know are or may be on that object. |
Just for the record, because it seems to be missing from the discussion, the relation between CLSID and IID (for non-WinRT COM) is specified in the IDL files in the Windows SDK. This information does not end up in the MIDL-compiled C/C++ headers deployed in the SDK, but they do end up in the TLB files. Since for a long time the COM projection of the Desktop Framework was based on referencing TLB files they also respected the relationship between CoClass and interfaces. (Personally I never found that link to be important, it helps a bit with discoverability and sometimes write a few less typecasts, but thats it.) I guess it'll be pretty hard to add to this automatically to metadata without including an IDL parse step, considering the current parsing is done from the C/C++ headers as far as I understand. Of course, in any case, all relationships between CoClass and interfaces are incomplete by design, its common to implement semi-private interfaces which need to be explicitly queried for. |
That all sounds very relevant and interesting. Thank you, @weltkante. |
I still can't find CLSID in rust, when i want to create instance of INetworkListManager. It is hard to find relation between clsid and it's corresponding interface. |
Yes, thank you @riverar . But I have a litter doubt that why does not provide CLSID in INetworkListManager, I think CLSID is a part of INetworkListManager. |
Long discussion but my read is that there's no reliable proposal here and what we have is consistent with the Win32 programming model. The other issue is with CLSID constants not being prefixed with CLSID. See #271 (comment). I believe that issue is tracked by #994. Should I close this one out? |
That was my initial read. But I read something different from @weltkante, which suggests there is metadata here that we don't parse:
Here's an example of this, showing the mapping between // CLSID_FileOpenDialog
[ uuid(DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7) ] coclass FileOpenDialog { interface IFileOpenDialog; }
[
uuid(d57c7288-d4ad-4768-be02-9d969532d960),
object,
pointer_default(unique)
]
interface IFileOpenDialog : IFileDialog
{
HRESULT GetResults([out] IShellItemArray **ppenum);
HRESULT GetSelectedItems([out] IShellItemArray **ppsai);
} |
He also said this so that approach doesn't sound complete. There's a lot of other context here as well against this approach. |
I guess I'm not clear why we wouldn't want to add this. Is there more context that isn't captured in this issue? We have metadata in the header files that we can capture that would be of use to projection authors, by identifying known mappings between interfaces and classes. The header files are deterministic and the information is useful. |
Given that interfaces may be used across multiple coclasses, annotating interfaces sounds messy. But annotating the classes sound OK to me. There's two distinct work items here:
Challenges that come to mind (please add more):
|
TLBs are disabled and have issues in some cases. See #1428. |
If you want to pursue this, I'd take a look at WinRT metadata which has already dealt with this design. |
Maybe this is my poor understanding of COM, but I think we need some way to map an IID onto a corresponding class.
For example, take the Network List Manager API:
https://docs.microsoft.com/en-us/windows/win32/nla/about-the-network-list-manager-api
To create an
INetworkListManager
, I need to callCoCreateInstance
and create theCLSID_NetworkListManager
COM object, which in turn implementsINetworkListManager
.From the metadata, I don't think I have a systematic way to identify the matching COM object, do I?
ILSpy just shows:
which gives me the CLSID, but doesn't tell me what it implements (nor vice versa).
Other attempts to wrap this interface seem to include an attribute that is I think derived from tlbimp. For example:
https://github.com/dahall/Vanara/blob/1fb8a2dc8a116995831730a7d433a07f940cc5ef/PInvoke/NetListMgr/NetListMgr.cs#L629
Is there missing metadata? Or is there another way to derive this relationship?
The text was updated successfully, but these errors were encountered: