michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #undef WINVER michael@0: #undef _WIN32_WINNT michael@0: #define WINVER 0x602 michael@0: #define _WIN32_WINNT 0x602 michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #define INITGUID michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: // Indicates that an application supports dual desktop and immersive modes. In Windows 8, this property is only applicable for web browsers. michael@0: //DEFINE_PROPERTYKEY(PKEY_AppUserModel_IsDualMode, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 11); michael@0: michael@0: void DumpParameters(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aAppModelID, LPCWSTR aDescription) michael@0: { michael@0: if (aTargetPath) michael@0: wprintf(L"target path: '%s'\n", aTargetPath); michael@0: if (aShortcutPath) michael@0: wprintf(L"shortcut path: '%s'\n", aShortcutPath); michael@0: if (aAppModelID) michael@0: wprintf(L"app id: '%s'\n", aAppModelID); michael@0: if (aDescription) michael@0: wprintf(L"description: '%s'\n", aDescription); michael@0: } michael@0: michael@0: HRESULT michael@0: SetShortcutProps(LPCWSTR aShortcutPath, LPCWSTR aAppModelID, bool aSetID, bool aSetMode) michael@0: { michael@0: HRESULT hres; michael@0: ::CoInitialize(nullptr); michael@0: michael@0: IPropertyStore *m_pps = nullptr; michael@0: if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath, michael@0: nullptr, michael@0: GPS_READWRITE, michael@0: IID_PPV_ARGS(&m_pps)))) { michael@0: printf("SHGetPropertyStoreFromParsingName failed\n"); michael@0: goto Exit; michael@0: } michael@0: michael@0: if (aSetMode) { michael@0: PROPVARIANT propvar; michael@0: if (FAILED(hres = InitPropVariantFromBoolean(true, &propvar)) || michael@0: FAILED(hres = m_pps->SetValue(PKEY_AppUserModel_IsDualMode, propvar))) { michael@0: goto Exit; michael@0: } michael@0: PropVariantClear(&propvar); michael@0: } michael@0: michael@0: if (aSetID && aAppModelID) { michael@0: PROPVARIANT propvar; michael@0: if (FAILED(hres = InitPropVariantFromString(aAppModelID, &propvar)) || michael@0: FAILED(hres = m_pps->SetValue(PKEY_AppUserModel_ID, propvar))) { michael@0: goto Exit; michael@0: } michael@0: PropVariantClear(&propvar); michael@0: } michael@0: michael@0: hres = m_pps->Commit(); michael@0: michael@0: Exit: michael@0: michael@0: if (m_pps) { michael@0: m_pps->Release(); michael@0: } michael@0: michael@0: CoUninitialize(); michael@0: return hres; michael@0: } michael@0: michael@0: HRESULT michael@0: PrintShortcutProps(LPCWSTR aTargetPath) michael@0: { michael@0: HRESULT hres; michael@0: ::CoInitialize(nullptr); michael@0: michael@0: IPropertyStore *m_pps = nullptr; michael@0: if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath, michael@0: nullptr, michael@0: GPS_READWRITE, michael@0: IID_PPV_ARGS(&m_pps)))) { michael@0: printf("SHGetPropertyStoreFromParsingName failed\n"); michael@0: goto Exit; michael@0: } michael@0: michael@0: bool found = false; michael@0: michael@0: PROPVARIANT propvar; michael@0: if (SUCCEEDED(hres = m_pps->GetValue(PKEY_AppUserModel_IsDualMode, &propvar)) && propvar.vt == VT_BOOL && propvar.boolVal == -1) { michael@0: printf("PKEY_AppUserModel_IsDualMode found\n"); michael@0: PropVariantClear(&propvar); michael@0: found = true; michael@0: } michael@0: michael@0: if (SUCCEEDED(hres = m_pps->GetValue(PKEY_AppUserModel_ID, &propvar)) && propvar.pwszVal) { michael@0: printf("PKEY_AppUserModel_ID found "); michael@0: wprintf(L"value: '%s'\n", propvar.pwszVal); michael@0: PropVariantClear(&propvar); michael@0: found = true; michael@0: } michael@0: michael@0: if (!found) { michael@0: printf("no known properties found.\n"); michael@0: } michael@0: michael@0: Exit: michael@0: michael@0: if (m_pps) { michael@0: m_pps->Release(); michael@0: } michael@0: michael@0: CoUninitialize(); michael@0: return hres; michael@0: } michael@0: michael@0: HRESULT michael@0: CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription) michael@0: { michael@0: HRESULT hres; michael@0: IShellLink* psl; michael@0: michael@0: wprintf(L"creating shortcut: '%s'\n", aShortcutPath); michael@0: michael@0: CoInitialize(nullptr); michael@0: michael@0: hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, michael@0: IID_IShellLink, (LPVOID*)&psl); michael@0: if (FAILED(hres)) { michael@0: CoUninitialize(); michael@0: return hres; michael@0: } michael@0: psl->SetPath(aTargetPath); michael@0: if (aDescription) { michael@0: psl->SetDescription(aDescription); michael@0: } else { michael@0: psl->SetDescription(L""); michael@0: } michael@0: michael@0: IPersistFile* ppf = nullptr; michael@0: hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); michael@0: michael@0: if (SUCCEEDED(hres)) { michael@0: hres = ppf->Save(aShortcutPath, TRUE); michael@0: ppf->Release(); michael@0: } michael@0: psl->Release(); michael@0: CoUninitialize(); michael@0: return hres; michael@0: } michael@0: michael@0: void DumpCommands() michael@0: { michael@0: printf("control options:\n"); michael@0: printf(" /CREATE create a shortcut for the target file.\n"); michael@0: printf(" /UPDATE update properties on the target file.\n"); michael@0: printf(" /PRINT print the known properties set on the target file.\n"); michael@0: printf("parameters:\n"); michael@0: printf(" /T(path) the full path and filename of the target file.\n"); michael@0: printf(" /S(path) with CREATE, the full path and filename of the shortcut to create.\n"); michael@0: printf(" /D(string) with CREATE, adds a description to the shortcut.\n"); michael@0: printf(" /A(id) the app model id to assign to the shortcut or target file.\n"); michael@0: printf(" /M enable support for dual desktop and immersive modes on the shortcut or target file.\n"); michael@0: } michael@0: michael@0: int wmain(int argc, WCHAR* argv[]) michael@0: { michael@0: WCHAR shortcutPathStr[MAX_PATH]; michael@0: WCHAR targetPathStr[MAX_PATH]; michael@0: WCHAR appModelIDStr[MAX_PATH]; michael@0: WCHAR descriptionStr[MAX_PATH]; michael@0: michael@0: shortcutPathStr[0] = '\0'; michael@0: targetPathStr[0] = '\0'; michael@0: appModelIDStr[0] = '\0'; michael@0: descriptionStr[0] = '\0'; michael@0: michael@0: bool createShortcutFound = false; michael@0: bool updateFound = false; michael@0: bool shortcutPathFound = false; michael@0: bool targetPathFound = false; michael@0: bool appModelIDFound = false; michael@0: bool modeFound = false; michael@0: bool descriptionFound = false; michael@0: bool printFound = false; michael@0: michael@0: int idx; michael@0: for (idx = 1; idx < argc; idx++) { michael@0: if (!wcscmp(L"/CREATE", argv[idx])) { michael@0: createShortcutFound = true; michael@0: continue; michael@0: } michael@0: if (!wcscmp(L"/UPDATE", argv[idx])) { michael@0: updateFound = true; michael@0: continue; michael@0: } michael@0: if (!wcscmp(L"/PRINT", argv[idx])) { michael@0: printFound = true; michael@0: continue; michael@0: } michael@0: michael@0: if (!wcsncmp(L"/S", argv[idx], 2) && wcslen(argv[idx]) > 2) { michael@0: wcscpy_s(shortcutPathStr, MAX_PATH, (argv[idx]+2)); michael@0: shortcutPathFound = true; michael@0: continue; michael@0: } michael@0: if (!wcsncmp(L"/T", argv[idx], 2) && wcslen(argv[idx]) > 2) { michael@0: wcscpy_s(targetPathStr, MAX_PATH, (argv[idx]+2)); michael@0: targetPathFound = true; michael@0: continue; michael@0: } michael@0: if (!wcsncmp(L"/A", argv[idx], 2) && wcslen(argv[idx]) > 2) { michael@0: wcscpy_s(appModelIDStr, MAX_PATH, (argv[idx]+2)); michael@0: appModelIDFound = true; michael@0: continue; michael@0: } michael@0: if (!wcsncmp(L"/D", argv[idx], 2) && wcslen(argv[idx]) > 2 && wcslen(argv[idx]) < MAX_PATH) { michael@0: wcscpy_s(descriptionStr, MAX_PATH, (argv[idx]+2)); michael@0: descriptionFound = true; michael@0: continue; michael@0: } michael@0: if (!wcscmp(L"/M", argv[idx])) { michael@0: modeFound = true; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: DumpParameters(targetPathStr, shortcutPathStr, appModelIDStr, descriptionStr); michael@0: michael@0: if (!createShortcutFound && !updateFound && !printFound) { michael@0: DumpCommands(); michael@0: return 0; michael@0: } michael@0: michael@0: if (!targetPathFound) { michael@0: printf("missing target file path.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: HRESULT hres; michael@0: michael@0: if (printFound) { michael@0: if (FAILED(hres = PrintShortcutProps(targetPathStr))) { michael@0: printf("failed printing target props HRESULT=%X\n", hres); michael@0: return -1; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: if (createShortcutFound && !shortcutPathFound) { michael@0: printf("missing shortcut file path.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: if (updateFound && !appModelIDFound && !modeFound) { michael@0: printf("no properties selected.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: if (createShortcutFound) { michael@0: if (FAILED(hres = CreateLink(targetPathStr, michael@0: shortcutPathStr, michael@0: (descriptionFound ? descriptionStr : nullptr)))) { michael@0: printf("failed creating shortcut HRESULT=%X\n", hres); michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: LPCWSTR target; michael@0: if (createShortcutFound) { michael@0: target = shortcutPathStr; michael@0: } else { michael@0: target = targetPathStr; michael@0: } michael@0: michael@0: if (appModelIDFound || modeFound) { michael@0: if (FAILED(hres = SetShortcutProps(target, michael@0: (appModelIDFound ? appModelIDStr : nullptr), michael@0: appModelIDFound, modeFound))) { michael@0: printf("failed adding property HRESULT=%X\n", hres); michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: