NtfsHandler CInStream cannot query for interface
A free file archiver for extremely high compression
Brought to you by:
ipavlov
The COM definition for this class contains a bad implementation that only can be queried for IInStream
and cannot be QueryInterface
'd for ISequentialInStream
:
This code in NtfsHandler.cpp:
Z7_CLASS_IMP_COM_1(
CInStream
, IInStream
)
Z7_IFACE_COM7_IMP(ISequentialInStream)
Should instead be this code:
Z7_CLASS_IMP_IInStream(
CInStream
)
As a matter of fact, just a quick scan over the code, and I see similar implementation errors in XzHandler, DmgHandler, and MultiStream
In spite of ::GetStream()
returning an ISequentialInStream *
already, .NET (and possibly other languages) will execute a QueryInterface
on the IUnknown
anyway, and will be unable to get the interface back.
These are all the implementations I found that do not allow QueryInterface on ISequentialInStream:
InStreamWithCRC.h - CInStreamWithCRC
MultiStream.h - CMultiStream
DmgHandler.cpp - CInStream
NtfsHandler.cpp - CInStream
XzHandler.cpp - CInStream
ArchiveOpenCallback.cpp - CInFileStreamVol
I'm finding more places where this happens. For example, CHandlerImg in HandlerCont.h implements IInStream but it doesn't support QueryInterface for ISequentialInStream
Looks like CHandlerImg is the last one I could find. Fortunately the fixes are super simple, like in HandlerCont.h:
From this:
To this:
In what exact code line it will call
QueryInterface
?Are you asking me how .NET's source code works when it interops with COM? That I'm not sure. All I know is that my code looks like this in .NET:
When I call
GetStream
in .NET on certain Handlers like the NTFS handler, .NET throws an error saying it can't marshal the interface. When I started digging through the 7z source code to find out why, I discovered that 7z'sGetStream
implementation for NtfsHandler didn't support callingQueryInterface
forISequentialInStream
directly. Once I changed the 7z source code, then .NET started working.I realize that 7z is already returning an ISequentialInStream, and it doesn't make sense that .NET would query for an interface it already has a reference to, but perhaps internally .NET is just treating everything as
IUnknown
and queries for the interface regardless of what you return.Digging through the .NET source code, what I suspected is the case. .NET constructs a generic method proxy for
GetStream()
which returns back avoid *
which it then passes to ConvertToManagedThe de-compiled auto-generated code in .NET looks like this:
It's a bit of a tangled mess, but in short, .NET compiles a generic method for
GetStream
that changes the output fromISequentialInStream **
tovoid **
and then casts that void * to a managed interface usingQueryInterface
.This is all out of my control.
Last edit: Robert Simpson 2024-03-28
Ok.
I'll fix that code.