dcsimg
Results 1 to 13 of 13
  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Aug 2016
    Posts
    244

    Question wininet asynchronous download

    Because of Dragokas the thread, I checked my hard disk and found asynchronous code downloads about wininet. I feel that there is too little information here and I hope someone can improve. At the same time the search forum found this thread, the lesson can help

    http://www.vbforums.com/showthread.p...eaded-Callback


    NOW ,i fixed,may work ok?


    Code:
    'Constants - InternetOpen.dwFlags
    Private Const INTERNET_FLAG_ASYNC = 1 '&H10000000-------------i searched ,somebody  change to 1 , work ok! i donot konw why
    Attached Files Attached Files
    Last edited by xxdoc123; Jun 14th, 2018 at 08:21 AM.

  2. #2
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,967

    Re: wininet asynchronous download

    I don't have an answer, but just a brief check of that other thread, shows that at the time Lavolpe didn't quite understand that any other callback thread initiated by another method/api call (in this case wininet) needs the runtime initiated on the thread before it can use any runtime functions, or error checking, or even declared APIs (not in a typelib).
    You can't even use (shared) variables or data outside the local (stack based) ones within the function, unless you use synchronization or marshalling/serializing or IPC.

    Things to keep in mind if you are servicing a callback on a thread not created by yourself.

    The first order in servicing a callback from on another thread should be getting the VB6 runtime initiated.
    Either that or you can keep the actual callback functionality very minimal, and use typelib APIs to either post messages back to the main thread (this is what i do) or use synchronization to access global variables.
    Last edited by DEXWERX; Jun 12th, 2018 at 07:19 AM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  3. #3
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    not sure how to do it in native vb due to the threading issues.

    I have an example on how to do it using a C dll in another thread.
    The C thread has access to some VB variable addresses which it writes to directly (no sync) and seems to work.

    Maybe worth mentioning but there is a way to do async downloads natively in VB using the asyncread method of a user control

    https://github.com/dzzie/pdfstreamdu...ncDownload.ctl

  4. #4
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,967

    Re: wininet asynchronous download

    Quote Originally Posted by dz32 View Post
    The C thread has access to some VB variable addresses which it writes to directly (no sync) and seems to work.
    I guess technically that wouldn't cause an automatic crash... assuming you only read the data from VB, and write from C.

    You just have no guarantee when the data is updated. But you can try coordinate with a PostMessage back to the main thread.
    Trying to figure out, how to make that work is making my head hurt... I think when it comes to threading, I'll just go by the book and use proper sync.
    Last edited by DEXWERX; Jun 12th, 2018 at 03:46 PM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  5. #5
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    Yeah thats what i was experimenting with and it seems to be stable. The async C code from another thread writes to some vb variable memory addresses such as download size and download progress directly, and then reads from an abort variable that only vb sets.

    as far as I can see there is never a conflict, values will never stomp on each other as its only one way data exchange and the data is not critical
    (download progress only) so there is never a real out of date issue.

    The only danger should be if the variables memory addresses go out of scope, so just have to make sure they are declared private at the form level*, or the calling function has a wait loop and to make sure abort is set on form close or function exit if download is not complete.

    (*public variables at the form level seem to actually be COM property get/sets and not direct variable access so cant use as say a copymemory arg)

    The way I usual do it is to use postmessage from the C thread to the vb window hwnd, but then I have to include subclassing code. The windows message que handles the thread stuff so the vb code handles it when its ready, it works out really nice but again more code.

    Actually another alternative I have used before to bypass subclassing is to have the alt thread (or external process) set the text of a vb textbox using postmessage and WM_PASTE to its hwnd. Then in your vb code you can use the text1_change event handler to be automatically notified of the new data received. (with the caveat of process levels if applicable)
    Attached Files Attached Files
    Last edited by dz32; Jun 12th, 2018 at 05:43 PM.

  6. #6
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,967

    Re: wininet asynchronous download

    as far as VB6 on x86 is concerned, Integer writes are atomic. I would just never do it

    As for subclassing, I made a crashproof subclasing dll, so it's as convenient as checking off a reference.
    I get people don't like 3rd party dependencies (myself included),
    But since know what's in it, I'm happy to use it myself.
    Last edited by DEXWERX; Jun 12th, 2018 at 05:23 PM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  7. #7
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    right cant blame ya one bit. I havent used it except as experiment.

    Where it counts use known safe and tested techniques.
    PostMessage is the way to go for stability and robustness.

  8. #8

    Thread Starter
    Addicted Member
    Join Date
    Aug 2016
    Posts
    244

    Re: wininet asynchronous download

    Code:
    class CHttpClient
    {
    public:
    	CHttpClient(void);
    	~CHttpClient(void);
    public:
    	BOOL Open(LPCTSTR lpUrl,int timeout = INFINITE);
    	int Read(unsigned char* buffer,int size,int timeout = INFINITE);
    	void Close();
    };
    Code:
    int main(int argc, char *argv[])
    {
    	CHttpClient httpClient;
    	BOOL bRet = httpClient.Open("http://192.168.169.200/stream.php?cam=c0a86b8500",5000);
    	if(!bRet)
    	{
    		return 0;
    	}
    	unsigned char buffer[4096] = {0};
    	while(1)
    	{
    		int len = httpClient.Read(buffer,sizeof(buffer));
    		if(len <= 0)
    		{
    			break;
    		}
    	}
    	httpClient.Close();
    	return 1;
    }
    Code:
    /********************************************************************  
    filename:   HttpClient.h
    created:    2016-04-08
    author:     firehood  
    purpose:    A Asynchronous Http Client By Using WinInet HTTP functions
    *********************************************************************/
    #pragma once
    #include <windows.h>
    #include <wininet.h>
    
    class CHttpClient
    {
    public:
    	CHttpClient(void);
    	~CHttpClient(void);
    public:
    	BOOL Open(LPCTSTR lpUrl,int timeout = INFINITE);
    	int Read(unsigned char* buffer,int size,int timeout = INFINITE);
    	void Close();
    private:
    	static void CALLBACK HttpStatusCallback(
    		HINTERNET hInternet,
    		DWORD dwContext,
    		DWORD dwInternetStatus,
    		LPVOID lpStatusInfo,
    		DWORD dwStatusInfoLen);
    private:
    	HINTERNET  m_hInternet;
    	HINTERNET  m_hSession;
    	HANDLE  m_hRequestOpenedEvent;
    	HANDLE  m_hRequestCompleteEvent;
    	DWORD   m_dwCompleteResult;
    };
    
    CHttpClient.cpp
    
    /********************************************************************  
    filename:   HttpClient.cpp
    created:    2016-04-08
    author:     firehood  
    purpose:    A Asynchronous Http Client By Using WinInet HTTP functions
    *********************************************************************/
    #include "HttpClient.h"
    #include <tchar.h>
    #pragma comment(lib,"wininet.lib")
    
    CHttpClient::CHttpClient(void)
    : m_hInternet(NULL)
    , m_hSession(NULL)
    , m_dwCompleteResult(0)
    {
    	m_hRequestOpenedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    	m_hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    }
    
    CHttpClient::~CHttpClient(void)
    {
    	Close();
    	if(m_hRequestOpenedEvent)
    	{
    		CloseHandle(m_hRequestOpenedEvent);
    		m_hRequestOpenedEvent = NULL;
    	}
    	if(m_hRequestCompleteEvent)
    	{
    		CloseHandle(m_hRequestCompleteEvent);
    		m_hRequestCompleteEvent = NULL;
    	}
    }
    
    BOOL CHttpClient::Open(LPCTSTR lpUrl,int timeout)
    {
    	if(lpUrl == NULL)
    	{
    		return FALSE;
    	}
    
    	if(m_hInternet)
    	{
    		Close();
    	}
    	BOOL bRet = FALSE;
    	do
    	{
    		m_hInternet = InternetOpen(NULL,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC);
    
    		if (m_hInternet == NULL)
    		{
    			break;
    		}
    
    		// Setup callback function
    		if (InternetSetStatusCallback(m_hInternet,HttpStatusCallback) == INTERNET_INVALID_STATUS_CALLBACK)
    		{
    			break;
    		}
    
    		m_hSession = InternetOpenUrl(m_hInternet,lpUrl,NULL,NULL,INTERNET_FLAG_NO_CACHE_WRITE,(DWORD_PTR)this);
    		if(NULL == m_hSession)
    		{
    			if (GetLastError() != ERROR_IO_PENDING)
    			{
    				break;
    			}
    			// Wait until we get the connection handle
    			if(WaitForSingleObject(m_hRequestOpenedEvent,timeout) == WAIT_TIMEOUT)
    			{
    				break;
    			}
    		}
    
    		if(WaitForSingleObject(m_hRequestCompleteEvent,timeout) == WAIT_TIMEOUT)
    		{
    			break;
    		}
    
    		if(m_dwCompleteResult == 0)
    		{
    			break;
    		}
    
    		DWORD dwStatusCode;
    		TCHAR responseText[256] = {0};
    		DWORD responseTextSize = sizeof(responseText);
    		if(!HttpQueryInfo(m_hSession,HTTP_QUERY_STATUS_CODE,&responseText,&responseTextSize,NULL))
    		{
    			break;
    		}
    		dwStatusCode = _ttoi(responseText);
    		if(dwStatusCode != HTTP_STATUS_OK )
    		{
    			break;
    		}
    		bRet = TRUE;
    	}while(0);
    
    	if(!bRet)
    	{
    		if(m_hSession)
    		{
    			InternetCloseHandle(m_hSession);
    			m_hSession = NULL;
    		}
    		if(m_hInternet)
    		{
    			InternetSetStatusCallback(m_hInternet, NULL);
    			InternetCloseHandle(m_hInternet);
    			m_hInternet = NULL;
    		}
    	}
    	return bRet;
    }
    
    int CHttpClient::Read(unsigned char* buffer,int size,int timeout)
    {
    	if(buffer == NULL || size <= 0)
    	{
    		return -1;
    	}
    	INTERNET_BUFFERS InetBuff;
    	memset(&InetBuff, 0, sizeof(InetBuff));
    	InetBuff.dwStructSize = sizeof(InetBuff);
    	InetBuff.lpvBuffer = buffer;
    	InetBuff.dwBufferLength = size;
    
    	if(!InternetReadFileEx(m_hSession,&InetBuff,0,(DWORD_PTR)this))
    	{
    		if (GetLastError() == ERROR_IO_PENDING)
    		{
    			if(WaitForSingleObject(m_hRequestCompleteEvent,timeout) == WAIT_TIMEOUT)
    			{
    				return -1;
    			}
    		}
    		else
    		{
    			return -1;
    		}
    	}
    	return InetBuff.dwBufferLength;
    }
    
    void CHttpClient::Close()
    {
    	SetEvent(m_hRequestOpenedEvent);
    	SetEvent(m_hRequestCompleteEvent);
    	if(m_hSession)
    	{
    		InternetCloseHandle(m_hSession);
    		m_hSession = NULL;
    	}
    	if(m_hInternet)
    	{
    		InternetSetStatusCallback(m_hInternet, NULL);
    		InternetCloseHandle(m_hInternet);
    		m_hInternet = NULL;
    	}
    	ResetEvent(m_hRequestOpenedEvent);
    	ResetEvent(m_hRequestCompleteEvent);
    }
    
    void CALLBACK CHttpClient::HttpStatusCallback(
    						HINTERNET hInternet,
    						DWORD dwContext,
    						DWORD dwInternetStatus,
    						LPVOID lpStatusInfo,
    						DWORD dwStatusInfoLen)
    {
    	CHttpClient *p = (CHttpClient*)dwContext;
    
    	switch(dwInternetStatus)
    	{
    	case INTERNET_STATUS_HANDLE_CREATED:
    		{
    			INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
    			p->m_hSession = (HINTERNET)pRes->dwResult;
    			SetEvent(p->m_hRequestOpenedEvent);
    		}
    		break;
    	case INTERNET_STATUS_REQUEST_COMPLETE:
    		{
    			INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
    			p->m_dwCompleteResult = pRes->dwResult;
    			SetEvent(p->m_hRequestCompleteEvent);
    		}
    		break;
    	case INTERNET_STATUS_HANDLE_CLOSING:
    		break;
    	case INTERNET_STATUS_RESPONSE_RECEIVED:
    		break;
    	default:
    		break;
    	}
    }
    I found this code on the Internet, it looks nothing special, why can not be translated into vb

  9. #9
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    I tested the updated example and added in some more debug messages.

    It does seem stable both in exe and IDE.

    using GetCurrentThreadID it shows that code hitting the callback is the same as the main vb calling thread.

    Adding another downloader class instance and starting both from command1_click,
    command1_click starts
    download1 runs until completion with call backs first
    then it starts the call to download 2
    then command1_click event completes.

    Name:  screen.png
