wininet内部的探索
这里源代码都是reatos,有些是WIN2K的代码。
1:HINTERNET Handles
对外的定义winhttp.h 里面
这个就是我用的到句柄
typedef LPVOID HINTERNET;
typedef HINTERNET * LPHINTERNET;
这里就是一个指针,具体指向什么数据结构就看他的上下文了。(上下文可以看成是哪个函数调用的他)
MSDN说了一句很重要的话
Note that handle values are recycled quickly; therefore, if a handle is closed and a new handle is generated immediately, there is a good chance that the new handle has the same value as the handle just closed.
注意释放 HINTERNET句柄很可能会导致一些意想不到的问题。(后面写一个专门测试例子来验证)
2:InternetOpen 看看他做了什么,毕竟这个所有的根函数
MSDN解析:Initializes an application‘s use of the WinINet functions.
貌似说初始化wininet的函数。
我觉得是在一个进程里面建立起一个数据结构(我觉得这个不是系统的数据结构,而是一个进程的内部的数据结构)。
看一下在这个函数做了什么?
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags) { appinfo_t *lpwai = NULL; lpwai = alloc_object(NULL, &APPINFOVtbl, sizeof(appinfo_t)); if (!lpwai) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } lpwai->hdr.htype = WH_HINIT; lpwai->hdr.dwFlags = dwFlags; lpwai->accessType = dwAccessType; lpwai->proxyUsername = NULL; lpwai->proxyPassword = NULL; lpwai->agent = heap_strdupW(lpszAgent); if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG) INTERNET_ConfigureProxy( lpwai ); else lpwai->proxy = heap_strdupW(lpszProxy); lpwai->proxyBypass = heap_strdupW(lpszProxyBypass); TRACE("returning %p\n", lpwai); return lpwai->hdr.hInternet; //这里在alloc_object做的。 //这里看是返回的索引,但我实际检测的时候,返回数组的地址,而不是索引,其实2种相同。 }
这个函数做了分配对应的INTERNET根的数据结构,同时把参数分配内存保存堆上。
我在vc6 测试InternetOpen.
void TestInternetOpen()
{
HINTERNET hInternetRoot = InternetOpen("fishclient",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
printf("%u\n",(UINT_PTR)hInternetRoot);
//InternetCloseHandle(hInternetRoot);
}
第一次创建值都是固定,如果你没有释放的话,下次创建的值就偏移Int位置,看上面的函数,貌似return lpwai->hdr.hInternet;
返回的索引,但根据TestInternetOpen是不对的,后来我自己下了win2000的源代码,看了一下返回是内部数据结构的假的地址(类似索引),所以reactos反汇编确实不完全安装微软的源代码走的,而是通过自己理解再重写一遍,可以说提炼微软意思,然后用少量代码写出来(因为一些功能,reactos没有实现)。感谢这些前辈给我留下这么好的东西。
win2k 的InternetOpenA的代码
I
NTERNETAPI HINTERNET WINAPI InternetOpenA( IN LPCSTR lpszAgent, IN DWORD dwAccessType, IN LPCSTR lpszProxy OPTIONAL, IN LPCSTR lpszProxyBypass OPTIONAL, IN DWORD dwFlags ) /*++ Routine Description: Opens a root Internet handle from which all HINTERNET objects are derived Arguments: lpszAgent - name of the application making the request (arbitrary identifying string). Used in "User-Agent" header when communicating with HTTP servers, if the application does not add a User-Agent header of its own dwAccessType - type of access required. Can be INTERNET_OPEN_TYPE_PRECONFIG - Gets the configuration from the registry INTERNET_OPEN_TYPE_DIRECT - Requests are made directly to the nominated server INTERNET_OPEN_TYPE_PROXY - Requests are made via the nominated proxy INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY - Like Pre-Config, but prevents JavaScript, INS and other auto-proxy types from being used. lpszProxy - if INTERNET_OPEN_TYPE_PROXY, a list of proxy servers to use lpszProxyBypass - if INTERNET_OPEN_TYPE_PROXY, a list of servers which we will communicate with directly dwFlags - flags to control the operation of this API or potentially all APIs called on the handle generated by this API. Currently supported are: INTERNET_FLAG_ASYNC - if specified then all subsequent API calls made against the handle returned from this API, or handles descended from the handle returned by this API, have the opportunity to complete asynchronously, depending on other factors relevant at the time the API is called Return Value: HINTERNET Success - handle of Internet object Failure - NULL. For more information, call GetLastError() --*/ { PERF_INIT(); DEBUG_ENTER_API((DBG_API, Handle, "InternetOpenA", "%q, %s (%d), %q, %q, %#x", lpszAgent, InternetMapOpenType(dwAccessType), dwAccessType, lpszProxy, lpszProxyBypass, dwFlags )); DWORD error; HINTERNET hInternet = NULL; if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } // // we are doing GetUserName here instead of in DLL_PROCESS_ATTACH // As every caller of wininet has to do this first, we ensure // that the username is initialized when they get to actually doing // any real operation // GetWininetUserName(); // // validate parameters // if (! ( (dwAccessType == INTERNET_OPEN_TYPE_DIRECT) || (dwAccessType == INTERNET_OPEN_TYPE_PROXY) || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG) || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY) || ( (dwAccessType == INTERNET_OPEN_TYPE_PROXY) && ( !ARGUMENT_PRESENT(lpszProxy) || (*lpszProxy == '\0') ) ) || (dwFlags & ~INTERNET_FLAGS_MASK) ) ) { error = ERROR_INVALID_PARAMETER; goto quit; } GlobalHaveInternetOpened = TRUE; // // Initalize an auto proxy dll if needed, // as long as the caller is allowing us free rein to do this // by calling us with INTERNET_OPEN_TYPE_PRECONFIG. // //if ( dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG ) //{ // if ( ! InitalizeAutoConfigDllIfNeeded() ) // { // error = GetLastError(); // // INET_ASSERT(error != ERROR_SUCCESS); // // goto quit; // } // // INTERNET_HANDLE_OBJECT * lpInternet; lpInternet = new INTERNET_HANDLE_OBJECT(lpszAgent, dwAccessType, (LPSTR)lpszProxy, (LPSTR)lpszProxyBypass, dwFlags ); if (lpInternet == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } error = lpInternet->GetStatus(); if (error == ERROR_SUCCESS) { hInternet = (HINTERNET)lpInternet; // // success - don't return the object address, return the pseudo-handle // value we generated // hInternet = ((HANDLE_OBJECT *)hInternet)->GetPseudoHandle();//这里强制转发基类指针调用函数。(微软这个时候已经用的C++来写了) // // start async support now if required. If we can't start it, we'll get // another chance the next time we create an async request // if (dwFlags & INTERNET_FLAG_ASYNC) { InitializeAsyncSupport(); } } else { // // hack fix to stop InternetIndicateStatus (called from the handle // object destructor) blowing up if there is no handle object in the // thread info block. We can't call back anyway // LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo) { // // BUGBUG - incorrect handle value // _InternetSetObjectHandle(lpThreadInfo, lpInternet, lpInternet); } // // we failed during initialization. Kill the handle using Dereference() // (in order to stop the debug version complaining about the reference // count not being 0. Invalidate for same reason) // lpInternet->Invalidate(); lpInternet->Dereference(); INET_ASSERT(hInternet == NULL); } quit: if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(hInternet); return hInternet; }
---------------------------------------------------------------------------------------------
//我们看到这个数据结构所以我们一定要知道保存了,不过其实大概也能够猜到一些东西
typedef struct
{
object_header_t hdr;
LPWSTR agent;
LPWSTR proxy;
LPWSTR proxyBypass;
LPWSTR proxyUsername;
LPWSTR proxyPassword;
DWORD accessType;
} appinfo_t;
typedef struct _object_header_t object_header_t;
struct _object_header_t
{
WH_TYPE htype;
const object_vtbl_t *vtbl;
HINTERNET hInternet;
BOOL valid_handle;
DWORD dwFlags;
DWORD_PTR dwContext;
DWORD dwError;
ULONG ErrorMask;
DWORD dwInternalFlags;
LONG refs;
INTERNET_STATUS_CALLBACK lpfnStatusCB;
struct list entry;
struct list children;
};
可以看出来这个就是链表所有的数据都保存在这里了。
这个结构会一些inertnet通用函数调用的,进行设置。
因为reactos 的 wininet的数据结构用的c来写,win2k用c++ 写。
但是意思表达一个意思,可能我觉得用c来写更能清晰,感觉的reacos代码非常好,有些技巧有必要自己慢慢的挖掘出来。
看一下指针
typedef struct {
void (*Destroy)(object_header_t*);
void (*CloseConnection)(object_header_t*);
DWORD (*QueryOption)(object_header_t*,DWORD,void*,DWORD*,BOOL);
DWORD (*SetOption)(object_header_t*,DWORD,void*,DWORD);
DWORD (*ReadFile)(object_header_t*,void*,DWORD,DWORD*);
DWORD (*ReadFileExA)(object_header_t*,INTERNET_BUFFERSA*,DWORD,DWORD_PTR);
DWORD (*ReadFileExW)(object_header_t*,INTERNET_BUFFERSW*,DWORD,DWORD_PTR);
DWORD (*WriteFile)(object_header_t*,const void*,DWORD,DWORD*);
DWORD (*QueryDataAvailable)(object_header_t*,DWORD*,DWORD,DWORD_PTR);
DWORD (*FindNextFileW)(object_header_t*,void*);
} object_vtbl_t;
BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
{
object_header_t *obj;
TRACE("%p\n", hInternet);
obj = get_handle_object( hInternet );
if (!obj) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
invalidate_handle(obj);
WININET_Release(obj);
return TRUE;
}
//关闭就比较明了,到引用为0就是放内部结构
总结:
inertOpen 确实跟MSDN说作用是一个意思
Initializes an application‘s use of the WinINet functions.
其实就是初始化一个重要的数据结构而已,然后插入到链表里面去。
其实HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
参数已经说了这一切了;。
保存的客户端的名字,保存接受类型,保存代理。===这些都是关系到后面的请求怎么去请求的问题,比喻代理如果有了,那么socket连接的时候必须连接到代理的服务器去。所有这些信息保存内部数据结构。然后通过 HINTERNET 传递出去,带调用不同的函数。