Issue 5119: wide character parameter handling in ctypes (original) (raw)
Created on 2009-01-31 20:53 by jaraco, last changed 2022-04-11 14:56 by admin. This issue is now closed.
Messages (7)
Author: Jason R. Coombs (jaraco) *
Date: 2009-01-31 20:53
Using Python 2.5.4 and Python 2.6.1 on 32-bit python, when passing a regular string to a function expecting pointer to a wide string (wintypes.LPCWSTR), the function executes without problems.
When calling the same using Python 2.6.1 on 64-bit windows, the called function appears not to recognize the parameter unless it is first converted to unicode.
I discovered this when calling the WNetAddConnection2 function.
Assuming a properly-initialized NETRESOURCE, resource.
password = None username = 'username@domain.com' flags = 0
val = ctypes.windll.mpr.WNetAddConnection2W( ctypes.byref(resource), password, username, flags)
This method works fine on 32-bit python but fails on 64-bit python unless username=unicode(username). The error returned is "The specified password is incorrect", indicating that the username is in fact incorrect because the correct username does not require a password.
I wish I had a better test case; I'll try to track down one that doesn't require such a complex underlying API.
I'm not sure what the correct fix is for this, but regardless, I would expect the behavior to be consistent for the same Python version independent of word size.
Author: Jason R. Coombs (jaraco) *
Date: 2009-01-31 21:33
After putting together a more simple example without externalities, I'm unable to continue to assert the discrepancy between 32 and 64-bit Windows, although I do still see where narrow character strings are treated as wide character buffers.
See the attached script that demonstrates the issue (on Python 2.5 and Python 2.6 regardless of word size).
Now the inconsistency seems to only lie with the WNetAddConnection2W function and not the MessageBoxW function, both of which take LPCTSTR parameters (at least according to the documentation).
Perhaps this is a non-issue, but I'd be interested to know why the WNetAddConnection2W example works in 32-bit but not 64-bit. Could it be the 32-bit WNetAddConnection2W actually attempts to handle the buffer as both wide and narrow, but MessageBoxW takes it at face value?
Author: Jason R. Coombs (jaraco) *
Date: 2009-01-31 21:39
Or alternately, is it possible (and reasonable) for ctypes to inspect the function signature and create wide character buffers when appropriate?
Author: Jason R. Coombs (jaraco) *
Date: 2009-01-31 23:54
I've confirmed that my original assumption was quite false, and that even if the parameters are the correct width, WNetAddConnection2W behaves differently in 64-bit windows versus 32-bit windows, so it made a bad test case.
So I've changed the title of this issue, because I still would like to know if it is proper for ctypes to accept a narrow string but treat it as a wide string without converting it.
Author: Jason R. Coombs (jaraco) *
Date: 2009-02-02 20:35
I see this in the documentation, which basically answers the question:
"windll does not try to select [wide or narrow functions] by magic, you must access the version you need by specifying GetModuleHandleA or GetModuleHandleW explicitely, and then call it with normal strings or unicode strings respectively."
This behavior is inconsistent with how structures are handled, where members are up-converted to unicode. For example.
class simple(Structure): fields = [('value', c_wchar_p)]
simple('foo').value u'foo'
Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) *
Date: 2009-02-03 01:39
ctypes cannot guess the function signature, and does not know if the function expects strings or unicodes.
In your examples, ctypes.windll.user32.MessageBoxW(handle, text, caption, type) will accept everything you pass, and create C values depending on the types of the actual values of the parameters: when you pass a unicode, ctypes uses a wchar_t* buffer; when you pass a narrow string, ctypes uses a char* buffer (and is wrong in this case).
In your Structure example, you do declare a kind of signature for the Structure. ctypes is now able to convert the value you give into the declared type.
You can do the same thing with functions, if you provide the signature:
MessageBox = ctypes.windll.user32.MessageBoxW
MessageBox.argtypes = [ctypes.c_void_p, ctypes.c_wchar_p,
ctypes.c_wchar_p, ctypes.c_int]
(or better, since this matches the documentation on msdn:) from ctypes.wintypes import * MessageBox.argtypes = [HWND, LPCWSTR, LPCWSTR, UINT]
And then you may indifferently pass strings or unicodes: MessageBox(None, u"café", "drink", 0)
Author: Jason R. Coombs (jaraco) *
Date: 2009-02-03 12:22
Thanks for the excellent suggestion.
Please close this issue.
History
Date
User
Action
Args
2022-04-11 14:56:45
admin
set
github: 49369
2009-02-03 12:41:54
amaury.forgeotdarc
set
status: pending -> closed
2009-02-03 12:22:07
jaraco
set
messages: +
2009-02-03 01:39:23
amaury.forgeotdarc
set
status: open -> pending
nosy: + amaury.forgeotdarc
resolution: works for me
messages: +
2009-02-02 20:35:59
jaraco
set
messages: +
2009-01-31 23:54:16
jaraco
set
messages: +
title: inconsistent wide character parameter handling in 64-bit python -> wide character parameter handling in ctypes
2009-01-31 21:39:35
jaraco
set
messages: +
2009-01-31 21:33:19
jaraco
set
files: + ui.py
messages: +
2009-01-31 20:53:36
jaraco
create