intshcut.c 14.8 KB
Newer Older
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
/*
 * Unit tests to document InternetShortcut's behaviour
 *
 * Copyright 2008 Damjan Jovanovic
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <stdio.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winerror.h"

#include "shlobj.h"
#include "shobjidl.h"
#include "shlguid.h"
#include "ole2.h"
35
#include "mshtml.h"
36 37 38 39 40 41
#include "initguid.h"
#include "isguids.h"
#include "intshcut.h"

#include "wine/test.h"

42
static HRESULT WINAPI Unknown_QueryInterface(IUnknown *pUnknown, REFIID riid, void **ppvObject)
43 44 45 46 47 48 49 50 51
{
    if (IsEqualGUID(&IID_IUnknown, riid))
    {
        *ppvObject = pUnknown;
        return S_OK;
    }
    return E_NOINTERFACE;
}

52
static ULONG WINAPI Unknown_AddRef(IUnknown *pUnknown)
53 54 55 56
{
    return 2;
}

57
static ULONG WINAPI Unknown_Release(IUnknown *pUnknown)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
{
    return 1;
}

static IUnknownVtbl unknownVtbl = {
    Unknown_QueryInterface,
    Unknown_AddRef,
    Unknown_Release
};

static IUnknown unknown = {
    &unknownVtbl
};

static void test_Aggregability(void)
{
    HRESULT hr;
    IUnknown *pUnknown = NULL;

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUnknown, (void**)&pUnknown);
78
    ok(hr == S_OK, "could not create instance of CLSID_InternetShortcut with IID_IUnknown, hr = 0x%x\n", hr);
79 80 81 82
    if (pUnknown)
        IUnknown_Release(pUnknown);

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&pUnknown);
83
    ok(hr == S_OK, "could not create instance of CLSID_InternetShortcut with IID_IUniformResourceLocatorA, hr = 0x%x\n", hr);
84 85 86 87
    if (pUnknown)
        IUnknown_Release(pUnknown);

    hr = CoCreateInstance(&CLSID_InternetShortcut, &unknown, CLSCTX_ALL, &IID_IUnknown, (void**)&pUnknown);
88
    ok(hr == CLASS_E_NOAGGREGATION, "aggregation didn't fail like it should, hr = 0x%x\n", hr);
89 90 91 92 93 94 95 96 97
    if (pUnknown)
        IUnknown_Release(pUnknown);
}

static void can_query_interface(IUnknown *pUnknown, REFIID riid)
{
    HRESULT hr;
    IUnknown *newInterface;
    hr = IUnknown_QueryInterface(pUnknown, riid, (void**)&newInterface);
98
    ok(hr == S_OK, "interface %s could not be queried\n", wine_dbgstr_guid(riid));
99 100 101 102 103 104 105 106 107 108
    if (SUCCEEDED(hr))
        IUnknown_Release(newInterface);
}

static void test_QueryInterface(void)
{
    HRESULT hr;
    IUnknown *pUnknown;

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUnknown, (void**)&pUnknown);
109 110 111 112 113 114
    ok(hr == S_OK, "Could not create InternetShortcut object: %08x\n", hr);

    can_query_interface(pUnknown, &IID_IUniformResourceLocatorA);
    can_query_interface(pUnknown, &IID_IUniformResourceLocatorW);
    can_query_interface(pUnknown, &IID_IPersistFile);
    IUnknown_Release(pUnknown);
115 116
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
#define test_shortcut_url(a,b) _test_shortcut_url(__LINE__,a,b)
static void _test_shortcut_url(unsigned line, IUnknown *unk, const char *exurl)
{
    IUniformResourceLocatorA *locator_a;
    char *url_a;
    HRESULT hres;

    hres = IUnknown_QueryInterface(unk, &IID_IUniformResourceLocatorA, (void**)&locator_a);
    ok_(__FILE__,line)(hres == S_OK, "Could not get IUniformResourceLocatorA iface: %08x\n", hres);

    hres = locator_a->lpVtbl->GetURL(locator_a, &url_a);
    ok_(__FILE__,line)(hres == S_OK, "GetURL failed: %08x\n", hres);
    ok_(__FILE__,line)(!strcmp(url_a, exurl), "unexpected URL, got %s, expected %s\n", url_a, exurl);
    CoTaskMemFree(url_a);

132
    IUnknown_Release((IUnknown*)locator_a);
133 134
}

135 136 137
#define check_string_transform(a,b,c,d,e) _check_string_transform(__LINE__,a,b,c,d,e)
static void _check_string_transform(unsigned line, IUniformResourceLocatorA *urlA, LPCSTR input, DWORD flags,
        LPCSTR expectedOutput, BOOL is_todo)
138
{
139
    CHAR *output;
140
    HRESULT hr;
141

142
    hr = urlA->lpVtbl->SetURL(urlA, input, flags);
143 144 145
    ok_(__FILE__,line)(hr == S_OK, "SetUrl failed, hr=0x%x\n", hr);
    if (FAILED(hr))
        return;
146

147 148 149
    output = (void*)0xdeadbeef;
    hr = urlA->lpVtbl->GetURL(urlA, &output);
    if(expectedOutput) {
150
        todo_wine_if(is_todo) {
151 152 153 154
            ok_(__FILE__,line)(hr == S_OK, "GetUrl failed, hr=0x%x\n", hr);
        }
        todo_wine
        ok_(__FILE__,line)(!lstrcmpA(output, expectedOutput), "unexpected URL change %s -> %s (expected %s)\n",
155
            input, output, expectedOutput);
156 157 158 159 160
    }else {
        todo_wine
        ok_(__FILE__,line)(hr == S_FALSE, "GetUrl failed, hr=0x%x\n", hr);
        todo_wine
        ok_(__FILE__,line)(!output, "GetUrl returned %s\n", output);
161
    }
162 163
    if (hr == S_OK)
        CoTaskMemFree(output);
164 165
}

166 167
static void test_ReadAndWriteProperties(void)
{
168 169
    int iconIndex = 7;
    PROPSPEC ps[2];
170 171
    HRESULT hr;
    IUniformResourceLocatorA *urlA;
172
    IUniformResourceLocatorA *urlAFromFile;
173
    WCHAR fileNameW[MAX_PATH];
174

175
    static const WCHAR shortcutW[] = {'t','e','s','t','s','h','o','r','t','c','u','t','.','u','r','l',0};
176 177
    WCHAR iconPath[] = {'f','i','l','e',':','/','/','/','C',':','/','a','r','b','i','t','r','a','r','y','/','i','c','o','n','/','p','a','t','h',0};
    char testurl[] = "http://some/bogus/url.html";
178

179
    ps[0].ulKind = PRSPEC_PROPID;
180
    U(ps[0]).propid = PID_IS_ICONFILE;
181
    ps[1].ulKind = PRSPEC_PROPID;
182
    U(ps[1]).propid = PID_IS_ICONINDEX;
183

184 185 186 187
    /* Make sure we have a valid temporary directory */
    GetTempPathW(MAX_PATH, fileNameW);
    lstrcatW(fileNameW, shortcutW);

