[win] support file assoc for win8 and above

This commit is contained in:
SimplestStudio
2025-04-22 16:49:21 +03:00
parent 9006935a60
commit f8665c6880
7 changed files with 694 additions and 3 deletions

View File

@ -48,9 +48,19 @@ build_xp {
DESTDIR = $$DESTDIR/xp
DEFINES += __OS_WIN_XP
} else {
HEADERS += $$PWD/src/jumplist.h
SOURCES += $$PWD/src/jumplist.cpp
HEADERS += \
$$PWD/src/jumplist.h \
$$PWD/src/shellassoc.h
SOURCES += \
$$PWD/src/jumplist.cpp \
$$PWD/src/shellassoc.cpp
LIBS += -lshlwapi \
-lshell32 \
-ladvapi32 \
-lcrypt32 \
-lRpcrt4 \
-lole32
}

View File

@ -36,6 +36,7 @@
#include <Windows.h>
#ifndef __OS_WIN_XP
# include "jumplist.h"
# include "shellassoc.h"
# include <shlobj_core.h>
#endif
@ -75,6 +76,40 @@ int main(int argc, char *argv[])
ClearHistory();
DeleteJumpList();
return 0;
} else
if (_cmdArgs.contains("--assoc")) {
if (_cmdArgs.size() > 1) {
std::wstring assocLine = _cmdArgs.at(1).toStdWString();
size_t len = assocLine.length();
if (len == 0)
return 0;
std::vector<AssocPair> assocList;
wchar_t *buf = &assocLine[0];
if (buf[len - 1] == ';')
buf[len - 1] = '\0';
size_t last_sep_pos = 0;
wchar_t *it = buf;
while (1) {
while (*it != '\0' && *it != ';')
it++;
wchar_t tmp = *it;
*it = '\0';
wchar_t *pair = buf + last_sep_pos;
if (wchar_t *colon = wcschr(pair, L':')) {
*colon = L'\0';
assocList.push_back({pair, colon + 1});
}
if (tmp == '\0')
break;
last_sep_pos = it - buf + 1;
it++;
}
if (!assocList.empty())
SetUserFileAssoc(assocList.data(), assocList.size());
}
return 0;
}
#endif

View File

