-
-
Notifications
You must be signed in to change notification settings - Fork 98
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
Returning out
arguments breaks when they must be preallocated
#474
Comments
Please write a short code snippets to reproduce the condition.
|
I quickly put together a file based on your test you provided here: Here I use the workaround described above: As before, a portable device must be connected for the test to work. This test won't modify anything on the device, but it may download personal data to the host computer. (not sure how one can get the code snippets to render here) |
If you change the USERNAME of
comtypes/comtypes/test/test_portabledevice.py Lines 116 to 121 in 967b397
And comtypes/comtypes/test/test_portabledevice.py Lines 122 to 127 in 967b397
|
The MemberSpec for the # in `...\comtypes\gen\_1F001332_1A57_4934_BE31_AFFC99F4EE0A_0_1_0.py`
IPortableDeviceResources._methods_ = [
...
COMMETHOD(
[],
HRESULT,
'GetStream',
(['in'], WSTRING, 'pszObjectID'),
(['in'], POINTER(_tagpropertykey), 'key'),
(['in'], c_ulong, 'dwMode'),
(['in', 'out'], POINTER(c_ulong), 'pdwOptimalBufferSize'),
(['out'], POINTER(POINTER(IStream)), 'ppStream')
),
...
] Then, I have a few questions about some of your code snippets. comtypes/comtypes/test/test_portabledevice.py Lines 106 to 114 in 967b397
|
Both of those are relics from someone else's earlier code that I forgot to remove, my bad. The updated code is here, and the behaviour has not changed. |
You should try to redefine class ISequentialStream(comtypes.IUnknown):
_case_insensitive_ = True
_iid_ = GUID('{0C733A30-2A1C-11CE-ADE5-00AA0044773D}')
_idlflags_ = []
def RemoteRead(self, size: int) -> tuple["Array[c_ubyte]", int]:
pv = (c_ubyte * size)()
pcb_read = pointer(c_ulong(0))
self.__com_RemoteRead(pv, c_ulong(size), pcb_read)
return pv, pcb_read.contents.value # or `list(pv), pcb_read.contents.value`, or `bytes(pv), pcb_read.contents.value`
ISequentialStream._methods_ = [
COMMETHOD(
[],
HRESULT,
'RemoteRead',
(['out'], POINTER(c_ubyte), 'pv'),
(['in'], c_ulong, 'cb'),
(['out'], POINTER(c_ulong), 'pcbRead')
),
COMMETHOD(
[],
HRESULT,
'RemoteWrite',
(['in'], POINTER(c_ubyte), 'pv'),
(['in'], c_ulong, 'cb'),
(['out'], POINTER(c_ulong), 'pcbWritten')
),
]
_LARGE_INTEGER = c_longlong
_ULARGE_INTEGER = c_ulonglong
class tagSTATSTG(Structure):
pass
class IStream(ISequentialStream):
_case_insensitive_ = True
_iid_ = GUID('{0000000C-0000-0000-C000-000000000046}')
_idlflags_ = []
IStream._methods_ = [
COMMETHOD(
[],
HRESULT,
'RemoteSeek',
(['in'], _LARGE_INTEGER, 'dlibMove'),
(['in'], c_ulong, 'dwOrigin'),
(['out'], POINTER(_ULARGE_INTEGER), 'plibNewPosition')
),
COMMETHOD(
[],
HRESULT,
'SetSize',
(['in'], _ULARGE_INTEGER, 'libNewSize')
),
COMMETHOD(
[],
HRESULT,
'RemoteCopyTo',
(['in'], POINTER(IStream), 'pstm'),
(['in'], _ULARGE_INTEGER, 'cb'),
(['out'], POINTER(_ULARGE_INTEGER), 'pcbRead'),
(['out'], POINTER(_ULARGE_INTEGER), 'pcbWritten')
),
COMMETHOD(
[],
HRESULT,
'Commit',
(['in'], c_ulong, 'grfCommitFlags')
),
COMMETHOD([], HRESULT, 'Revert'),
COMMETHOD(
[],
HRESULT,
'LockRegion',
(['in'], _ULARGE_INTEGER, 'libOffset'),
(['in'], _ULARGE_INTEGER, 'cb'),
(['in'], c_ulong, 'dwLockType')
),
COMMETHOD(
[],
HRESULT,
'UnlockRegion',
(['in'], _ULARGE_INTEGER, 'libOffset'),
(['in'], _ULARGE_INTEGER, 'cb'),
(['in'], c_ulong, 'dwLockType')
),
COMMETHOD(
[],
HRESULT,
'Stat',
(['out'], POINTER(tagSTATSTG), 'pstatstg'),
(['in'], c_ulong, 'grfStatFlag')
),
COMMETHOD(
[],
HRESULT,
'Clone',
(['out'], POINTER(POINTER(IStream)), 'ppstm')
),
] Then rewrite as follows; comtypes/comtypes/test/test_portabledevice.py Lines 106 to 112 in 1c8a9bd
optimalTransferSizeBytes, fileStream = resources.GetStream(
objectID,
WPD_RESOURCE_DEFAULT,
STGM_READ,
optimalTransferSizeBytes,
)
fileStream = fileStream.QueryInterface(IStream)
blockSize = optimalTransferSizeBytes.contents.value comtypes/comtypes/test/test_portabledevice.py Lines 122 to 125 in 1c8a9bd
_, data_read = fileStream.RemoteRead(blockSize)
print(f"Read data: {data_read}") This way is WET and may not be very elegant because it redefines the classes. edited: fix return value of |
If we were going to fix these problems permanently(in other words, "fix as no need workarounds defined by users"), we need the agreements with the community. The followings are my shallow thoughts.
|
With regard to the aforementioned, I have done a little experimentation that are adding the module with interfaces statically defined and they are imported instead of being defined dynamically. Lines 1 to 140 in cfb8ba1
comtypes/comtypes/client/_generate.py Lines 249 to 260 in cfb8ba1
However, when it comes to introducing such these into production code (as I have said many times), we must be very careful about backward compatibility. |
I like the idea of statically importing these streams. Edit: I was wrong, see the comment below
Regarding
This works as expected. The code from https://github.com/KasparNagu/PortableDevices uses |
Thanks a lot! After posting the sample code snippet, I still gave it some thought.
To test this Edited: Fixed the part where I meant to |
Hi, I have put together a unit test for Also, my apologies - your suggested code for Your suggestions sound reasonable, though I suspect that other interfaces which implement As far as I am concerned, statically importing |
Your contribution helps us so much. I found the SO that might help you about Your code is a great use case for I am trying to figure out how to take this into the production code so that we can continue to operate well. Please wait a while. Any opinions would be appreciated. |
As you say, the only thing that needs to be statically defined in the scope of this issue is
Is this code okay for you? Also keep in mind, the |
The code looks fine to me, thank you! I can also confirm the correct behaviour of the test (passes with the modification in
In that case, would it make more sense to create the pull request at a later point in time? |
Do you think these changes are reasonable to resolve this issue? Even if these changes are acceptable for the project, I think it is out of scope of the How do you think this should go? |
Hi, The However, with #529 and #534, the criteria for determining whether a COM interface is a known symbol now include not only the name but also the iid. With those change, I think that If you are still interested in this issue, feel free to PR. |
* Implement static import for `ISequentialStream` (#474) * add `Test_KnownSymbols.test_symbols_in_comtypes_objidl` * add `Test_GetModule.test_portabledeviceapi` * split `Test_IStream` * Rename objidl.py to stream.py * Add RemoteRead explanation to stream.py * Fix refactoring issues * Apply suggestions from code review --------- Co-authored-by: jonschz <jonschz@users.noreply.github.com> Co-authored-by: Jun Komoda <45822440+junkmd@users.noreply.github.com>
The standard treatment of argument directions is as follows:
'in'
argument must be provided and is not returned.'out'
argument is returned by the function and must not be provided.'in', 'out'
argument must be provided and is returned.This model breaks down when a parameter marked as
'out'
must be preallocated with some defined amount of memory. This happens for example in ISequentialStream::Read (orRemoteRead
in my case, which has the same signature). The parameters are given byThis call only works if
pv
is pre-allocated withcb
bytes, which cannot be done in the current model.I resorted to calling the base method
ISequentialStream._ISequentialStream__com_RemoteRead
with three arguments. My question is whether I am overlooking a more elegant solution. If that is not the case, I was wondering if there is a better way to treat this problem.One other option is to change the generated header and set the first parameter to be
['in', 'out']
, but this breaks if one re-generates the file and is also technically incorrect.The text was updated successfully, but these errors were encountered: