How to use the URLDownloadToFile() Function???

closed account (3hMz8vqX)
Hi All,

I am a beginner in WINAPI C++ and I like to download files from the internet using
the URLDownloadToFile() function

I did all the syntax and stuff correctly . . .
I use Orwell Dev-C++ TDM GCC
Im using Windows 7 Ultimate 32 bit . . .

But I get the following error when I compile my project
C:\Users\user\Documents\CC++\main.o main.cpp:(.text+0x3e): undefined reference to `URLDownloadToFileA@20'

And I cannot find liburlmon.a ???

Please Please help me . . .
Thankyou everyone in advance!!!


closed account (Dy7SLyTq)
are you linking right?
Out of interest, is the header urlmon.h provided with your GCC? Or have you obtained it from elsewhere?

I have GCC 4.7.2 from the main MinGW release (http://sourceforge.net/projects/mingw/ ) and it has neither the urlmon.h header nor the import library.

If you can't find a suitable import library, you could dynamically link instead. You only need the single function for the basic use of the URLDownloadToFile function.

Andy

PS If you want to receive progress messages while downloading a file with URLDownloadToFile, it's rather more compicated!
Last edited on
I have got a small program to link by using the libmon header and import lib from the Windows SDK (version 7.0).

But it did involve a bit of hacking, mostly to #define away all the SAL annotations littering urlmon.h. I also had to provide an empty msxml.h file and #define a few pretend types to make the compiler happy (types I would not be using, so any old definition was ok).

After that, I think dynamic linking would be both easier and rather tidier!

Andy

SAL Annotations
http://msdn.microsoft.com/en-us/library/ms235402%28v=vs.100%29.aspx

I copied urlmon.h and urlmon.lib from the Windows SDK into a directory along with:
a. main.cpp (see below)
b. an empty file named msxml.h
c. hack.h (see below)

And then built main.cpp using g++ and a regular Windows command prompt, with the MinGW tools on the path, using:

g++ -ggdb -O2 -fomit-frame-pointer -std=c++11 -Wall -pedantic -U __STRICT_ANSI__
-DNDEBUG -o url_test.exe main.cpp -lwininet -luuid urlmon.lib

(but without wrapping; that was done to make it display a bit better here.)

There were a couple of warnings about unknown #pragma's, but other than that it built ok, to url_test.exe. And ran! (It downloads a copy of the working draft of the C++ standard to the folder C:\Test, which is assumed to exist.)

Where main.cpp is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>

#include <iostream> // must be included before hack.h as SAL annotations
#include <iomanip>  // collide with GCC's standard library implmentation

#include "hack.h"   // contains #defines of SAL annotations (to nothing)
                    // plus a few pretend types

#include "urlmon.h" // copied into project folder (from Windows SDK)
                    // along with the import library and an empty "msxml.h" file

#include <wininet.h> // MinGW does provide this (needed for DeleteUrlCacheEntry)

using namespace std;

#ifdef _UNICODE
#define tcout wcout
#else
#define tcout cout
#endif

int main()
{
    const TCHAR url[] = _T("http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf");
    const TCHAR filePath[] = _T("C:\\Test\\n3337.pdf");

    tcout << _T("Downloading   : ") << url      << endl;
    tcout << _T("To local file : ") << filePath << endl;

    // invalidate cache, so file is always downloaded from web site
    // (if not called, the file will be retieved from the cache if
    // it's already been downloaded.)
    DeleteUrlCacheEntry(url);

    HRESULT hr = URLDownloadToFile(
        NULL,   // A pointer to the controlling IUnknown interface (not needed here)
        url,
        filePath,
        0,      // Reserved. Must be set to 0.
        NULL ); // status callback interface (not needed for basic use)
    if(SUCCEEDED(hr))
    {
        tcout << _T("Downloaded OK") << endl;
    }
    else
    {
        tcout << _T("An error occured : error code = 0x") << hex << hr << endl;
    }

    return 0;
}


and hack.h is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// some of these are defined in MinGw's specstrings.h, but not all...

#define uCLSSPEC     void*
#define QUERYCONTEXT void*
struct IXMLElement
{
    void* unused;
};

#define __in
#define __in_opt
#define __out
#define __out_opt
#define __inout_opt
#define __inout
#define __deref_inout
#define __out_ecount_part(c, p)
#define __in_bcount_opt(c)
#define __out_bcount_part(c, p)
#define __reserved
#define __deref_out
#define __deref_out_opt
#define __out_ecount(c)

#define __out_bcount_part_opt(c, p)
#define __in_range(a, b)

#define __RPC__in
#define __RPC__in_opt
#define __RPC__out
#define __RPC__out_opt
#define __RPC__inout
#define __RPC__inout_opt
#define __RPC_unique_pointer
#define __RPC__deref_out_opt
#define __RPC__deref_out_ecount_full_opt(c)
#define __RPC__in_ecount_full(c)
#define __RPC__out_ecount_full(c)
#define __RPC__in_xcount(c)
#define __RPC__inout_xcount(c)
#define __RPC__inout_ecount_full(c)
#define __RPC__out_ecount(c) 

Last edited on
And this version of main.cpp show what you need to do to receive status feedback (as it's a console app, I use the status info to update an ASCII progress bar...)

The URLDownloadToFile function appears to have been designed to work with ActiveX controls (i.e. COM objects), so that's the way the callback mechanism works. Might not be so easy to understand if you don't already know COM.

Component Object Model
http://en.wikipedia.org/wiki/Component_Object_Model

Andy

C:\cplusplus\url_tool>url_test.exe
Downloading   : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
To local file : C:\Test\n3337.pdf
Finding resource...
Connecting...
Sending request...
Mime type available
Begin download
Cache filename available
100% [====================]
End download
Downloaded OK


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>

#include <iostream> // must be included before hack.h as SAL annotations
#include <iomanip>  // collide with GCC's standard library implmentation

#include "hack.h"   // contains #defines of SAL annotations (to nothing)
                    // plus

#include "urlmon.h" // copied into project folder (from Windows SDK)
                    // along with an empty "msxml.h" file

#include <wininet.h> // MinGW does provide this (needed for DeleteUrlCacheEntry)

using namespace std;

#ifdef _UNICODE
#define tcout wcout
#else
#define tcout cout
#endif

// Adapted from:
// Creating a progress bar in C/C++ (or any other console app.)
// http://www.rosshemsley.co.uk/2011/02/creating-a-progress-bar-in-c-or-any-other-console-app/
void LoadBar(unsigned curr_val, unsigned max_val, unsigned bar_width = 20)
{
    if((curr_val != max_val) && (curr_val % (max_val / 100) != 0))
        return;

    double   ratio   =  curr_val / (double)max_val;
    unsigned bar_now =  (unsigned)(ratio * bar_width);

    tcout << _T("\r") << setw(3) << (unsigned)(ratio * 100.0) << _T("% [");
    for(unsigned curr_val = 0; curr_val < bar_now; ++curr_val)
        tcout << _T("=");
    for(unsigned curr_val = bar_now; curr_val < bar_width; ++curr_val)
        tcout << _T(" ");
    tcout << _T("]") << flush;
}

class CallbackHandler : public IBindStatusCallback
{
private:
    int m_percentLast;

public:
    CallbackHandler() : m_percentLast(0)
    {
    }

    // IUnknown

    HRESULT STDMETHODCALLTYPE
    QueryInterface(REFIID riid, void** ppvObject)
    {

        if(    IsEqualIID(IID_IBindStatusCallback, riid)
            || IsEqualIID(IID_IUnknown, riid) )
        {
            *ppvObject = reinterpret_cast<void*>(this);
            return S_OK;
        }

        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE
    AddRef()
    {
        return 2UL;
    }

    ULONG STDMETHODCALLTYPE
    Release()
    {
        return 1UL;
    }

    // IBindStatusCallback

    HRESULT STDMETHODCALLTYPE
    OnStartBinding(DWORD     /*dwReserved*/,
                   IBinding* /*pib*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    GetPriority(LONG* /*pnPriority*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    OnLowResource(DWORD /*reserved*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    OnProgress(ULONG   ulProgress,
               ULONG   ulProgressMax,
               ULONG   ulStatusCode,
               LPCWSTR /*szStatusText*/)
    {
        switch(ulStatusCode)
        {
            case BINDSTATUS_FINDINGRESOURCE:
                tcout << _T("Finding resource...") << endl;
            break;
            case BINDSTATUS_CONNECTING:
                tcout << _T("Connecting...") << endl;
            break;
            case BINDSTATUS_SENDINGREQUEST:
                tcout << _T("Sending request...") << endl;
            break;
            case BINDSTATUS_MIMETYPEAVAILABLE:
                tcout << _T("Mime type available") << endl;
            break;
            case BINDSTATUS_CACHEFILENAMEAVAILABLE:
                tcout << _T("Cache filename available") << endl;
            break;
            case BINDSTATUS_BEGINDOWNLOADDATA:
                tcout << _T("Begin download") << endl;
            break;
            case BINDSTATUS_DOWNLOADINGDATA:
            case BINDSTATUS_ENDDOWNLOADDATA:
            {
                int percent = (int)(100.0 * static_cast<double>(ulProgress)
                                / static_cast<double>(ulProgressMax));
                if(m_percentLast < percent)
                {
                    LoadBar(percent, 100);
                    m_percentLast = percent;
                }
                if(ulStatusCode == BINDSTATUS_ENDDOWNLOADDATA)
                {
                    tcout << endl << _T("End download") << endl;
                }
            }
            break;

            default:
            {
                tcout << _T("Status code : ") << ulStatusCode << endl;
            }
        }
        // The download can be cancelled by returning E_ABORT here
        // of from any other of the methods.
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE
    OnStopBinding(HRESULT /*hresult*/,
                  LPCWSTR /*szError*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    GetBindInfo(DWORD*    /*grfBINDF*/,
                BINDINFO* /*pbindinfo*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    OnDataAvailable(DWORD      /*grfBSCF*/,
                    DWORD      /*dwSize*/,
                    FORMATETC* /*pformatetc*/,
                    STGMEDIUM* /*pstgmed*/)
    {
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE
    OnObjectAvailable(REFIID    /*riid*/,
                      IUnknown* /*punk*/)
    {
        return E_NOTIMPL;
    }
};

int main()
{
    const TCHAR url[] = _T("http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf");
    const TCHAR filePath[] = _T("C:\\Test\\n3337.pdf");

    tcout << _T("Downloading   : ") << url      << endl;
    tcout << _T("To local file : ") << filePath << endl;

    // invalidate cache, so file is always downloaded from web site
    // (if not called, the file will be retieved from the cache if
    // it's already been downloaded.)
    DeleteUrlCacheEntry(url);

    CallbackHandler callbackHandler;
    IBindStatusCallback* pBindStatusCallback = NULL;
    callbackHandler.QueryInterface(IID_IBindStatusCallback,
                                   reinterpret_cast<void**>(&pBindStatusCallback));

    HRESULT hr = URLDownloadToFile(
        NULL,   // A pointer to the controlling IUnknown interface
        url,
        filePath,
        0,      // Reserved. Must be set to 0.
        pBindStatusCallback );
    if(SUCCEEDED(hr))
    {
        tcout << _T("Downloaded OK") << endl;
    }
    else
    {
        tcout << _T("An error occured : error code = 0x") << hex << hr << endl;
    }

    // should usually call Release on a COM object, but this one (callbackHandler)
    // was created on the stack so is going out of scope now and will die anyway.

    return 0;
}
Last edited on
Topic archived. No new replies allowed.