From f8665c6880db39cba9d71ed24efb7942b464576e Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Tue, 22 Apr 2025 16:49:21 +0300 Subject: [PATCH] [win] support file assoc for win8 and above --- win-linux/extras/projicons/common.pri | 14 +- win-linux/extras/projicons/src/main.cpp | 35 ++ win-linux/extras/projicons/src/shellassoc.cpp | 594 ++++++++++++++++++ win-linux/extras/projicons/src/shellassoc.h | 14 + win-linux/src/defines.h | 2 +- win-linux/src/main.cpp | 3 + win-linux/src/platform_win/association.cpp | 35 ++ 7 files changed, 694 insertions(+), 3 deletions(-) create mode 100644 win-linux/extras/projicons/src/shellassoc.cpp create mode 100644 win-linux/extras/projicons/src/shellassoc.h diff --git a/win-linux/extras/projicons/common.pri b/win-linux/extras/projicons/common.pri index c6ca242b6..1981e29ff 100644 --- a/win-linux/extras/projicons/common.pri +++ b/win-linux/extras/projicons/common.pri @@ -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 } diff --git a/win-linux/extras/projicons/src/main.cpp b/win-linux/extras/projicons/src/main.cpp index aafbd259e..a735f12e7 100644 --- a/win-linux/extras/projicons/src/main.cpp +++ b/win-linux/extras/projicons/src/main.cpp @@ -36,6 +36,7 @@ #include #ifndef __OS_WIN_XP # include "jumplist.h" +# include "shellassoc.h" # include #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 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 diff --git a/win-linux/extras/projicons/src/shellassoc.cpp b/win-linux/extras/projicons/src/shellassoc.cpp new file mode 100644 index 000000000..d58687a13 --- /dev/null +++ b/win-linux/extras/projicons/src/shellassoc.cpp @@ -0,0 +1,594 @@ +#include "shellassoc.h" +#include +#include +#include +#include +#include +#include +#include + +#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"( +ShellAssoc + + + +)"; + +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(×tamp[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; +// } diff --git a/win-linux/extras/projicons/src/shellassoc.h b/win-linux/extras/projicons/src/shellassoc.h new file mode 100644 index 000000000..fe201a656 --- /dev/null +++ b/win-linux/extras/projicons/src/shellassoc.h @@ -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 diff --git a/win-linux/src/defines.h b/win-linux/src/defines.h index cba03de84..07b3a8dee 100644 --- a/win-linux/src/defines.h +++ b/win-linux/src/defines.h @@ -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" diff --git a/win-linux/src/main.cpp b/win-linux/src/main.cpp index 499f24949..238f2a717 100644 --- a/win-linux/src/main.cpp +++ b/win-linux/src/main.cpp @@ -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"); diff --git a/win-linux/src/platform_win/association.cpp b/win-linux/src/platform_win/association.cpp index d0c88ea1d..1731d6fe3 100644 --- a/win-linux/src/platform_win/association.cpp +++ b/win-linux/src/platform_win/association.cpp @@ -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 } 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;