Skip to content

wide character parameter handling in ctypes #49369

Closed

Description

@jaraco
BPO 5119
Nosy @theller, @amauryfa, @jaraco
Files
  • ui.py: simple script to reproduce the behavior
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/theller'
    closed_at = <Date 2009-02-03.12:41:54.468>
    created_at = <Date 2009-01-31.20:53:36.488>
    labels = ['ctypes', 'type-bug']
    title = 'wide character parameter handling in ctypes'
    updated_at = <Date 2009-02-03.12:41:54.467>
    user = 'https://github.com/jaraco'

    bugs.python.org fields:

    activity = <Date 2009-02-03.12:41:54.467>
    actor = 'amaury.forgeotdarc'
    assignee = 'theller'
    closed = True
    closed_date = <Date 2009-02-03.12:41:54.468>
    closer = 'amaury.forgeotdarc'
    components = ['ctypes']
    creation = <Date 2009-01-31.20:53:36.488>
    creator = 'jaraco'
    dependencies = []
    files = ['12908']
    hgrepos = []
    issue_num = 5119
    keywords = []
    message_count = 7.0
    messages = ['80883', '80885', '80886', '80892', '80994', '81022', '81047']
    nosy_count = 3.0
    nosy_names = ['theller', 'amaury.forgeotdarc', 'jaraco']
    pr_nums = []
    priority = 'normal'
    resolution = 'works for me'
    stage = None
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue5119'
    versions = ['Python 2.6', 'Python 2.5']

    Activity

    1. jaraco commented on Jan 31, 2009

      @jaraco
      MemberAuthor

      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 = '[email protected]'
      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.

    2. jaraco commented on Jan 31, 2009

      @jaraco
      MemberAuthor

      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?

    3. jaraco commented on Jan 31, 2009

      @jaraco
      MemberAuthor

      Or alternately, is it possible (and reasonable) for ctypes to inspect
      the function signature and create wide character buffers when appropriate?

    4. jaraco commented on Jan 31, 2009

      @jaraco
      MemberAuthor

      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.

    5. changed the title [-]inconsistent wide character parameter handling in 64-bit python[/-] [+]wide character parameter handling in ctypes[/+] on Jan 31, 2009
    6. jaraco commented on Feb 2, 2009

      @jaraco
      MemberAuthor

      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'
    7. amauryfa commented on Feb 3, 2009

      @amauryfa
      Contributor

      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)

    8. jaraco commented on Feb 3, 2009

      @jaraco
      MemberAuthor

      Thanks for the excellent suggestion.

      Please close this issue.

    9. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions