diff --git a/Docs/src/generalpurpose.but b/Docs/src/generalpurpose.but index e0ddb143..0cd01d39 100644 --- a/Docs/src/generalpurpose.but +++ b/Docs/src/generalpurpose.but @@ -81,6 +81,17 @@ Gets the last write time of "filename". Sets the user output variables with the This is similar to \R{getfiletime}{GetFileTime}, only it acts on the system building the installer (it actually compiles into two \R{StrCpy}{StrCpy} commands). Sets the two output variables with the file timestamp of the file on the build system. +\S2{getknownfolderpath} GetKnownFolderPath + +\c user_var(output) knownfolderid + +Get the path of a \W{https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid}{known folder}. The error flag is set and the output variable is empty if the call fails or the knownfolderid guid is not available. This function is only able to resolve known folders Windows Vista or higher. + +\c !include WinCore.nsh +\c GetKnownFolderPath $InstDir ${FOLDERID_UserProgramFiles} +\c StrCmp $InstDir "" 0 +2 +\c StrCpy $InstDir "$LocalAppData\Programs" + \S2{getfullpathname} GetFullPathName \c [/SHORT] user_var(output) path_or_file diff --git a/Docs/src/history.but b/Docs/src/history.but index d00c1c05..80cf6ac2 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -12,6 +12,8 @@ Released on ? ?th, 2020 \S2{} Major Changes +\b Added \R{getknownfolderpath}{GetKnownFolderPath} + \S2{} Minor Changes \b Added \R{ifshellvarcontextall}{IfShellVarContextAll} and \R{ifrtllanguage}{IfRtlLanguage} diff --git a/Include/WinCore.nsh b/Include/WinCore.nsh index b062b506..692dca4b 100644 --- a/Include/WinCore.nsh +++ b/Include/WinCore.nsh @@ -227,5 +227,37 @@ Shobjidl.h + + +/************************************************** +ShlGuid.h +**************************************************/ +!ifndef __WIN_NOINC_SHLGUID +!define FOLDERID_Public {DFDF76A2-C82A-4D63-906A-5644AC457385} ; Vista+ Fixed=%SystemDrive%\Users\Public +!define FOLDERID_Games {CAC52C1A-B53D-4edc-92D7-6B2E8AC19434} ; Vista+ && < 10 (1803) Virtual +!define FOLDERID_SavedGames {4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4} ; Vista+ PerUser=%USERPROFILE%\Saved Games +!define FOLDERID_GameTasks {054FAE61-4DD8-4787-80B6-090220C4B700} ; Vista+ PerUser=%LOCALAPPDATA%\Microsoft\Windows\GameExplorer +!define FOLDERID_PublicGameTasks {DEBF2536-E1A8-4c59-B6A2-414586476AEA} ; Vista+ Common=%ALLUSERSPROFILE%\Microsoft\Windows\GameExplorer +!define FOLDERID_Contacts {56784854-C6CB-462b-8169-88E350ACB882} ; Vista+ PerUser=%USERPROFILE%\Contacts +!define FOLDERID_Downloads {374DE290-123F-4565-9164-39C4925E467B} ; Vista+ PerUser=%USERPROFILE%\Downloads +!define FOLDERID_PublicDownloads {3D644C9B-1FB8-4f30-9B45-F670235F79C0} ; Vista+ Common=%PUBLIC%\Downloads +!define FOLDERID_UserProfiles {0762D272-C50A-4BB0-A382-697DCD729B80} ; Vista+ Fixed=%SystemDrive%\Users +!define FOLDERID_UserProgramFiles {5CD7AEE2-2219-4A67-B85D-6C9CE15660CB} ; 7+ PerUser=%LOCALAPPDATA%\Programs +!define FOLDERID_UserProgramFilesCommon {BCBD3057-CA5C-4622-B42D-BC56DB0AE516} ; 7+ PerUser=%LOCALAPPDATA%\Programs\Common +!define FOLDERID_PublicLibraries {48DAF80B-E6CF-4F4E-B800-0E69D84EE384} ; 7+ Common=%ALLUSERSPROFILE%\Microsoft\Windows\Libraries +!define FOLDERID_UserPinned {9E3995AB-1F9C-4F13-B827-48B24B6C7174} ; 7+ PerUser=%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned +!define FOLDERID_ImplicitAppShortcuts {BCB5256F-79F6-4CEE-B725-DC34E402FD46} ; 7+ PerUser=%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\ImplicitAppShortcuts +!define FOLDERID_DeviceMetadataStore {5CE4A5E9-E4EB-479D-B89F-130C02886155} ; 7+ Common=%ALLUSERSPROFILE%\Microsoft\Windows\DeviceMetadataStore +!define FOLDERID_ApplicationShortcuts {A3918781-E5F2-4890-B3D9-A7E54332328C} ; 8.0+ PerUser=%LOCALAPPDATA%\Microsoft\Windows\Application Shortcuts +!define FOLDERID_RoamingTiles {00BCFC5A-ED94-4e48-96A1-3F6217F21990} ; 8.0+ PerUser=%LOCALAPPDATA%\Microsoft\Windows\RoamingTiles +!define FOLDERID_RoamedTileImages {AAA8D5A5-F1D6-4259-BAA8-78E7EF60835E} ; 8.0+ PerUser=%LOCALAPPDATA%\Microsoft\Windows\RoamedTileImages +!define FOLDERID_PublicUserTiles {0482af6c-08f1-4c34-8c90-e17ec98b1e17} ; 8.0+ Common=%PUBLIC%\AccountPictures +!define FOLDERID_AccountPictures {008ca0b1-55b4-4c56-b8a8-4de4b299d3be} ; 8.0+ PerUser=%APPDATA%\Microsoft\Windows\AccountPictures +!define FOLDERID_Screenshots {b7bede81-df94-4682-a7d8-57a52620b86f} ; 8.0+ PerUser=%USERPROFILE%\Pictures\Screenshots +!define FOLDERID_SkyDrive {A52BBA46-E9E1-435f-B3D9-28DAA648C0F6} ; 8.1+ PerUser=%USERPROFILE%\OneDrive +!define FOLDERID_AppDataProgramData {559D40A3-A036-40FA-AF61-84CB430A4D34} ; 10 (1709)+ PerUser=%LOCALAPPDATA%\ProgramData +!endif /* __WIN_NOINC_SHLGUID */ + + !verbose pop !endif /* __WIN_WINDOWS__INC */ \ No newline at end of file diff --git a/Source/exehead/exec.c b/Source/exehead/exec.c index 305c88fb..ab19c093 100644 --- a/Source/exehead/exec.c +++ b/Source/exehead/exec.c @@ -1390,7 +1390,7 @@ static int NSISCALL ExecuteEntry(entry *entry_) else if (which==EW_FPUTS) { GetStringFromParm(0x21); // load string in buf2, convert it to ANSI in buf1 - WideCharToMultiByte(CP_ACP, 0, buf2, -1, (LPSTR) buf1, NSIS_MAX_STRLEN, NULL, NULL); + StrWideToACP(buf2, (LPSTR) buf1, NSIS_MAX_STRLEN); l=lstrlenA((LPCSTR)buf1); } #endif @@ -1704,6 +1704,32 @@ static int NSISCALL ExecuteEntry(entry *entry_) break; #endif//NSIS_CONFIG_COMPONENTPAGE + case EW_GETOSINFO: + { + //switch(parm0) + { +#ifdef NSIS_SUPPORT_FNUTIL + //case 0: + { + TCHAR *outstr = var1; + IID kfid; + HRESULT(WINAPI*SHGKFP)(REFIID,DWORD,HANDLE,PWSTR*) = (HRESULT(WINAPI*)(REFIID,DWORD,HANDLE,PWSTR*)) myGetProcAddress(MGA_SHGetKnownFolderPath); + TCHAR *buf2 = GetStringFromParm(0x22), succ = FALSE; + if (SHGKFP && SUCCEEDED(ComIIDFromString(buf2, &kfid))) + { + PWSTR path; + HRESULT hr = SHGKFP(&kfid, parm3, NULL, &path); + if (SUCCEEDED(hr)) + strcpyWideToT(outstr, path), CoTaskMemFree(path), ++succ; + } + if (!succ) + exec_error++, *outstr = _T('\0'); + } + //break; +#endif + } + } + break; #ifdef NSIS_LOCKWINDOW_SUPPORT case EW_LOCKWINDOW: { diff --git a/Source/exehead/fileform.h b/Source/exehead/fileform.h index a8e58b61..dcb9e11c 100644 --- a/Source/exehead/fileform.h +++ b/Source/exehead/fileform.h @@ -193,9 +193,8 @@ enum // InstTypeGetFlags: 3: [idx, 1, output] #endif - // instructions not actually implemented in exehead, but used in compiler. - EW_GETLABELADDR, // both of these get converted to EW_ASSIGNVAR - EW_GETFUNCTIONADDR, + EW_GETOSINFO, // 1+ [operation, ...] + EW_RESERVEDOPCODE, // Free slot, feel free to use it for something #ifdef NSIS_LOCKWINDOW_SUPPORT EW_LOCKWINDOW, @@ -207,6 +206,10 @@ enum EW_FGETWS, // FileReadUTF16LE: 4 [handle, output, maxlen, ?getchar:gets] #endif//NSIS_SUPPORT_FILEFUNCTIONS #endif + + // Opcodes listed here are not actually used in exehead. No exehead opcodes should be present after these! + EW_GETLABELADDR, // --> EW_ASSIGNVAR + EW_GETFUNCTIONADDR, // --> EW_ASSIGNVAR }; #pragma pack(push, 1) // fileform.cpp assumes no padding/alignment diff --git a/Source/exehead/util.c b/Source/exehead/util.c index 41c5b50e..a653556f 100644 --- a/Source/exehead/util.c +++ b/Source/exehead/util.c @@ -771,6 +771,30 @@ TCHAR * NSISCALL mystrcat(TCHAR *out, const TCHAR *concat) return lstrcat(out, concat); } +int StrWideToACP(LPCWSTR Src, char* Dst, int DstCap) +{ + return WideCharToMultiByte(CP_ACP, 0, Src, -1, Dst, DstCap, NULL, NULL); +} + +#ifndef UNICODE +void strcpyWideToT(TCHAR *out, LPCWSTR in) +{ + StrWideToACP(in, out, NSIS_MAX_STRLEN); +} +#endif + +#if !defined(_WIN64) && !defined(UNICODE) +HRESULT ComIIDFromString(LPCTSTR str, IID*out) +{ + WCHAR buf[130]; + signed char i; + for (i = 0; i >= 0; ++i) + if (!(buf[i] = str[i])) + return IIDFromString(buf, out); + return E_FAIL; +} +#endif + TCHAR ps_tmpbuf[NSIS_MAX_STRLEN*2]; const TCHAR SYSREGKEY[] = _T("Software\\Microsoft\\Windows\\CurrentVersion"); @@ -1145,6 +1169,7 @@ struct MGA_FUNC MGA_FUNCS[] = { {"ADVAPI32", "RegDeleteKeyExW"}, #endif {"ADVAPI32", "InitiateShutdownW"}, + {"SHELL32", "SHGetKnownFolderPath"}, {"SHELL32", (CHAR*) 680}, // IsUserAnAdmin #ifndef _WIN64 {"SHLWAPI", (CHAR*) 437}, // IsOS @@ -1163,6 +1188,7 @@ struct MGA_FUNC MGA_FUNCS[] = { {"KERNEL32", "GetUserDefaultUILanguage"}, {"ADVAPI32", "RegDeleteKeyExA"}, {"ADVAPI32", "InitiateShutdownA"}, + {"SHELL32", "SHGetKnownFolderPath"}, {"SHELL32", (CHAR*) 680}, // IsUserAnAdmin #ifndef _WIN64 {"SHLWAPI", (CHAR*) 437}, // IsOS @@ -1234,7 +1260,7 @@ void * NSISCALL NSISGetProcAddress(HANDLE dllHandle, TCHAR* funcName) { #ifdef _UNICODE char ansiName[256]; - if (WideCharToMultiByte(CP_ACP, 0, funcName, -1, ansiName, 256, NULL, NULL) != 0) + if (StrWideToACP(funcName, ansiName, 256) != 0) return GetProcAddress(dllHandle, ansiName); return NULL; #else diff --git a/Source/exehead/util.h b/Source/exehead/util.h index 600d626d..703d1dec 100644 --- a/Source/exehead/util.h +++ b/Source/exehead/util.h @@ -40,7 +40,20 @@ TCHAR * NSISCALL mystrcpy(TCHAR *out, const TCHAR *in); int NSISCALL mystrlen(const TCHAR *in); TCHAR * NSISCALL mystrcat(TCHAR *out, const TCHAR *concat); TCHAR * NSISCALL mystrstr(TCHAR *a, TCHAR *b); +int StrWideToACP(LPCWSTR Src, char* Dst, int DstCap); +#ifdef UNICODE +#define strcpyWideToT mystrcpy +#else +void strcpyWideToT(TCHAR *out, LPCWSTR in); +#endif +#ifdef _WIN64 +#define ComIIDFromString(s,out) SHCLSIDFromString((s),(CLSID*)(out)) +#elif defined(UNICODE) +#define ComIIDFromString(s,out) IIDFromString((s), (IID*)(out)) +#else +HRESULT ComIIDFromString(LPCTSTR str, IID*out); +#endif #ifndef KEY_CREATE_LINK #define KEY_CREATE_LINK 0x0020 @@ -154,6 +167,7 @@ enum myGetProcAddressFunctions { MGA_RegDeleteKeyEx, #endif MGA_InitiateShutdown, + MGA_SHGetKnownFolderPath, MGA_IsUserAnAdmin, #ifndef _WIN64 MGA_IsOS, diff --git a/Source/script.cpp b/Source/script.cpp index e29d0a5e..66f5945d 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -4187,6 +4187,12 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) line.gettoken_str(2+a),line.gettoken_str(1+a),a?_T("sfn"):_T("lfn")); } return add_entry(&ent); + case TOK_GETKNOWNFOLDERPATH: + ent.which=EW_GETOSINFO; + ent.offsets[0]=0; // Operation + ent.offsets[1]=GetUserVarIndex(line, 1); + ent.offsets[2]=add_string(line.gettoken_str(2)); + return add_entry(&ent); case TOK_SEARCHPATH: ent.which=EW_SEARCHPATH; ent.offsets[0]=GetUserVarIndex(line, 1); @@ -4198,6 +4204,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) case TOK_SEARCHPATH: case TOK_GETTEMPFILENAME: case TOK_GETFULLPATHNAME: + case TOK_GETKNOWNFOLDERPATH: ERROR_MSG(_T("Error: %") NPRIs _T(" specified, NSIS_SUPPORT_FNUTIL not defined.\n"), line.gettoken_str(0)); return PS_ERROR; #endif //~ NSIS_SUPPORT_FNUTIL diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 61f2574f..e8404dd8 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -110,6 +110,7 @@ static tokenType tokenlist[TOK__LAST] = {TOK_GETDLGITEM,_T("GetDlgItem"),3,0,_T("$(user_var: handle output) dialog item_id"),TP_CODE}, {TOK_GETFULLPATHNAME,_T("GetFullPathName"),2,1,_T("[/SHORT] $(user_var: result) path_or_file"),TP_CODE}, {TOK_GETTEMPFILENAME,_T("GetTempFileName"),1,1,_T("$(user_var: name output) [base_dir]"),TP_CODE}, +{TOK_GETKNOWNFOLDERPATH,_T("GetKnownFolderPath"),2,0,_T("$(user_var: result) knownfolderid"),TP_CODE}, {TOK_HIDEWINDOW,_T("HideWindow"),0,0,_T(""),TP_CODE}, {TOK_ICON,_T("Icon"),1,0,_T("local_icon.ico"),TP_GLOBAL}, {TOK_IFABORT,_T("IfAbort"),1,1,_T("label_to_goto_if_abort [label_to_goto_if_no_abort]"),TP_CODE}, diff --git a/Source/tokens.h b/Source/tokens.h index bcacf627..cdc44413 100644 --- a/Source/tokens.h +++ b/Source/tokens.h @@ -280,6 +280,7 @@ enum #endif TOK_FILESEEK, TOK_GETFULLPATHNAME, + TOK_GETKNOWNFOLDERPATH, TOK_REBOOT, TOK_IFREBOOTFLAG, TOK_SETREBOOTFLAG,