【问题标题】:WinInet Access Violation in HttpOpenRequest()HttpOpenRequest() 中的 WinInet 访问冲突
【发布时间】:2021-12-25 18:09:12
【问题描述】:

我正在尝试使用 WinInet 将文件上传到 PHP 页面。我在其中一个功能上遇到访问冲突,但不明白为什么。我已经从示例页面构建了代码。

代码如下:

HINTERNET aInternet=InternetOpen("My-Custom-Agent/1.0",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
HINTERNET aConnect=InternetConnect(aInternet,"www.myserver.com",INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
if (aConnect)
{
    HINTERNET aRequest=HttpOpenRequest(aConnect, (const char*)"POST","myphppage.php", NULL, NULL, (const char**)"*/*\0",0,1);
    // ^^
    // Exception happens on this line
    // Exception thrown at 0x70C85B7C (ininet.dll) in TestApp.exe:
    // 0xC00000005: Access violation reading location 0x002A2F2A
    //
}

当我使用InternetOpenURL() 从服务器下载时,一切似乎都很好。它只是不喜欢我在这里某处所做的事情。任何线索我做错了什么?

【问题讨论】:

  • 与您的问题无关,但在 C++ 中,所有文字字符串实际上都是常量字符数组,它衰减为指向常量字符的指针(即const char*)。换句话说,不需要(const char*)"POST" 中的演员表。事实上,每当你觉得需要进行这样的 C 风格转换时,你应该把它当作你做错了什么的标志。
  • 回到错误的 C 样式转换,字符串 "*/*\0" 将衰减为 const char* 类型的指针。绝对不可能将其用作指向指针的指针(您尝试使用强制转换为const char**)。而且很可能是坠机的原因。如果您删除演员表,编译器应该(正确地)抱怨。抛开这些抱怨不是一个好主意。
  • 某个程序员老兄,这就是答案。但雷米(下)也给出了一个可行的答案,但没有具体说明。既然你先回答了,如果你想在下面给出答案,我会给你功劳。
  • @KiraHoneybee 我的回答怎么没有提供细节?
  • 读取位置 0x002A2F2A - 这是"*/*" 的二进制值。字符串数据解释为指针。错误已经给你强烈的提示

标签: c++ windows winapi networking


【解决方案1】:

根据HttpOpenRequest() documentation

[in] lplpszAcceptTypes

指向以空结尾的字符串数组的指针,指示客户端接受的媒体类型。这是一个例子。

PCTSTR rgpszAcceptTypes[] = {_T("text/*"), NULL};

未能正确终止使用 NULL 指针的数组将导致崩溃。

您正在传递一个指向单个以 null 结尾的字符串的指针,错误地类型转换为 const char**

lplpszAcceptTypes -> "*/*"

但该函数需要一个指向 数组 的指针,该数组包含指向以 null 结尾的字符串的指针,其中数组中的最后一个元素必须为 NULL 才能终止数组(因为没有指定函数参数数组中的元素个数):

                     -----
lplpszAcceptTypes -> | 0 | -> "*/*"
                     |---|
                     | 1 | -> NULL
                     -----

看到区别了吗?

该函数将您的字符串文字的 content 误解为好像它是一个指针,但事实并非如此,因此导致 AV 崩溃。发生 AV 的地址 0x002A2F2A 与字符串文字内容的字节数完全相同 ("*/*“ = 0x2A 0x2F 0x2A 0x00)。

你需要改用这个:

LPCSTR rgpszAcceptTypes[] = {"*/*", NULL};
HINTERNET aRequest = HttpOpenRequest(aConnect, "POST", "myphppage.php", NULL, NULL, rgpszAcceptTypes, 0, 1);

【讨论】:

  • 嗨,我的字符串中的“\0”扩展为空(通过检查字节确认)。这是另一个问题 - 如果不使用调试器,它可以正常工作。
  • @KiraHoneybee 字符串文字具有由编译器添加的隐式'\0',因此您无需添加自己的。字符串文字"*/*" 是数组{'*', '/', '*', '\0'}。通过手动添加'\0',您只是将数组扩展到{'*', '/', '*', '\0', '\0'},这对函数处理数据的方式没有任何影响。影响它的是内存中字符串数据的布局,而你最初传入的是完全错误的布局,因此崩溃
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多