This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: wide character parameter handling in ctypes
Type: behavior Stage:
Components: ctypes Versions: Python 2.6, Python 2.5
process
Status: closed Resolution: works for me
Dependencies: Superseder:
Assigned To: theller Nosy List: amaury.forgeotdarc, jaraco, theller
Priority: normal Keywords:

Created on 2009-01-31 20:53 by jaraco, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
ui.py jaraco, 2009-01-31 21:33 simple script to reproduce the behavior
Messages (7)
msg80883 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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.
msg80885 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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?
msg80886 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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?
msg80892 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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.
msg80994 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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'
msg81022 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) 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)
msg81047 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) 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:45adminsetgithub: 49369
2009-02-03 12:41:54amaury.forgeotdarcsetstatus: pending -> closed
2009-02-03 12:22:07jaracosetmessages: + msg81047
2009-02-03 01:39:23amaury.forgeotdarcsetstatus: open -> pending
nosy: + amaury.forgeotdarc
resolution: works for me
messages: + msg81022
2009-02-02 20:35:59jaracosetmessages: + msg80994
2009-01-31 23:54:16jaracosetmessages: + msg80892
title: inconsistent wide character parameter handling in 64-bit python -> wide character parameter handling in ctypes
2009-01-31 21:39:35jaracosetmessages: + msg80886
2009-01-31 21:33:19jaracosetfiles: + ui.py
messages: + msg80885
2009-01-31 20:53:36jaracocreate