-
Notifications
You must be signed in to change notification settings - Fork 539
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
"Mismatched types" generated for COM methods that take or return array of COM interface pointers as "pointer-to-first plus separate array-length" #3493
Comments
Can you share the .winmd file that you're using to generate the bindings? I can have a quick look at that to see if there's anything amiss. |
it's not really a .txt, but github doesn't like the .winmd extension |
Here's the metadata representation for the first two build errors I encountered: HRESULT IGetIntersectionEdges([In] IBody ToolBodyIn, [Out][RetVal] IEdge** EdgeListOut);
HRESULT IRemoveFacesFromSheet([In] int NumOfFaces, [In] IFace** FacesToRemove); Both appear to be different array formulations that aren't handled too well because they're not consistent with existing array parameters in the Win32 metadata. You could compare this with existing APIs to see what attributes or transformations might produce working bindings. Neither indicate that they are arrays and so there's some confusion as to why these seemingly non-array parameters include the purported level of indirection. I'm working on a next generation of parsing and code generation that should be a lot more resilient to arbitrary metadata like this but it will be a little while until that's ready to test. |
Makes sense; that's likely why they were deprecated, if I had to guess. Other than that, am I roughly correct about how to consume "arrays" of COM pointers like this, and what my responsibilities as the COM "caller" are? |
It's not clear from this metadata. Keep in mind that the metadata implies an interface pointer when HRESULT IGetIntersectionEdges(IBody*, IEdge***); I have no idea whether that's correct and then what the semantics are for ownership as that is not conventional COM parameter passing. |
Everything looks generally correct here in terms of metadata. This API returns an array of pointers, per the docs. And the length is retrieved out of band. It might help to emit metadata with I do want to clarify that the word "deprecated" is getting used here, but the documentation indicates these APIs are obsolete which typically means you shouldn't be using them at all and they will likely not work/be missing. So maybe excluding them from your crate is the better solution here? 😅 (Or maybe not, the suggested replacement API doesn't look much better.) Edit: Also of interesting note is that the SolidWorks docs suggest using IDispatch methods when dealing with anything containing arrays. |
(Sorry, am fighting my keyboard today and triggered the wrong button.) |
Yes, I agree that going with the The "deprecated" vs "obsolete" question is a tricky one. The API isn't versioned, but SolidWorks-the-application is. Yearly releases can add new methods/interfaces, but the old ones still exist and should be supported (YMMV going further into the past, but this is the "aspirational" goal) so that old clients continue to work and new clients can target old releases of SolidWorks. Because of licensing/subscription, you(r users) may be "stuck" on an old year/service-pack. "Deprecated" generally implies "move to the new thing, the old will be removed at some point", but the "at some point" in this case is fuzzy and kind of delegated to the consuming user. It's a difficult design question of how to surface this information in the more-idiomatic |
But in this case, "provide the entire interface" needs to be qualified with "that can be made to compile". The "--filter" switch in Binding Postprocessingwindows-bindgen turns enum declarations into a newtype wrapper and module-level pub const s. I've read elsewhere here that this is the "simplest obvious correct choice", because of the C-based semantics of MIDL enums (non-exhaustiveness, and such), and I agree.
What I've done, though, is take the module-level constants and turn them into associated constants of their type, such that pub struct swObjectEquality(pub i32);
pub const swObjectNotSame: swObjectEquality = swObjectEquality(0i32);
pub const swObjectSame: swObjectEquality = swObjectEquality(1i32);
pub const swObjectUnsupported: swObjectEquality = swObjectEquality(2i32); becomes pub struct swObjectEquality(pub i32);
impl swObjectEquality {
pub const swObjectNotSame: Self = swObjectEquality(0i32);
pub const swObjectSame: Self = swObjectEquality(1i32);
pub const swObjectUnsupported: Self = swObjectEquality(2i32);
}
pub const swObjectNotSame: swObjectEquality = <swObjectEquality>::swObjectNotSame;
pub const swObjectSame: swObjectEquality = <swObjectEquality>::swObjectSame;
pub const swObjectUnsupported: swObjectEquality = <swObjectEquality>::swObjectUnsupported; This way, you can do the C/C++-style "brought in by a header somewhere" existing behavior, but also a more C#-style "namespaced to type" access. |
What's the plan here? Metadata fix or bindgen hardening? |
Summary
TL;DR, generated bindings that used to compile now fail to compile with "mismatched types". Mismatch is between the type expected by the interface wrapper and what the
map
over the vtable invocation result actually returns.Using the current master branch instead of the latest crates.io release results in the same behavior.
I can manually comment out the offending items from the generated bindings (and make notes of which they are, why it's necessary, etc.), so this is not currently a high-priority blocking item on my end. The priority comes from long-term, forward-looking correctness, as well as potentially revealing a blind-spot in the
windows-rs
COM support.This is kind of a tricky issue, because there are so many unknowns on my end. I'm not certain what the "real problem" is, because I'm not sure what the "correct behavior" actually looks like, even in a non-Rust, non-
windows-rs
context. So, I'm just going to start at the beginning.SolidWorks COM API wrapper
I am working from the type libraries redistributed with a SolidWorks installation. The API is so old that not only do I not have .winmd to work from, I have to "decompile" the .tlb files into .idl files that the
WinmdGenerator
SDK even knows what to do with, kind of. This has (seemed to have) worked so far, after a lot of finagling.I mention that here just to underscore that, because of the multi-step translation process, I actually don't know if this is even a faithful translation of the original SolidWorks interface definition, even without considering whether the original SolidWorks devs wrote that definition correctly.
Problem Bindings
All the problematic methods are
Some of the successor methods are using
VARIANT
arrays instead (SolidWorks is a heavy user ofIDispatch
"OLE Automation"), but some of them are still pointer-and-length based (just with one less pointer indirection, for some reason, which seems to work correctly).Return
Result<*mut Option<IFoo>>
vsResult<IFoo>
IBody.IGetIntersectionEdges
is deprecated in favor ofIBody2.IGetIntersectionEdges
(which is itself deprecated in favor ofIBody2.GetIntersectionEdges
)The
IBody2
interface compiles successfully in its entirety.The only difference in the generated wrappers between v0.58 and v0.59 is the
unsafe
block enclosing the method body.Parameter of
*const *const Option<IFoo>
vs*const Option<IFoo>
Our lovely friend,
IBody
, is at it againAs before,
IBody.IRemoveFacesFromSheet
is deprecated in favor ofIBody2.IRemoveFacesFromSheet
. However,IBody2.RemoveFacesFromSheet
exists, but is not specified as supersedingIBody2.IRemoveFacesFromSheet
.Again, the only difference in the generated wrappers between v0.58 and v0.59 is the enclosing
unsafe
block.The other problematic methods are just variations on this common theme.
Questions
Is this a simple matter of "getting confused by double indirection"?
As far as I can tell,
windows-bindgen
appears to be going "well, it's a pointer type, and pointer types are copy-able, so do the copy-able thing, which is the identitymap
". I think. Maybe. It's very complicated, and I'm very new to that codebase.How is this "supposed" to work?
If I replace
.map(|| result__)
with.map(|| core::mem::transmute(result__))
, it compiles. Given thattransmute
is black freakin magic "just_ pretend that the bits mean something different", I personally have no idea if this means it's "correct". It makes sense in my head, where*mut c_void
turns into*mut IFoo
("castvoid
to the type you're actually pointing to"), which turns intoOption<IFoo>
("nullptr
is the niche forOption
discriminant"), without any binary/layout changes along the way.And then, if that's valid, shouldn't it be a simple matter of (if
Some
) usingInterface::as_raw
/Interface::into_raw
,<pointer>::offset
, andInterface::from_raw
to grab the following elements of the array? Those interface pointers have already been "given" to me by returning from the method, and it's my responsibility torelease
them (by wrapping them in a type that will do that ondrop
), right? Something like,Is that right? I've been programming in the lower-level Microsoft realm for just long enough to know that "if you've done it 'the wrong way', it still might 'work', and if you've done it 'the right way', it still might not work."
Should I be seeing some explicit
Ref
/OutRef
?Because even though I haven't fully grok'd how they work, in a post-#3386 world, not seeing them is slightly concerning to me. :)
Disclaimer
I know that "supporting 3rd-party metadata" is not a top priority for this project. The
solidworks-sys
crate I'm working on is not public (yet) and quite chonky (~650k lines of bindings for the mainsldworks
namespace alone). My apologies if this is out of scope or if providing the relevant code-snippets inline is less helpful than a live repo.The
windows-rs
project is seriously magical. Thank you guys for the hard work you've put into it. I feel like I've learned a ton so far, and I'd love to see it go as far as it can go. Maybe help out if/where I can. Cheers!Crate manifest
Crate code
The text was updated successfully, but these errors were encountered: