- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我们正在DLL中使用WinInet进行异步网络调用。
当应用程序退出时,我们使用InetSetStatusCallback(connect_handle, NULL);
删除针对未决请求的已注册回调函数。但是,偶尔在DLL卸载后仍会调用回调函数,从而导致应用程序崩溃。
症状与该博客的上一个常见问题解答完全相似:WinHTTP Questions: About Callbacks
我试图找出一种安全删除所有挂起请求的回调函数的方法,以便在DLL卸载后WinInet不会调用它们。
最佳答案
在将指针传递给回调函数之后,当然不能卸载包含此回调函数的模块,直到可以调用回调为止。如果回调函数位于EXE模块中,则绝对不会有问题,因为它将永远不会被卸载。但是如果是DLL,则需要在设置回调之前添加对DLL的引用,以防止DLL卸载。这很容易,可以通过 GetModuleHandleExW
函数完成
HMODULE hmod;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PCWSTR)&__ImageBase, &hmod);
FreeLibrary
)。什么时候会?
InternetSetStatusCallback
为某些句柄设置了一个回调函数。当然,应该通过
InternetCloseHandle
和以下方式关闭此句柄:
It is safe to call InternetCloseHandle in a callback for the handle being closed. If there is a status callback registered for the handle being closed, and the handle was created with a non-NULL context value, an INTERNET_STATUS_HANDLE_CLOSING callback will be made. This indication will be the last callback made from a handle and indicates that the handle is being destroyed.
If asynchronous requests are pending for the handle or any of its child handles, the handle cannot be closed immediately, but it will be invalidated. Any new requests attempted using the handle will return with an ERROR_INVALID_HANDLE notification. The asynchronous requests will complete with INTERNET_STATUS_REQUEST_COMPLETE. Applications must be prepared to receive any INTERNET_STATUS_REQUEST_COMPLETE indications on the handle before the final INTERNET_STATUS_HANDLE_CLOSING indication is made, which indicates that the handle is completely closed.
INTERNET_STATUS_HANDLE_CLOSING
-将是从句柄进行的最后回调的
。 最终调用。恰在此时,我们需要取消引用DLL模块-不再调用回调。不需要更多引用。FreeLibrary
,因为如果DLL驻留在最后一个引用上-它将在FreeLibrary
调用中卸载,当我们返回时-我们将返回emply空间并崩溃。FreeLibraryAndExitThread
-因为我们在任意线程上。但是存在不正确的,但是在实际解决方案上起作用:ULONG WINAPI SafeUnload(void*)
{
//Sleep(*);
FreeLibraryAndExitThread((HMODULE)&__ImageBase, 0);
}
if (HANDLE hThread = CreateThread(0, 0, SafeUnload, 0, 0, 0))
{
CloseHandle(hThread);
}
FreeLibraryAndExitThread
(此处调用正确)。但是我怎么说-理论上这是不正确的解决方案。这里存在种族-FreeLibraryAndExitThread
调用可以在从我们从
CreateThread
调用返回之前执行并卸载DLL
。结果我们在CreateThread
之后崩溃(我们尝试执行已经卸载的代码)。当然这不太可能,但是..Sleep
之前插入FreeLibraryAndExitThread
(有些延迟),但是无论如何-理论上可以在任何延迟下进行竞赛。再次选择延迟的具体值是多少?并且DLL这次将延迟卸载。不正确,也不好。尽管这是相对简单的“解决方案”,但我不使用它。FreeLibrary
而不返回该跳转的位置。但必须返回到调用回调的位置。这在c / c++中是不可能的,但在asm代码中是可能的。当然,每个平台目标都需要单独的asm文件(x86,x64最低要求)INTERNET_STATUS_CALLBACK
回调函数无论如何都必须与某个类相关联,我们在其中保存了句柄上下文。这必须是类和DWORD_PTR中的静态函数dwContext-与hInternet关联的应用程序定义的上下文值必须指向类的实例(实际上是此指针)。通常这是通过以下方式完成的:class REQUEST_CONTEXT
{
// ...
static void WINAPI _StatusCallback(
__in HINTERNET hRequest,
__in DWORD_PTR dwContext,
__in DWORD dwInternetStatus,
__in LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
)
{
reinterpret_cast<REQUEST_CONTEXT*>(dwContext)->StatusCallback(
hRequest,
dwInternetStatus,
lpvStatusInformation,
dwStatusInformationLength
);
}
void StatusCallback(
__in HINTERNET hRequest,
__in DWORD dwInternetStatus,
__in LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
);
};
_StatusCallback
以便在此处调用FreeLibrary
。并将StatusCallback
的返回值从void更改为BOOL-TRUE-如果这是最终调用(dwInternetStatus == INTERNET_STATUS_HANDLE_CLOSING
),则返回,否则返回FALSE。// helper for get complex c++ names for use in asm code
#if 0
#define ASM_FUNCTION {__pragma(message(__FUNCDNAME__" proc\r\n" __FUNCDNAME__ " endp"))}
#define CPP_FUNCTION __pragma(message("extern " __FUNCDNAME__ " : PROC ; " __FUNCSIG__))
#else
#define ASM_FUNCTION
#define CPP_FUNCTION
#endif
class REQUEST_CONTEXT
{
// ...
static void WINAPI _StatusCallback(
__in HINTERNET hRequest,
__in DWORD_PTR dwContext,
__in DWORD dwInternetStatus,
__in LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
) ASM_FUNCTION;
BOOL StatusCallback(
__in HINTERNET hRequest,
__in DWORD dwInternetStatus,
__in LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
);
ULONG SendRequest(PCWSTR pwszObjectName);
void OnRequestComplete(HINTERNET hRequest, INTERNET_ASYNC_RESULT* pres);
};
StatusCallback
实现:BOOL REQUEST_CONTEXT::StatusCallback(
__in HINTERNET hRequest,
__in DWORD dwInternetStatus,
__in LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
)
{
CPP_FUNCTION;
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CLOSING:
Release();
return TRUE;
case INTERNET_STATUS_REQUEST_COMPLETE:
OnRequestComplete(hRequest, (INTERNET_ASYNC_RESULT*)lpvStatusInformation);
break;
}
return FALSE;
}
SendRequest
内的代码中,用于从 HttpOpenRequestW
返回的句柄)ULONG SendRequest(PCWSTR pwszObjectName)
{
// ... some code ...
AddRef();
HMODULE hmod;
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PCWSTR)&__ImageBase, &hmod))
{
if (HINTERNET hRequest = HttpOpenRequestW(..., (DWORD_PTR)this))
{
set_handle(hRequest);
if (INTERNET_INVALID_STATUS_CALLBACK != InternetSetStatusCallback(hRequest, _StatusCallback))
{
INTERNET_ASYNC_RESULT ar;
ar.dwResult = HttpSendRequestW(hRequest, 0, 0, 0, 0);
ar.dwError = ar.dwResult ? NOERROR : GetLastError();
if (ar.dwError != ERROR_IO_PENDING)
{
OnRequestComplete(hRequest, &ar);
}
return NOERROR;
}
}
FreeLibrary(hmod);
}
Release();
return GetLastError();
}
GetModuleHandleExW
在设置回调之前添加对DLL的引用。如果设置回调失败-我们将FreeLibrary
用作引用DLL。请注意,此处安全正确地调用FreeLibrary
,因为调用SendRequest
的人必须具有DLL的引用-当然,在此调用期间不能卸载DLL。因此,当我们从此函数调用FreeLibrary
时-确保不释放对DLL的最后一个引用(我们从GetModuleHandleExW
调用中释放引用,但在此函数执行期间在DLL上存在其他引用。此函数的调用者(直接或间接)关心这个)。在dwContext中,我们传递了该指针。_StatusCallback
实现。extern __imp_FreeLibrary:QWORD
extern __ImageBase:BYTE
; void REQUEST_CONTEXT::StatusCallback(void *,unsigned long,void *,unsigned long)
extern ?StatusCallback@REQUEST_CONTEXT@@QEAAHPEAXK0K@Z : PROC
.CODE
?_StatusCallback@REQUEST_CONTEXT@@SAXPEAX_KK0K@Z proc
xchg rcx,rdx
mov rax,[rsp + 28h]
sub rsp,38h
mov [rsp + 20h],rax
call ?StatusCallback@REQUEST_CONTEXT@@QEAAHPEAXK0K@Z
add rsp,38h
test eax,eax
jnz @@1
ret
@@1:
lea rcx, __ImageBase
jmp __imp_FreeLibrary
?_StatusCallback@REQUEST_CONTEXT@@SAXPEAX_KK0K@Z endp
end
.MODEL FLAT
extern __imp__FreeLibrary@4:DWORD
extern ___ImageBase:BYTE
; int __thiscall REQUEST_CONTEXT::StatusCallback(void *,unsigned long,void *,unsigned long)
extern ?StatusCallback@REQUEST_CONTEXT@@QAEHPAXK0K@Z : PROC
.CODE
?_StatusCallback@REQUEST_CONTEXT@@SGXPAXKK0K@Z proc
mov ecx,[esp + 8]
push [esp + 20]
push [esp + 20]
push [esp + 20]
push [esp + 16]
call ?StatusCallback@REQUEST_CONTEXT@@QAEHPAXK0K@Z
test eax,eax
jnz @@1
ret 4*5
@@1:
mov eax,[esp]
lea edx, ___ImageBase
add esp,4*4
mov [esp],eax
mov [esp + 4],edx
jmp __imp__FreeLibrary@4
?_StatusCallback@REQUEST_CONTEXT@@SGXPAXKK0K@Z endp
end
关于c++ - 如何安全地防止在Wininet中调用状态回调函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55862504/
我正在寻找一个简单的函数,它能够将来自 Internet 的文本或二进制文件读入 string 多变的。 令人难以置信的是,我在网络上找不到任何内容,只有所有 WinInet 的低级描述。在 MQL
我有以下代码: #include #include #include #include #include using namespace std; int main(int argc, ch
我正在使用 wininet 连接到服务器。一切正常,上传,下载,列出命令等......因此,我有一个小错误,我不知道它来自哪里。错误是某些包含超过 100 个文件(例如图像)的服务器文件夹没有全部列出
我正在使用 WinInet 和 InternetOpenUrl 下载一个文件...正在运行。但我想监控进度,所以我尝试添加一个回调函数,但由于某种原因它从未被调用过...... 代码: void CA
我对 C++ 编程还很陌生,所以请多多包涵。我正在尝试创建一个用于教育目的的 ftp 客户端,做了一些研究并决定尝试一下 Wininet,并发现了一些在线教程: 以此为例: #include #in
我有一个将网页下载到文本文件的功能 #include #include #include #include #include #pragma comment(lib, "WinINet.li
我很好奇为什么我在使用此功能时遇到问题。我正在将网络上的 PNG 文件下载到目标路径。例如下载谷歌图片到C:盘: netDownloadData("http://www.google.com/intl
我已经为 Internet Explorer 编写了一个 BHO,它 Hook WinInet 以修改来自 IE 的一些 HTTP 请求,方法是将它们重定向到内部服务器。 当我在 IE 中打开前 3
我正在试验我的 .NET 客户端和服务器之间的缓存。在 WinInet 决定缓存结果之前,我看到一个看似随机的端点命中数。 .NET 客户端使用 HttpWebRequest 发出请求: HttpWe
我发现 WinHTTP 不可重入(1、2)。 WinINET 是可重入的吗? 我们有一个与 WinINET 同步 HTTP 的 ActiveX 控件。如果浏览器(在 javascript 中)触发一个
相关 How to send a HTTP POST Request in Delphi using WinInet api : 我如何提出发布请求并跟踪进度? 这不起作用(检查评论): proced
iam 使用 wininet 下载图像并将其保存到内存流这是我的程序 procedure DownloadToStream(const Url: string; ms: TMemoryStream);
我们使用 WinInet 和 Delphi 通过 HTTPS 进行通信。WinInet 中是否有一个函数可以返回 session 中协商的协议(protocol),即 TLS1.1、TLS 1.2 等
我有一个应用程序,它大量使用 Wininet 函数从互联网获取一些数据。有时我会收到非常奇怪的与句柄相关的错误消息: Internal error in ConnectToHost when tryi
我从第三方网站获得了以下源代码,解释了如何使用 WinInet 从互联网下载文件。我对 API 不太熟悉,我查看了 WinInet 单元,但没有看到任何我需要的 API 调用。 我正在做的是添加报告文
我在 wininet.dll 深处遇到崩溃。尝试读取 HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection 中的零内存位置时崩溃了 这是实际 DLL 中的错误,而
我正在尝试检测 ftp 服务器何时关闭我在应用程序中打开的连接。我正在使用 WinInet 功能。 我发现了一些使用 InternetSetStatusCallback 函数的示例。我实现了一个回调函
我编写了一个使用 WinInet 库的程序。该程序每天运行约 8-12 小时。首先它连接到互联网,然后它使用 FTP 下载/上传文件。之后它开始一个循环,在不同的时间间隔启动最多两个线程。两个线程都在
我正在尝试连接并确保我们设计的仪器提供的网络服务器上存在各种页面。我正在尝试使用 WinInet 命令通过 C++ Win32 执行此操作。 我很高兴我已经通过 HTTP 正确连接到网络服务器: hI
我正在尝试使用 C++ 中的 WinHTTP 获取文件的内容。该文件是一个 XML 文件,由服务器上的可执行文件生成。 用于初始化、连接甚至读取指定服务器地址上的文件的代码正在运行。 // Conne
我是一名优秀的程序员,十分优秀!