InternetCrackUrlW 不填充结构化指针后面的字符串值 class

InternetCrackUrlW does not fill the values of strings behind pointers in structured class

我正在尝试使用 WinINet 库中的 InternetCrackUrl 函数解析 url。

此函数的 "return" 值是通过具有 defined structure.

的 lpUrlComponents 参数完成的

我现在遇到的问题是我的结构化 class 仅检索数字字段 DWORDINT 中的值,但所有字母数字字段/所有指针 LPCWSTR, 为空(在代码块末尾输出)。

我知道有问题的参数应该是指向变量的指针。我认为这是错误的部分。

我做错了什么?

编辑: 我在代码块的末尾添加了使其工作所需的几行。这已经在Win8.1和XP上测试过。

from ctypes import *
from ctypes.wintypes import *

dll = windll.wininet

url = LPCWSTR("http://user:password@www.host.com:8080/url-path?key=value")
url_length = DWORD(len(url.value))
flags = DWORD()

class URL_COMPONENTS(Structure):
    _fields_ = [
        ("dwStructSize", DWORD),
        ("lpszScheme", LPCWSTR),
        ("dwSchemeLength", DWORD),
        ("nScheme", INT),
        ("lpszHostName", LPCWSTR),
        ("dwHostNameLength", DWORD),
        ("nPort", INT),
        ("lpszUserName", LPCWSTR),
        ("dwUserNameLength", DWORD),
        ("lpszPassword", LPCWSTR),
        ("dwPasswordLength", DWORD),
        ("lpszUrlPath", LPCWSTR),
        ("dwUrlPathLength", DWORD),
        ("lpszExtraInfo", LPCWSTR),
        ("dwExtraInfoLength", DWORD),
    ]

url = LPCWSTR("http://user:password@www.host.com:8080/url-path?key=value")
url_length = DWORD(len(url.value))
flags = DWORD()
url_components = URL_COMPONENTS()

dll.InternetCrackUrlW.restype = c_bool

print "Output of initial question:"
print dll.InternetCrackUrlW(url, url_length, flags, byref(url_components))

for field in url_components._fields_:
     print field[0], getattr(url_components, field[0])

print "\nOutput of working:"

# Give the those lengths a nonzero value. == 0 do nothing, != 0 do something
url_components.dwHostNameLength = DWORD(-1)

dll.InternetCrackUrlW(url, url_length, flags, byref(url_components))

# Now we got the  string cut off at the start of the desired element.
print "lpszHostName:", url_components.lpszHostName

# And the length of the content.
print "dwHostNameLength:", url_components.dwHostNameLength

# Just cut it out and you get the desired result.
print "HostName:", url_components.lpszHostName[:url_components.dwHostNameLength]

Output of initial question output:
True
dwStructSize 60
lpszScheme None
dwSchemeLength 0
nScheme 3
lpszHostName None
dwHostNameLength 0
nPort 8080
lpszUserName None
dwUserNameLength 0
lpszPassword None
dwPasswordLength 0
lpszUrlPath None
dwUrlPathLength 0
lpszExtraInfo None
dwExtraInfoLength 0

Output of working:
lpszHostName: www.host.com:8080/url-path?key=value
dwHostNameLength: 12
HostName: www.host.com

根据 remarks:

The required components are indicated by members of the URL_COMPONENTS structure. Each component has a pointer to the value and has a member that stores the length of the stored value. If both the value and the length for a component are equal to zero, that component is not returned.

因此在调用 InternetCrackUrl 之前为您感兴趣的所有项目设置长度组件。

当函数returns时,指针将设置为有效组件,但它们指向您自己的字符串!同时使用指针从结构中检索它们的长度。

下面是 URL_COMPONENTS 结构的实现,它将所有字符串缓冲区设置为固定大小,默认为 512 个字符。

from ctypes import *
from ctypes.wintypes import *

wininet = WinDLL('wininet', use_last_error=True)

class URL_COMPONENTS(Structure):
    _fields_ = (("dwStructSize", DWORD),
                ("lpszScheme", LPWSTR),
                ("dwSchemeLength", DWORD),
                ("nScheme", INT),
                ("lpszHostName", LPWSTR),
                ("dwHostNameLength", DWORD),
                ("nPort", INT),
                ("lpszUserName", LPWSTR),
                ("dwUserNameLength", DWORD),
                ("lpszPassword", LPWSTR),
                ("dwPasswordLength", DWORD),
                ("lpszUrlPath", LPWSTR),
                ("dwUrlPathLength", DWORD),
                ("lpszExtraInfo", LPWSTR),
                ("dwExtraInfoLength", DWORD))
    def __init__(self, bufsize=512):
        self.dwStructSize = sizeof(self)
        fields = iter(self._fields_)
        for name, dtype in fields:
            if dtype == LPWSTR:
                buf = (c_wchar * bufsize)()
                setattr(self, name, cast(buf, LPWSTR))
                name, dtype = next(fields)
                setattr(self, name, bufsize)
if __name__ == '__main__':
    url = LPCWSTR("http://user:password@www.host.com:8080/url-path?key=value")
    url_length = len(url.value)
    flags = 0
    url_components = URL_COMPONENTS()
    if not wininet.InternetCrackUrlW(url, url_length, flags,
                                     byref(url_components)):
        raise WinError(get_last_error())
    for name, dtype in url_components._fields_:
        print '%s: %s' % (name, getattr(url_components, name))

输出:

dwStructSize: 104
lpszScheme: http
dwSchemeLength: 4
nScheme: 3
lpszHostName: www.host.com
dwHostNameLength: 12
nPort: 8080
lpszUserName: user
dwUserNameLength: 4
lpszPassword: password
dwPasswordLength: 8
lpszUrlPath: /url-path
dwUrlPathLength: 9
lpszExtraInfo: ?key=value
dwExtraInfoLength: 10