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 参数完成的。
我现在遇到的问题是我的结构化类只检索数字字段DWORD
和INT
中的值,但所有字母数字字段/所有指针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 结构之间的大小不匹配,默认结构对齐/填充