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

Posted

技术标签:

【中文标题】InternetCrackUrlW 不填充结构化类中指针后面的字符串值【英文标题】:InternetCrackUrlW does not fill the values of strings behind pointers in structured class 【发布时间】:2015-10-06 14:00:34 【问题描述】:

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

此函数的“返回”值是通过具有defined structure 的 lpUrlComponents 参数完成的。

我现在遇到的问题是我的结构化类只检索数字字段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

【问题讨论】:

【参考方案1】:

这是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

【讨论】:

是使用你提供的缓冲区,还是用指向传入 url 的指针覆盖它们? @MarkJansen,它使用传递给它的缓冲区,根据文档:“如果指针包含用户提供的缓冲区的地址,则长度成员必须包含缓冲区的大小。@987654325 @将组件复制到缓冲区中,长度成员设置为复制组件的长度,为尾随字符串终止符减1。【参考方案2】:

根据remarks

所需的组件由URL_COMPONENTS 结构的成员指示。每个组件都有一个指向该值的指针,并有一个存储所存储值长度的成员。 如果一个组件的值和长度都为零,则不返回该组件。

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

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

【讨论】:

好的,所以我尝试了一些东西,但我无法弄清楚。 抱歉,我不小心按了 Enter... 长度有关系还是我可以只插入 1?然后我必须从开头读取字符串到长度指示的位置? 这适用于我在 Windows 7 中,并且通常比为每个组件分配缓冲区更灵活和高效。但是,我对 InternetCrackUrl 文档中的言论持谨慎态度,即这种行为仅适​​用于 Vista+。仍然有很多 XP 用户。我没有 XP 盒子/VM 来测试这个。 好点!我刚刚在 XP 上尝试过(安装了最新的 udpate),它似乎可以工作。以下是机器的输出。 dwStructSize:60 lpszScheme:http dwSchemeLength:4 nScheme:3 lpszHostName:www.host.com dwHostNameLength:12 nPort:8080 lpszUserName:用户dwUserNameLength:4 lpszPassword:密码dwPasswordLength:8 lpszUrlPath:/url-path dwUrlPathLength:9 lpszExtraInfo:? =值 dwExtraInfoLength: 10 @WaltheRed,该结果是基于我分配字符串缓冲区的答案还是 Mark 的(即为每个字符串设置非零大小,但将初始指针保留为 NULL)?在后一种情况下,它会将指针设置为指向您作为 lpszUrl 传递的 url 字符串,因此请保留一个引用并使用相应的 Length 字段来获取有效的子字符串。

以上是关于InternetCrackUrlW 不填充结构化类中指针后面的字符串值的主要内容,如果未能解决你的问题,请参考以下文章

Python 和 C 结构之间的大小不匹配,默认结构对齐/填充

为什么不为结构比较指定特定的填充内容?

Purify 在类/结构填充上的 Uninit Memory Read (UMR)

32位机器上“双”结构成员的填充逻辑[重复]

如何填充非线性树

如何使用 C++ 中的类创建和填充链表?