So some time ago I created a project, a DLL to create background requests in separate threads using WinHTTP and pass around data using C structures. It all worked out wonderfully with the exception of a crash every now and then, but it got the job done.
Now I have need to make my dll return data in binary instead of doing the conversions internally, I want to be able to receive any kind of data, from text, binary all the way to gzip data.
I basically make a copy of the structure in my dll on the AutoIt side.
Global $INT_BUFFERSIZE = 1048576 Global $tagTHREAD_PARAMETERS = _ "WCHAR UserAgent[1024];" & _ ; user agent "WCHAR HTTPVerb[1024];" & _ ; POST/GET/HEAD etc "WCHAR Host[1024];" & _ ; ex: google.com "WCHAR Resource[1024];" & _ ; ex: /somescript.php "int Port;" & _ ; 80/443 "WCHAR Referer[1024];" & _ ; optional referer "WCHAR Headers[1024];" & _ ; null terminated request headers "ptr ExtraData;" & _ ; pointer to structure with arguments ex: a=1&b=2&page=45 "DWORD Length;" & _ ; lenght of the arguents "DWORD TotalLength;" & _ ; same as above "int dwResolveTimeout;" & _ ; Resolve timout "int dwConnectTimeout;" & _ ; Connection timout "int dwSendTimeout;" & _ ; Send timeout "int dwReceiveTimeout;" & _ ; Recieve timeout "WCHAR Proxy[1024];" & _ ; proxy string "DWORD ProxyFlags;" & _ ; WinHTTP open flags "DWORD SendFlags;" & _ ; WinHttpOpenRequest flags "BYTE ResponceHTML[" & $INT_BUFFERSIZE & "];" & _ ; HTML returned "WCHAR ResponceHeaders[" & $INT_BUFFERSIZE & "];" & _ ; Responce Headers "DOUBLE Latency;" & _ ; Responce Latency "int RetryTimes;" & _ ; Times to retry request with server "int MaxTestTime;" & _ ; max amount of time the request can run for "ptr httpSession;" & _ "ptr httpConnect;" & _ "ptr httpRequest"
And I have this copy in my dll which I later initialize with my AutoIt copy so I can use all the info in it and subsequently return data as well.
// AutoIt thread parameters static const int INT_BUFFERSIZE = 1048576; // Had to increase to 1MB because I was getting very big pages with some tests and it wouldn't work // AutoIt thread parameters, AutoIt will create a version of this and pass it as a thread paramater struct THREAD_PARAM_COMPONENTS { WCHAR UserAgent[1024]; // user agent WCHAR HTTPVerb[1024]; // POST/GET/HEAD etc WCHAR Host[1024]; // Domain name for target request WCHAR Resource[1024]; // resource at target int Port; // port on target WCHAR Referer[1024]; // referer WCHAR Headers[1024]; // headers string LPVOID ExtraData; // additional data/paramaters to send DWORD Length; // lenght of additional data DWORD TotalLenght; // same as above for some reason int dwResolveTimeout; // max time for resolving int dwConnectTimeout; // max time for connection int dwSendTimeout; // max time to wait while sending int dwReceiveTimeout; // max time to wait for recieving responce WCHAR Proxy[1024]; // proxy for request DWORD ProxyFlags; // flags for proxy DWORD SendFlags; // send flags BYTE ResponceHTML[INT_BUFFERSIZE]; // HTML from responce WCHAR ResponceHeaders[INT_BUFFERSIZE]; // headers from responce double Latency; // return indicating total time spent in request int RetryTimes; // maximum times to retry request if failures happen int MaxTestTime; // maximum time to spend in request LPVOID httpSession; // Session handles returned to AutoIt LPVOID httpConnect; // Session handles returned to AutoIt LPVOID httpRequest; // Session handles returned to AutoIt };
I then pass the structure I created AutoIt side to the dll and it creates a thread and initializes the structure.
THREAD_PARAM_COMPONENTS* tData = (THREAD_PARAM_COMPONENTS*)threadData; // threadData is passed as an LPVOID to the thread
And then I start accessing the stuff I sent from my AutoIt script from inside the DLL...
... hSession = ::WinHttpOpen(tData->UserAgent, tData->ProxyFlags, tData->Proxy, WINHTTP_NO_PROXY_BYPASS, 0); ...
So far all that stuff works, requests are made and data was returned, up until I attempted to change my program from returning WCHAR data to a BYTE array. And now I get nothing but a ridiculous amount of zeros like "0x0000.... to infinity".
so far this is the problem var in both sides.
BYTE ResponceHTML[INT_BUFFERSIZE]; // HTML from responce
This is my DLL codebase, unicode.
////////////////////////////////////////////////////////////////////////////////////////// // DLL to create multiple background WinHTTP requests. // Why? since AutoIt is single threaded, I need a nonblocking function to make requests. // Project is Unicode ////////////////////////////////////////////////////////////////////////////////////////// #include <Windows.h> #include <WinHTTP.h> #include <string> #include <time.h> #pragma comment(lib, "winhttp.lib") // need to link to lib using namespace std; // AutoIt thread parameters static const int INT_BUFFERSIZE = 1048576; // Had to increase to 1MB because I was getting very big pages with some tests and it wouldn't work // AutoIt thread parameters, AutoIt will create a version of this and pass it as a thread paramater struct THREAD_PARAM_COMPONENTS { WCHAR UserAgent[1024]; // user agent WCHAR HTTPVerb[1024]; // POST/GET/HEAD etc WCHAR Host[1024]; // Domain name for target request WCHAR Resource[1024]; // resource at target int Port; // port on target WCHAR Referer[1024]; // referer WCHAR Headers[1024]; // headers string LPVOID ExtraData; // additional data/paramaters to send DWORD Length; // lenght of additional data DWORD TotalLenght; // same as above for some reason int dwResolveTimeout; // max time for resolving int dwConnectTimeout; // max time for connection int dwSendTimeout; // max time to wait while sending int dwReceiveTimeout; // max time to wait for recieving responce WCHAR Proxy[1024]; // proxy for request DWORD ProxyFlags; // flags for proxy DWORD SendFlags; // send flags BYTE ResponceHTML[INT_BUFFERSIZE]; // HTML from responce WCHAR ResponceHeaders[INT_BUFFERSIZE]; // headers from responce double Latency; // return indicating total time spent in request int RetryTimes; // maximum times to retry request if failures happen int MaxTestTime; // maximum time to spend in request LPVOID httpSession; // Session handles returned to AutoIt LPVOID httpConnect; // Session handles returned to AutoIt LPVOID httpRequest; // Session handles returned to AutoIt }; DWORD WINAPI _WinHTTP_Process(LPVOID threadData); ////////////////////////////////////////////////////////////////////////////////////////// // Dll entry point ////////////////////////////////////////////////////////////////////////////////////////// BOOLEAN APIENTRY DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { switch ( fdwReason ) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( hinstDLL );// disable callbacks on new threads, we don't need them. break; case DLL_PROCESS_DETACH: break; } return TRUE; } // End of DllMain ////////////////////////////////////////////////////////////////////////////////////////// // The exported function of our DLL ////////////////////////////////////////////////////////////////////////////////////////// HANDLE WINAPI WinHTTP_Action(LPVOID threadData) { return CreateThread(NULL, 0, // SIZE_T dwStackSize &_WinHTTP_Process, threadData, // LPVOID threadData 0, // DWORD dwCreationFlag NULL); // LPDWORD lpThreadId } // End of WinHTTP_Action ////////////////////////////////////////////////////////////////////////////////////////// // "The Thread" ////////////////////////////////////////////////////////////////////////////////////////// DWORD WINAPI _WinHTTP_Process(LPVOID threadData) { // Initialize the structure that AutoIt sent us, threadData with internal equivalent THREAD_PARAM_COMPONENTS THREAD_PARAM_COMPONENTS* tData = (THREAD_PARAM_COMPONENTS*)threadData; // Timer clock_t init, final; wstring m_charset; //DWORD dError; const unsigned int INT_RETRYTIMES = tData->RetryTimes; bool bRetVal = true; HINTERNET hSession = NULL; hSession = ::WinHttpOpen(tData->UserAgent, tData->ProxyFlags, tData->Proxy, WINHTTP_NO_PROXY_BYPASS, 0); //Note: do not use async... if (!hSession) return false; tData->httpSession = hSession; WinHttpSetTimeouts(hSession, tData->dwResolveTimeout, tData->dwConnectTimeout, tData->dwSendTimeout, tData->dwReceiveTimeout); HINTERNET hConnect = NULL; hConnect = ::WinHttpConnect(hSession, tData->Host, tData->Port, 0 ); if (hConnect != NULL) { tData->httpConnect = hConnect; HINTERNET hRequest = NULL; hRequest = ::WinHttpOpenRequest(hConnect, tData->HTTPVerb, tData->Resource, L"HTTP/1.1", tData->Referer, WINHTTP_DEFAULT_ACCEPT_TYPES, tData->SendFlags); if (hRequest != NULL) { tData->httpRequest = hRequest; bool bGetReponseSucceed = false; init = clock(); // start timer double TotalTime; if (!::WinHttpSendRequest(hRequest, tData->Headers, -1L, //<-- using wcslen()+1 seems to cause an invalid param error here so will stick with -1L tData->ExtraData, (DWORD) tData->Length, (DWORD) tData->TotalLenght, (DWORD) 0)) { // This is just here in case I need to refrence back to how to get and view the last error of a function //dError = GetLastError(); //WCHAR temp[100]; //swprintf_s(temp, 100, L"Error: %d" , dError); //MessageBox(0 , temp, L"Error!", 0); } else { // Call was successfull, continue accepting whatever if (::WinHttpReceiveResponse(hRequest, NULL)) { DWORD dwSize = 0; // Get the buffer size of the HTTP response header. BOOL bResult = ::WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX); if (bResult || (!bResult && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER))) { wchar_t *szHeader = new wchar_t[dwSize]; if (szHeader != NULL) { memset(szHeader, 0, dwSize* sizeof(wchar_t)); // Get HTTP response header. if (::WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, szHeader, &dwSize, WINHTTP_NO_HEADER_INDEX)) { _wcslwr_s(szHeader, wcslen(szHeader)+1); wstring lwrHeader = szHeader; // get headers to our structure later unsigned int iMaxBufferSize = INT_BUFFERSIZE; // Should proabbly be setting buffer size autoit side somehow unsigned int iCurrentBufferSize = 0; BYTE* m_pResponse = NULL; m_pResponse = new BYTE[iMaxBufferSize]; memset(m_pResponse, 0, iMaxBufferSize); do { dwSize = 0; if (::WinHttpQueryDataAvailable(hRequest, &dwSize)) { BYTE *pResponse = new BYTE[dwSize + 1]; if (pResponse != NULL) { memset(pResponse, 0, dwSize); DWORD dwRead = 0; if (::WinHttpReadData(hRequest, pResponse, dwSize, &dwRead)) { if (dwRead + iCurrentBufferSize > iMaxBufferSize) { BYTE *pOldBuffer = m_pResponse; m_pResponse = new BYTE[iMaxBufferSize * 2]; if (m_pResponse == NULL) { m_pResponse = pOldBuffer; bRetVal = false; break; } iMaxBufferSize *= 2; memset(m_pResponse, 0, iMaxBufferSize); memcpy(m_pResponse, pOldBuffer, iCurrentBufferSize); delete[] pOldBuffer; } memcpy(m_pResponse + iCurrentBufferSize, pResponse, dwRead); iCurrentBufferSize += dwRead; } delete[] pResponse; } } } while (dwSize > 0); final=clock()-init; TotalTime = (double)final / ((double)CLOCKS_PER_SEC); // Return anything regardless, unless it took too long if (TotalTime < tData->MaxTestTime) { bGetReponseSucceed = true; int p=0; while (p < sizeof(m_pResponse)) { if(m_pResponse[p] != NULL) tData->ResponceHTML[p]=m_pResponse[p]; // write chars to our autoit char array so we can process it over at scriptside p++; } swprintf(tData->ResponceHeaders, INT_BUFFERSIZE, L"%s" ,lwrHeader); tData->Latency = TotalTime; } } } } } } ::WinHttpCloseHandle(hRequest); } ::WinHttpCloseHandle(hConnect); } ::WinHttpCloseHandle(hSession); return 1; } // End of _WinHTTP_Process
Everything looked like it should work right but obviously something is not going over as planned...
I create the thread like so~
Local $ContentType = _ "Connection: keep-alive" & @CRLF & _ "Cache-Control: no-cache" & @CRLF & _ "Pragma: no-cache" & @CRLF & _ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" & @CRLF & _ "Accept-Language: en-US,en;q=0.8" & @CRLF & _ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3" & @CRLF $tURL_COMPONENTS = DllStructCreate($tagTHREAD_PARAMETERS) DllStructSetData($tURL_COMPONENTS, "UserAgent", "(COMPATABLE; AutoIt/v3)") DllStructSetData($tURL_COMPONENTS, "HTTPVerb", "GET") DllStructSetData($tURL_COMPONENTS, "Host", "www.google.com") DllStructSetData($tURL_COMPONENTS, "Resource", "test") DllStructSetData($tURL_COMPONENTS, "Port", $INTERNET_DEFAULT_HTTPS_PORT) DllStructSetData($tURL_COMPONENTS, "Referer", "") DllStructSetData($tURL_COMPONENTS, "Headers", $ContentType) DllStructSetData($tURL_COMPONENTS, "ExtraData", 0) DllStructSetData($tURL_COMPONENTS, "Length", 0) DllStructSetData($tURL_COMPONENTS, "TotalLength", 0) DllStructSetData($tURL_COMPONENTS, "dwResolveTimeout", 30000) DllStructSetData($tURL_COMPONENTS, "dwConnectTimeout", 30000) DllStructSetData($tURL_COMPONENTS, "dwSendTimeout", 30000) ; 15 seconds DllStructSetData($tURL_COMPONENTS, "dwReceiveTimeout", 30000) If $Proxy Then DllStructSetData($tURL_COMPONENTS, "Proxy", $Proxy) DllStructSetData($tURL_COMPONENTS, "ProxyFlags", $WINHTTP_ACCESS_TYPE_NAMED_PROXY) Else DllStructSetData($tURL_COMPONENTS, "ProxyFlags", $WINHTTP_ACCESS_TYPE_NO_PROXY) EndIf DllStructSetData($tURL_COMPONENTS, "SendFlags", BitOR($WINHTTP_FLAG_SECURE, $WINHTTP_FLAG_ESCAPE_DISABLE)) ;DllStructSetData($tURL_COMPONENTS, "RetryTimes", 2); unused now DllStructSetData($tURL_COMPONENTS, "MaxTestTime", 60) $hThread = CreateThread(DllStructGetPtr($tURL_COMPONENTS)); dllcall() that will pass the structure pointer to the dll as "ptr"
then after creating the thread, the app idles around and waits for the thread to report it terminated.
Once the thread has finished doing its thing, it will write whatever it got my my BYTE array and return it.
The problem comes when I try to access it now...
DllStructGetData() will return the binary equivalent of "<!Doc" and then the rest will be zeros, garbage and totally not what I was expecting..
Any ideas?
I have included a ready to compile project with a script to test the DLL in the release directory.
Full Project.zip 31.86KB
5 downloads