188
    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&urlA);
189
    ok(hr == S_OK, "Could not create CLSID_InternetShortcut instance: %08x\n", hr);
190 191
    if (hr == S_OK)
    {
192
        IPersistFile *pf;
193
        IPropertyStorage *pPropStgWrite;
194
        IPropertySetStorage *pPropSetStg;
195
        PROPVARIANT pv[2];
196 197 198 199 200 201 202 203 204 205 206 207 208 209

        /* We need to set a URL -- IPersistFile refuses to save without one. */
        hr = urlA->lpVtbl->SetURL(urlA, testurl, 0);
        ok(hr == S_OK, "Failed to set a URL.  hr=0x%x\n", hr);

        /* Write this shortcut out to a file so that we can test reading it in again. */
        hr = urlA->lpVtbl->QueryInterface(urlA, &IID_IPersistFile, (void **) &pf);
        ok(hr == S_OK, "Failed to get the IPersistFile for writing.  hr=0x%x\n", hr);

        hr = IPersistFile_Save(pf, fileNameW, TRUE);
        ok(hr == S_OK, "Failed to save via IPersistFile. hr=0x%x\n", hr);

        IPersistFile_Release(pf);

210
        pv[0].vt = VT_LPWSTR;
211
        U(pv[0]).pwszVal = (void *) iconPath;
212
        pv[1].vt = VT_I4;
213
        U(pv[1]).lVal = iconIndex;
214 215 216 217 218 219 220 221 222 223 224 225 226
        hr = urlA->lpVtbl->QueryInterface(urlA, &IID_IPropertySetStorage, (void **) &pPropSetStg);
        ok(hr == S_OK, "Unable to get an IPropertySetStorage, hr=0x%x\n", hr);

        hr = IPropertySetStorage_Open(pPropSetStg, &FMTID_Intshcut, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStgWrite);
        ok(hr == S_OK, "Unable to get an IPropertyStorage for writing, hr=0x%x\n", hr);

        hr = IPropertyStorage_WriteMultiple(pPropStgWrite, 2, ps, pv, 0);
        ok(hr == S_OK, "Unable to set properties, hr=0x%x\n", hr);

        hr = IPropertyStorage_Commit(pPropStgWrite, STGC_DEFAULT);
        ok(hr == S_OK, "Failed to commit properties, hr=0x%x\n", hr);

        pPropStgWrite->lpVtbl->Release(pPropStgWrite);
227 228 229 230 231
        urlA->lpVtbl->Release(urlA);
        IPropertySetStorage_Release(pPropSetStg);
    }

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&urlAFromFile);
232
    ok(hr == S_OK, "Could not create CLSID_InternetShortcut instance: %08x\n", hr);