@ -0,0 +1,594 @@
#include "shellassoc.h"
#include <Windows.h>
#include <Shlobj.h>
#include <shlwapi.h>
#include <sddl.h>
#include <wincrypt.h>
#include <cstdint>
#include <string>
#define MD5_HASH_LEN 16
#define TIMESTAMP_LEN 16
#define STRING_BUFFER 1024
static const char REG_URL_IE_ASSOC[] = "SOFTWARE\\Microsoft\\Internet Explorer\\Capabilities\\UrlAssociations";
static const char REG_EXT_USER_ASSOC[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s\\UserChoice";
static const char REG_URL_USER_ASSOC[] = "SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\%s\\UserChoice";
static const char REG_STARTMENU_INET[] = "SOFTWARE\\Clients\\StartMenuInternet";
static const char EDGE_UWP_LEGACY_ID[] = "AppXq0fevzme2pys62n3e0fbqa7peapykr8v";
static const char EDGE_UWP_MODERN_ID[] = "AppX90nv6nhay5n6a98fnetv7tpk64pp35es";
static const char EDGE_UWP_EXE_PATH[] = "C:\\Windows\\SystemApps\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\MicrosoftEdge.exe";
static const uint32_t HASH_V1[] = {
0x69FB0000, 0x13DB0000, 0x10FA9605, 0x79F8A395,
0x689B6B9F, 0xEA970001, 0x3C101569, 0x3CE8EC25,
0x59C3AF2D, 0x2232E0F1, 0x1EC90001, 0x35BD1EC9
};
static const uint32_t HASH_V2[] = {
0xB1110000, 0x30674EEF, 0x5B9F0000, 0x78F7A461,
0x12CEB96D, 0x46930000, 0x1D830000, 0x257E1D83,
0x16F50000, 0x5D8BE90B, 0x96FF0000, 0x2C7C6901,
0x2B890000, 0x7C932B89, 0x9F690000, 0x405B6097
};
static const uint8_t ASSOC_DESCRIPTOR[] = {
0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x68, 0x6F,
0x69, 0x63, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
0x76, 0x69, 0x61, 0x20, 0x77, 0x69, 0x6E, 0x64,
0x6F, 0x77, 0x73, 0x20, 0x75, 0x73, 0x65, 0x72,
0x20, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x65,
0x6E, 0x63, 0x65, 0x20, 0x7B, 0x64, 0x31, 0x38,
0x62, 0x36, 0x64, 0x64, 0x35, 0x2D, 0x36, 0x31,
0x32, 0x34, 0x2D, 0x34, 0x33, 0x34, 0x31, 0x2D,
0x39, 0x33, 0x31, 0x38, 0x2D, 0x38, 0x30, 0x34,
0x30, 0x30, 0x33, 0x62, 0x61, 0x66, 0x61, 0x30,
0x62, 0x7D
};
static const char HTA_BEGIN[] = R"(
<!DOCTYPE html><html><head><title>ShellAssoc</title>
<HTA:APPLICATION ID="oHTA"
APPLICATIONNAME="%s"
BORDER="none"
CAPTION="no"
SHOWINTASKBAR="no"
SINGLEINSTANCE="yes"
SYSMENU="no"
WINDOWSTATE="minimize">
<script language="VBScript">
Sub Window_OnLoad
Set WshShell = CreateObject("WScript.Shell")
On Error Resume Next
)";
static const char HTA_WRITE[] = R"(
WshShell.RegWrite "HKCU\%s\Hash", "%s", "REG_SZ"
WshShell.RegWrite "HKCU\%s\ProgId", "%s", "REG_SZ"
)";
static const char HTA_DELETE[] = R"(
WshShell.RegDelete "HKCU\%s\"
)";
static const char HTA_END[] = R"(
Set WshShell = Nothing
self.close
End Sub
</script>
</head><body></body></html>
)";
enum WinVer {
Undef, WinXP, WinVista, Win7, Win8, Win8_1,
Win10UpTo1607, Win10Above1607, Win11, WinFuture
};
int getWindowsVersion()
{
static int winVer = Undef;
if (winVer != Undef)
return winVer;
if (HMODULE module = GetModuleHandleA("ntdll")) {
NTSTATUS(WINAPI *_RtlGetVersion)(LPOSVERSIONINFOEXW);
*(FARPROC*)&_RtlGetVersion = GetProcAddress(module, "RtlGetVersion");
OSVERSIONINFOEXW os = {0};
os.dwOSVersionInfoSize = sizeof(os);
if (_RtlGetVersion && _RtlGetVersion(&os) == 0) {
const DWORD major = os.dwMajorVersion;
const DWORD minor = os.dwMinorVersion;
const DWORD build = os.dwBuildNumber;
winVer =
major == 5L && (minor == 1L || minor == 2L) ? WinXP :
major == 6L && minor == 0L ? WinVista :
major == 6L && minor == 1L ? Win7 :
major == 6L && minor == 2L ? Win8 :
major == 6L && minor == 3L ? Win8_1 :
major == 10L && minor == 0L && build <= 14393 ? Win10UpTo1607 :
major == 10L && minor == 0L && build < 22000 ? Win10Above1607 :
major == 10L && minor == 0L && build >= 22000 ? Win11 :
major == 10L && minor > 0L ? WinFuture :
major > 10L ? WinFuture : Undef;
}
}
return winVer;
}
std::string GetCurrentUserSidString()
{
static std::string user_sid;
if (!user_sid.empty())
return user_sid;
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return user_sid;
DWORD tokenLen = 0, tokenInfoLen;
GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenLen);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || tokenLen == 0) {
CloseHandle(hToken);
return user_sid;
}
PTOKEN_USER pTokenUser = (PTOKEN_USER)LocalAlloc(LMEM_ZEROINIT, tokenLen);
if (!pTokenUser) {
CloseHandle(hToken);
return user_sid;
}
tokenInfoLen = tokenLen;
if (GetTokenInformation(hToken, TokenUser, pTokenUser, tokenInfoLen, &tokenLen)) {
LPSTR sidStr = NULL;
if (ConvertSidToStringSidA(pTokenUser->User.Sid, &sidStr)) {
user_sid.assign(sidStr);
LocalFree(sidStr);
}
}
LocalFree(pTokenUser);
CloseHandle(hToken);
return user_sid;
}
std::string GetRegistryKeyTimestampHex(LPCSTR lpSubKey)
{
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, lpSubKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
return "";
}
FILETIME ftLastWriteTime;
if (RegQueryInfoKeyA(hKey, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &ftLastWriteTime) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return "";
}
SYSTEMTIME sysTime;
FileTimeToSystemTime(&ftLastWriteTime, &sysTime);
sysTime.wMilliseconds = 0;
sysTime.wSecond = 0;
SystemTimeToFileTime(&sysTime, &ftLastWriteTime);
std::string timestamp(TIMESTAMP_LEN, '\0');
snprintf(&timestamp[0], TIMESTAMP_LEN + 1, "%08x%08x", (uint32_t)ftLastWriteTime.dwHighDateTime, (uint32_t)ftLastWriteTime.dwLowDateTime);
RegCloseKey(hKey);
return timestamp;
}
bool FindBrowserCommandByProtocol(HKEY hKey, LPCSTR protocol, LPCSTR progId, DWORD nBufferLen, LPSTR lpBuffer)
{
HKEY phkResult;
if (RegOpenKeyExA(hKey, REG_STARTMENU_INET, 0, KEY_READ, &phkResult) != ERROR_SUCCESS)
return false;
DWORD cSubKeys = 0;
if (RegQueryInfoKeyA(phkResult, NULL, NULL, NULL, &cSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
RegCloseKey(phkResult);
return false;
}
for (DWORD i = 0; i < cSubKeys; ++i) {
char pName[MAX_PATH];
DWORD cchName = _countof(pName);
if (RegEnumKeyExA(phkResult, i, pName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
continue;
char assocKeyPath[MAX_PATH];
if (_stricmp(pName, "IEXPLORE.EXE") == 0) {
strcpy_s(assocKeyPath, REG_URL_IE_ASSOC);
} else {
snprintf(assocKeyPath, MAX_PATH, "%s\\%s\\Capabilities\\URLAssociations", REG_STARTMENU_INET, pName);
}
HKEY assocKey;
if (RegOpenKeyExA(hKey, assocKeyPath, 0, KEY_READ, &assocKey) != ERROR_SUCCESS)
continue;
BYTE pData[MAX_PATH];
DWORD cbData = sizeof(pData);
if (RegQueryValueExA(assocKey, protocol, NULL, NULL, pData, &cbData) == ERROR_SUCCESS) {
if (_stricmp((const char*)pData, progId) == 0) {
char cmdKeyPath[MAX_PATH];
snprintf(cmdKeyPath, MAX_PATH, "%s\\%s\\shell\\open\\command", REG_STARTMENU_INET, pName);
HKEY cmdKey;
if (RegOpenKeyExA(hKey, cmdKeyPath, 0, KEY_READ, &cmdKey) == ERROR_SUCCESS) {
cbData = sizeof(pData);
if (RegQueryValueExA(cmdKey, NULL, NULL, NULL, pData, &cbData) == ERROR_SUCCESS) {
const char* command = (const char*)pData;
const char* quoted = strchr(command, '"');
if (quoted)
command = strtok((char*)pData, "\"");
strcpy_s(lpBuffer, nBufferLen, command);
RegCloseKey(cmdKey);
RegCloseKey(assocKey);
RegCloseKey(phkResult);
return true;
}
RegCloseKey(cmdKey);
}
}
}
RegCloseKey(assocKey);
}
RegCloseKey(phkResult);
return false;
}
bool IsPdfOrHttpScheme(const char *str)
{
static const char *exts[] = {".pdf", "http", "https"};
for (size_t i = 0; i < sizeof(exts)/sizeof(exts[0]); ++i) {
if (_stricmp(str, exts[i]) == 0)
return 1;
}
return 0;
}
bool CalcMD5Hash(const void *src, size_t size, uint32_t md5[2])
{
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
DWORD hashLen = MD5_HASH_LEN;
BYTE hash[MD5_HASH_LEN];
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
return false;
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
CryptReleaseContext(hProv, 0);
return false;
}
if (!CryptHashData(hHash, (const BYTE*)src, (DWORD)size, 0)) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return false;
}
if (!CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0)) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return false;
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
memcpy(md5, hash, MD5_HASH_LEN);
return true;
}
bool CalcCustomHash_V1(uint32_t *data, uint32_t count, const uint32_t key[2], uint32_t out[2])
{
if (count < 2 || count % 2 != 0)
return false;
count /= 2;
const uint32_t k0 = (key[0] | 1) + HASH_V1[0];
const uint32_t k1 = (key[1] | 1) + HASH_V1[1];
uint32_t res1 = 0;
uint32_t res2 = 0;
uint32_t index = 0;
while (count--) {
uint32_t r1 = res1 + data[index++];
uint32_t r2_0 = k0 * r1 - HASH_V1[2] * HIWORD(r1);
uint32_t r2_1 = HASH_V1[3] * r2_0 + HASH_V1[4] * HIWORD(r2_0);
uint32_t r3 = HASH_V1[5] * r2_1 - HASH_V1[6] * HIWORD(r2_1);
uint32_t r4_0 = r3 + data[index++];
uint32_t r5_0 = k1 * r4_0 - HASH_V1[7] * HIWORD(r4_0);
uint32_t r5_1 = HASH_V1[8] * r5_0 - HASH_V1[9] * HIWORD(r5_0);
res1 = HASH_V1[10] * r5_1 + HASH_V1[11] * HIWORD(r5_1);
res2 += res1 + r3;
}
out[0] = res1;
out[1] = res2;
return true;
}
bool CalcCustomHash_V2(uint32_t *data, uint32_t count, const uint32_t key[2], uint32_t out[2])
{
if (count < 2 || count % 2 != 0)
return false;
count /= 2;
const uint32_t k0 = key[0] | 1;
const uint32_t k1 = key[1] | 1;
uint32_t res1 = 0;
uint32_t res2 = 0;
uint32_t index = 0;
while (count--) {
uint32_t r1_0 = k0 * (res1 + data[index++]);
uint32_t r1_1 = HASH_V2[0] * r1_0 - HASH_V2[1] * HIWORD(r1_0);
uint32_t r2_0 = HASH_V2[2] * r1_1 - HASH_V2[3] * HIWORD(r1_1);
uint32_t r2_1 = HASH_V2[4] * HIWORD(r2_0) - HASH_V2[5] * r2_0;
uint32_t r3 = HASH_V2[6] * r2_1 + HASH_V2[7] * HIWORD(r2_1);
uint32_t r4_0 = k1 * (r3 + data[index++]);
uint32_t r4_1 = HASH_V2[8] * r4_0 - HASH_V2[9] * HIWORD(r4_0);
uint32_t r5_0 = HASH_V2[10] * r4_1 - HASH_V2[11] * HIWORD(r4_1);
uint32_t r5_1 = HASH_V2[12] * r5_0 + HASH_V2[13] * HIWORD(r5_0);
res1 = HASH_V2[14] * r5_1 - HASH_V2[15] * HIWORD(r5_1);
res2 += res1 + r3;
}
out[0] = res1;
out[1] = res2;
return true;
}
bool Base64Encode(const BYTE *pData, DWORD cbData, std::string &base64Str)
{
DWORD cchSize = 0;
if (!CryptBinaryToStringA(pData, cbData, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &cchSize))
return false;
base64Str.resize(cchSize - 1, '\0');
return CryptBinaryToStringA(pData, cbData, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, &base64Str[0], &cchSize);
}
bool CreateRegWriteHTA(const char *fileName, const char *regPath, const char *hash, const char *progId)
{
FILE *f;
errno_t err = fopen_s(&f, fileName, "w");
if (err != 0) {
return false;
}
fprintf(f, HTA_BEGIN, "WriteToRegistry");
fprintf(f, HTA_WRITE, regPath, hash, regPath, progId);
fputs(HTA_END, f);
return fclose(f) == 0;
}
bool CreateRegDeleteHTA(const char *fileName, const char *regPath)
{
FILE *f;
errno_t err = fopen_s(&f, fileName, "w");
if (err != 0) {
return false;
}
fprintf(f, HTA_BEGIN, "DeleteRegistryKey");
fprintf(f, HTA_DELETE, regPath);
fputs(HTA_END, f);
return fclose(f) == 0;
}
bool RunHTAScript(const char *filePath)
{
char pCmd[MAX_PATH];
PROCESS_INFORMATION pi;
STARTUPINFOA si;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(STARTUPINFOA);
si.dwFlags = STARTF_USESHOWWINDOW;
snprintf(pCmd, MAX_PATH, "mshta.exe \"%s\"", filePath);
if (!CreateProcessA(NULL, pCmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
return false;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
return CloseHandle(pi.hThread);
}
bool GenerateFilePathHTA(DWORD nBufferLen, LPSTR lpBuffer)
{
char tempPath[MAX_PATH];
DWORD ret = GetTempPathA(MAX_PATH, tempPath);
if (ret == 0 || ret > MAX_PATH)
return false;
UUID uuid;
RPC_CSTR wszUuid = NULL;
if (UuidCreate(&uuid) != RPC_S_OK || UuidToStringA(&uuid, &wszUuid) != RPC_S_OK)
return false;
const char *uid = (const char*)wszUuid;
snprintf(lpBuffer, nBufferLen, "%sfta_%s.hta", tempPath, uid);
RpcStringFreeA(&wszUuid);
return true;
}
bool RunWriteAssocWithHTA(const char *regPath, const char *hash, const char *progId)
{
char tempFile[MAX_PATH];
if (!GenerateFilePathHTA(MAX_PATH, tempFile))
return false;
CreateRegWriteHTA(tempFile, regPath, hash, progId);
RunHTAScript(tempFile);
return DeleteFileA(tempFile);
}
bool RunDeleteAssocWithHTA(const char *regPath)
{
char tempFile[MAX_PATH];
if (!GenerateFilePathHTA(MAX_PATH, tempFile))
return false;
CreateRegDeleteHTA(tempFile, regPath);
RunHTAScript(tempFile);
return DeleteFileA(tempFile);
}
bool SetAssocWithHash(const char *ext, const char *progID, const char *subkey)
{
int winVer = getWindowsVersion();
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, subkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
RegCloseKey(hKey);
if (!IsPdfOrHttpScheme(ext))
goto LABEL_DEL_ORDIN;
if (winVer > Win8_1) {
RunDeleteAssocWithHTA(subkey);
goto LABEL_CREATE;
}
LABEL_DEL_ORDIN:
if (RegDeleteKeyA(HKEY_CURRENT_USER, subkey) != ERROR_SUCCESS) {
printf("Could not delete registry key %s\n", subkey);
return false;
}
}
LABEL_CREATE:
if (RegCreateKeyExA(HKEY_CURRENT_USER, subkey, 0, NULL, 0, KEY_WRITE, 0, &hKey, 0) != ERROR_SUCCESS) {
printf("Could not create registry key %s\n", subkey);
return false;
}
RegCloseKey(hKey);
if (winVer < Win8) {
if (RegOpenKeyExA(HKEY_CURRENT_USER, subkey, 0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) {
return false;
}
if (RegSetValueExA(hKey, "ProgId", 0, REG_SZ, (const BYTE*)progID, strlen(progID) + 1) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return false;
}
return RegCloseKey(hKey) == ERROR_SUCCESS;
}
char pBrowsrCmd[MAX_PATH] = {0};
if (_stricmp(ext, "http") == 0 || _stricmp(ext, "https") == 0) {
if (_stricmp(progID, EDGE_UWP_LEGACY_ID) == 0 || _stricmp(progID, EDGE_UWP_MODERN_ID) == 0) {
strcpy_s(pBrowsrCmd, _countof(pBrowsrCmd), EDGE_UWP_EXE_PATH);
} else {
if (!FindBrowserCommandByProtocol(HKEY_CURRENT_USER, ext, progID, sizeof(pBrowsrCmd), pBrowsrCmd))
FindBrowserCommandByProtocol(HKEY_LOCAL_MACHINE, ext, progID, sizeof(pBrowsrCmd), pBrowsrCmd);
}
}
char pSource[STRING_BUFFER];
if (winVer > Win8_1) {
std::string ts = GetRegistryKeyTimestampHex(subkey);
std::string sid = GetCurrentUserSidString();
if (winVer < Win10Above1607) {
snprintf(pSource, _countof(pSource), "%s%s%s%s%s%s", ext, sid.c_str(), progID, pBrowsrCmd, ts.c_str(), (const char*)ASSOC_DESCRIPTOR);
} else {
snprintf(pSource, _countof(pSource), "%s%s%s%s%s", ext, sid.c_str(), progID, ts.c_str(), (const char*)ASSOC_DESCRIPTOR);
}
} else
if (winVer > Win7) {
std::string sid = GetCurrentUserSidString();
snprintf(pSource, _countof(pSource), "%s%s%s%s", ext, sid.c_str(), progID, pBrowsrCmd);
}
size_t cbDest;
wchar_t pDest[STRING_BUFFER] = {0};
mbstowcs_s(&cbDest, pDest, sizeof(pDest)/sizeof(pDest[0]), pSource, strlen(pSource));
cbDest = (wcslen(pDest) + 1) * sizeof(pDest[0]);
_wcslwr_s(pDest);
uint32_t md5Hash[2] = {0};
CalcMD5Hash(pDest, cbDest, md5Hash);
unsigned int len = ((cbDest & 4) == 0) + (cbDest >> 2) - 1;
uint32_t outHash[4] = {0};
CalcCustomHash_V1((uint32_t*)pDest, len, md5Hash, outHash);
CalcCustomHash_V2((uint32_t*)pDest, len, md5Hash, outHash + 2);
uint64_t hashVal1 = *(uint64_t*)(outHash + 2) ^ *(uint64_t*)outHash;
uint64_t hashVal2 = *(uint64_t*)(outHash + 3) ^ *(uint64_t*)(outHash + 1);
uint8_t outHashBase[8] = {0};
memcpy(outHashBase, &hashVal1, sizeof(outHashBase)/2);
memcpy(outHashBase + 4, &hashVal2, sizeof(outHashBase)/2);
std::string base64Enc;
if (!Base64Encode((const BYTE*)outHashBase, sizeof(outHashBase), base64Enc)) {
return false;
}
char resHash[20];
snprintf(resHash, 20, "%s", base64Enc.c_str());
if (_stricmp(ext, ".pdf") == 0 || _stricmp(ext, "http") == 0 || _stricmp(ext, "https") == 0) {
return RunWriteAssocWithHTA(subkey, resHash, progID);
} else {
if (RegOpenKeyExA(HKEY_CURRENT_USER, subkey, 0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) {
return false;
}
if (RegSetValueExA(hKey, "Hash", 0, REG_SZ, (const BYTE*)resHash, strlen(resHash) + 1) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return false;
}
if (RegSetValueExA(hKey, "ProgId", 0, REG_SZ, (const BYTE*)progID, strlen(progID) + 1) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return false;
}
return RegCloseKey(hKey) == ERROR_SUCCESS;
}
}
bool SetUserFileAssoc(const wchar_t* ext, const wchar_t* progId, bool notifySystem)
{
if (getWindowsVersion() < WinVista)
return false;
char lpExt[MAX_PATH];
size_t cbDest;
wcstombs_s(&cbDest, lpExt, sizeof(lpExt), ext, sizeof(lpExt) - 1);
char lpProgId[MAX_PATH];
wcstombs_s(&cbDest, lpProgId, sizeof(lpProgId), progId, sizeof(lpProgId) - 1);
char lpSubkey[MAX_PATH];
snprintf(lpSubkey, _countof(lpSubkey), lpExt[0] == L'.' ? REG_EXT_USER_ASSOC : REG_URL_USER_ASSOC, lpExt);
if (!SetAssocWithHash(lpExt, lpProgId, lpSubkey))
return false;
if (notifySystem)
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
}
bool SetUserFileAssoc(const AssocPair* assocArray, size_t count)
{
for (int i = 0; i < count; ++i) {
if (!SetUserFileAssoc(assocArray[i].extension, assocArray[i].progId, false))
return false;
}
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
}
// bool ResetUserFileAssoc(const wchar_t* ext, bool notifySystem)
// {
// int winVer = getWindowsVersion();
// if (winVer < WinVista)
// return false;
// char lpExt[MAX_PATH];
// size_t cbDest;
// wcstombs_s(&cbDest, lpExt, sizeof(lpExt), ext, sizeof(lpExt) - 1);
// char lpSubkey[MAX_PATH];
// snprintf(lpSubkey, _countof(lpSubkey), lpExt[0] == L'.' ? REG_EXT_USER_ASSOC : REG_URL_USER_ASSOC, lpExt);
// HKEY hKey;
// if (RegOpenKeyExA(HKEY_CURRENT_USER, lpSubkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
// RegCloseKey(hKey);
// if (IsPdfOrHttpScheme(lpExt) && winVer > Win8_1) {
// if (!RunDeleteAssocWithHTA(lpSubkey))
// return false;
// } else {
// if (RegDeleteKeyA(HKEY_CURRENT_USER, lpSubkey) != ERROR_SUCCESS)
// return false;
// }
// if (notifySystem)
// SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
// }
// return true;
// }
// bool ResetUserFileAssoc(const wchar_t** extArray, size_t count)
// {
// for (int i = 0; i < count; ++i) {
// if (!ResetUserFileAssoc(extArray[i], false))
// return false;
// }
// SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
// return true;
// }

View File

@ -0,0 +1,14 @@
#ifndef SHELLASSOC_H
#define SHELLASSOC_H
struct AssocPair {
const wchar_t* extension;
const wchar_t* progId;
};
bool SetUserFileAssoc(const wchar_t* ext, const wchar_t* progId, bool notifySystem = true);
bool SetUserFileAssoc(const AssocPair *assocArray, size_t count);
// bool ResetUserFileAssoc(const wchar_t* ext, bool notifySystem = true);
// bool ResetUserFileAssoc(const wchar_t** extArray, size_t count);
#endif // SHELLASSOC_H

View File

@ -51,7 +51,7 @@
# define DESKTOP_FILE_NAME "onlyoffice-desktopeditors"
#else
# define APP_DATA_PATH "/ONLYOFFICE/DesktopEditors"
# define APP_REG_NAME "ONLYOFFICE Editors"
# define APP_REG_NAME "ONLYOFFICE"
# define REG_GROUP_KEY "ONLYOFFICE"
# define REG_UNINST_KEY "ONLYOFFICE Desktop Editors"
# define APP_MUTEX_NAME "TEAMLAB"

View File

@ -56,6 +56,9 @@ int main( int argc, char *argv[] )
Utils::setAppUserModelId();
WCHAR * cm_line = GetCommandLine();
InputArgs::init(cm_line);
if ( InputArgs::contains(L"--assoc") ) {
return 0;
}
#else
qputenv("QT_QPA_PLATFORM", "xcb");
qputenv("GDK_BACKEND", "x11");

View File

@ -223,6 +223,8 @@ void Association::AssociationPrivate::tryProposeAssociation(QWidget *parent, con
}
if (res == MODAL_RESULT_YES) {
associate(unassocFileExts);
return;
if (Utils::getWinVersion() >= Utils::WinVer::Win10) {
ShellExecute(NULL, L"open", L"ms-settings:defaultapps", NULL, NULL, SW_SHOWNORMAL);
@ -276,6 +278,39 @@ void Association::AssociationPrivate::associate(const std::vector<std::wstring>
}
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
#else
if (Utils::getWinVersion() > Utils::WinVer::Win7) {
std::wstring args(L"--assoc \"");
for (const auto &ext : unassocFileExts) {
auto it = m_extMap.find(ext);
if (it != m_extMap.end()) {
args.append(it->first);
args.append(L":");
args.append(it->second);
args.append(L";");
}
}
args.append(L"\"");
QString appPath = qApp->applicationDirPath();
appPath += "/" + QString(REG_APP_NAME);
std::wstring path = appPath.toStdWString();
SHELLEXECUTEINFO shExInfo = {0};
shExInfo.cbSize = sizeof(shExInfo);
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_NO_UI;
shExInfo.hwnd = NULL;
shExInfo.lpVerb = L"open";
shExInfo.lpFile = path.c_str();
shExInfo.lpParameters = args.c_str();
shExInfo.lpDirectory = NULL;
shExInfo.nShow = SW_HIDE;
shExInfo.hInstApp = NULL;
if (ShellExecuteEx(&shExInfo)) {
CloseHandle(shExInfo.hProcess);
}
return;
}
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr)) {
IApplicationAssociationRegistration *pAr;