Views: 65
Size:  19.9 KB

    the doevents call in the callback does allow the UI to keep updated and a timer clock was able
    to keep running.

    I do receive hangs in the UI sometimes probably where the wininet api is blocking internally on its own.
    but this does generally prevent the UI from hanging during the download unlike say UrlDownloadToFile

    The next level which could be obtained would be a background downloader running its own thread.

    In this scenario the calling function would return immediately, and then the background downloader would just notify the main window with things like instance id, download progress, and error or complete messages. Any api delays would not trigger hangs in the main UI and instead be contained invisibly in their own thread.
    Attached Files Attached Files
    Last edited by dz32; Jun 13th, 2018 at 04:03 AM.

  10. #10

  11. #11
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    just to test the direct memory access code a little deeper I modified the c example dll i gave earlier so that
    it now can handle multiple async background downloads each in their own thread.

    The var writes are to private class variables and the class has a check on terminate to
    auto abort and wait until the downloader thread exits to prevent any crashs.

    the main form occasionally polls the class for current download status on a timer
    still seems ok in testing
    Attached Files Attached Files
    Last edited by dz32; Jun 13th, 2018 at 06:23 PM.

  12. #12

    Thread Starter
    Addicted Member
    Join Date
    Aug 2016
    Posts
    244

    Re: wininet asynchronous download

    Quote Originally Posted by dz32 View Post
    just to test the direct memory access code a little deeper I modified the c example dll i gave earlier so that
    it now can handle multiple async background downloads each in their own thread.

    The var writes are to private class variables and the class has a check on terminate to
    auto abort and wait until the downloader thread exits to prevent any crashs.

    the main form occasionally polls the class for current download status on a timer
    still seems ok in testing
    Name:  error1.jpg
Views: 43
Size:  22.1 KB

    i used Dev-Cpp compilation have a error.

    cfg->hOpen = InternetOpen("WininetDl", INTERNET_OPEN_TYPE_PRECONFIG, NULL,NULL, 0 );

    C:\Users\xxdoc\Desktop\C_VB_Background_Download\collect2.exe [Error] ld returned 1 exit status

  13. #13
    Addicted Member
    Join Date
    Jun 2015
    Posts
    220

    Re: wininet asynchronous download

    Sorry not familiar with that compiler. I use vs 2008 and it compiled ok for me. they all have slight differences in what they accept :-\

    Any updates to this code I will put in a git repo here:

    https://github.com/dzzie/libs/tree/m...round_Download

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width


×
We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.