233 234 235 236 237 238 239 240 241
    if (hr == S_OK)
    {
        IPropertySetStorage *pPropSetStg;
        IPropertyStorage *pPropStgRead;
        PROPVARIANT pvread[2];
        IPersistFile *pf;
        LPSTR url = NULL;

        /* Now read that .url file back in. */
242
        hr = urlAFromFile->lpVtbl->QueryInterface(urlAFromFile, &IID_IPersistFile, (void **) &pf);
243 244 245 246 247 248 249
        ok(hr == S_OK, "Failed to get the IPersistFile for reading.  hr=0x%x\n", hr);

        hr = IPersistFile_Load(pf, fileNameW, 0);
        ok(hr == S_OK, "Failed to load via IPersistFile. hr=0x%x\n", hr);
        IPersistFile_Release(pf);


250
        hr = urlAFromFile->lpVtbl->GetURL(urlAFromFile, &url);
251
        ok(hr == S_OK, "Unable to get url from file, hr=0x%x\n", hr);
252
        ok(lstrcmpA(url, testurl) == 0, "Wrong url read from file: %s\n",url);
253
        CoTaskMemFree(url);
254

255
        hr = urlAFromFile->lpVtbl->QueryInterface(urlAFromFile, &IID_IPropertySetStorage, (void **) &pPropSetStg);
256 257
        ok(hr == S_OK, "Unable to get an IPropertySetStorage, hr=0x%x\n", hr);

258 259 260
        hr = IPropertySetStorage_Open(pPropSetStg, &FMTID_Intshcut, STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropStgRead);
        ok(hr == S_OK, "Unable to get an IPropertyStorage for reading, hr=0x%x\n", hr);

261
        memset(pvread, 0, sizeof(pvread));
262
        hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread);
263 264
    todo_wine /* Wine doesn't yet support setting properties after save */
    {
265
        ok(hr == S_OK, "Unable to read properties, hr=0x%x\n", hr);
266 267 268 269 270
        ok(pvread[1].vt == VT_I4, "got %d\n", pvread[1].vt);
        ok(U(pvread[1]).lVal == iconIndex, "Read wrong icon index: %d\n", U(pvread[1]).iVal);
        ok(pvread[0].vt == VT_LPWSTR, "got %d\n", pvread[0].vt);
        ok(lstrcmpW(U(pvread[0]).pwszVal, iconPath) == 0, "Wrong icon path read: %s\n", wine_dbgstr_w(U(pvread[0]).pwszVal));
    }
271 272
        PropVariantClear(&pvread[0]);
        PropVariantClear(&pvread[1]);
273 274
        IPropertyStorage_Release(pPropStgRead);
        IPropertySetStorage_Release(pPropSetStg);
275
        urlAFromFile->lpVtbl->Release(urlAFromFile);
276
        DeleteFileW(fileNameW);
277 278 279
    }
}

280 281
static void test_NullURLs(void)
{
282
    LPSTR url = NULL;
283 284 285 286
    HRESULT hr;
    IUniformResourceLocatorA *urlA;

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&urlA);
287
    ok(hr == S_OK, "Could not create InternetShortcut object: %08x\n", hr);
288

289
    hr = urlA->lpVtbl->GetURL(urlA, &url);
290
    ok(hr == S_FALSE, "getting uninitialized URL unexpectedly failed, hr=0x%x\n", hr);
291
    ok(url == NULL, "uninitialized URL is not NULL but %s\n", url);
292

293
    hr = urlA->lpVtbl->SetURL(urlA, NULL, 0);
294
    ok(hr == S_OK, "setting NULL URL unexpectedly failed, hr=0x%x\n", hr);
295

296
    hr = urlA->lpVtbl->GetURL(urlA, &url);
297
    ok(hr == S_FALSE, "getting NULL URL unexpectedly failed, hr=0x%x\n", hr);
298
    ok(url == NULL, "URL unexpectedly not NULL but %s\n", url);
299

300
    urlA->lpVtbl->Release(urlA);
301 302
}

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
typedef struct {
    const char *data;
    const char *url;
} load_test_t;

static const load_test_t load_tests[] = {
    {"[InternetShortcut]\n"
     "URL=http://www.winehq.org/\n"
     "HotKey=0\n"
     "IDList=\n"
     "[{000214A0-0000-0000-C000-000000000046}]\n"
     "Prop0=1,2\n",

     "http://www.winehq.org/"
    }
};

static void test_Load(void)
{
    IPersistFile *persist_file;
    const load_test_t *test;
    WCHAR file_path[MAX_PATH];
    DWORD size;
    HANDLE file;
    HRESULT hres;

    static const WCHAR test_urlW[] = {'t','e','s','t','.','u','r','l',0};

    GetTempPathW(MAX_PATH, file_path);
    lstrcatW(file_path, test_urlW);

334
    for(test = load_tests; test < load_tests + ARRAY_SIZE(load_tests); test++) {
335 336 337 338 339
        IPropertySetStorage *propsetstorage;
        IPropertyStorage *propstorage;
        PROPVARIANT v;
        PROPSPEC ps;

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
        file = CreateFileW(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        ok(file != INVALID_HANDLE_VALUE, "could not create test file\n");
        if(file == INVALID_HANDLE_VALUE)
            continue;

        WriteFile(file, test->data, strlen(test->data), &size, NULL);
        CloseHandle(file);

        hres = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IPersistFile, (void**)&persist_file);
        ok(hres == S_OK, "Could not create InternetShortcut instance: %08x\n", hres);

        hres = IPersistFile_Load(persist_file, file_path, 0);
        ok(hres == S_OK, "Load failed: %08x\n", hres);

        test_shortcut_url((IUnknown*)persist_file, test->url);

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
        hres = IPersistFile_QueryInterface(persist_file, &IID_IPropertySetStorage, (void **)&propsetstorage);
        ok(hres == S_OK, "Unable to get an IPropertySetStorage, hr=0x%x\n", hres);

        hres = IPropertySetStorage_Open(propsetstorage, &FMTID_Intshcut, STGM_READ | STGM_SHARE_EXCLUSIVE, &propstorage);
        ok(hres == S_OK, "Unable to get an IPropertyStorage for reading, hr=0x%x\n", hres);

        ps.ulKind = PRSPEC_PROPID;
        U(ps).propid = PID_IS_ICONFILE;
        v.vt = VT_NULL;
        hres = IPropertyStorage_ReadMultiple(propstorage, 1, &ps, &v);
        ok(hres == S_FALSE, "got 0x%08x\n", hres);
        ok(v.vt == VT_EMPTY, "got %d\n", v.vt);

        ps.ulKind = PRSPEC_PROPID;
        U(ps).propid = PID_IS_ICONINDEX;
        v.vt = VT_EMPTY;
        hres = IPropertyStorage_ReadMultiple(propstorage, 1, &ps, &v);
        ok(hres == S_FALSE, "got 0x%08x\n", hres);
        ok(v.vt == VT_EMPTY, "got %d\n", v.vt);

        IPropertyStorage_Release(propstorage);
        IPropertySetStorage_Release(propsetstorage);

379 380 381 382 383
        IPersistFile_Release(persist_file);
        DeleteFileW(file_path);
    }
}

384 385 386 387 388 389
static void test_SetURLFlags(void)
{
    HRESULT hr;
    IUniformResourceLocatorA *urlA;

    hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&urlA);
390
    ok(hr == S_OK, "Could not create InternetShortcut object: %08x\n", hr);
391

392 393
    check_string_transform(urlA, "somerandomstring", 0, NULL, TRUE);
    check_string_transform(urlA, "www.winehq.org", 0, NULL, TRUE);
394

395 396
    check_string_transform(urlA, "www.winehq.org", IURL_SETURL_FL_GUESS_PROTOCOL, "http://www.winehq.org/", FALSE);
    check_string_transform(urlA, "ftp.winehq.org", IURL_SETURL_FL_GUESS_PROTOCOL, "ftp://ftp.winehq.org/", FALSE);
397 398

    urlA->lpVtbl->Release(urlA);
399 400 401 402
}

static void test_InternetShortcut(void)
{
403 404 405 406
    IUniformResourceLocatorA *url;
    HRESULT hres;

    hres = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL, &IID_IUniformResourceLocatorA, (void**)&url);
407 408
    ok(hres == S_OK, "Could not create CLSID_InternetShortcut instance: %08x\n", hres);
    if(FAILED(hres))
409
        return;
410
    url->lpVtbl->Release(url);
411 412 413 414 415 416

    test_Aggregability();
    test_QueryInterface();
    test_NullURLs();
    test_SetURLFlags();
    test_ReadAndWriteProperties();
417
    test_Load();
418 419 420 421 422
}

START_TEST(intshcut)
{
    OleInitialize(NULL);
423

424
    test_InternetShortcut();
425

426 427
    OleUninitialize();
}