v2026.3.22.0

English

This patch addresses the review feedback around Windows installer metadata and update-path handling, and removes a partially implemented UI surface that duplicated the existing multi-output path controls.

Installer / ARP metadata
- The installer previously wrote fork-specific hardcoded values into the Windows Uninstall registry entry (`DisplayName`, `DisplayVersion`, `Publisher`).
- That created drift between the installed product metadata and the actual upstream 7-Zip version/author macros used elsewhere in the tree.
- The patch removes the hardcoded fork values and derives the registry values from the existing 7-Zip definitions:
  - `DisplayName` now uses the existing versioned product string (`7-Zip 26.00 (x64)` on 64-bit builds).
  - `DisplayVersion` now comes from `MY_VERSION`.
  - `Publisher` now comes from `MY_AUTHOR_NAME`.
- This makes the Add/Remove Programs entry consistent with the rest of the build metadata and removes the need for manual source edits on each branch-specific release.

Compression dialog cleanup
- The `Paths...` button and its dedicated dialog were not part of the stable workflow.
- At this point the main compression dialog already exposes the supported multi-output behavior through the output-count selector plus the existing archive path rows and browse buttons.
- Keeping a second path-editing dialog in parallel would add maintenance overhead and duplicate UI/state management without providing new functionality.
- For that reason, this patch removes the incomplete/duplicate surface entirely:
  - the `Paths...` button was removed from the dialog resource,
  - the handler declaration/definition was removed,
  - the auxiliary `COutputArcPathsDialog` class was removed,
  - the associated resource IDs and helper parsing/serialization code were removed.
- Functional capability is unchanged: users still configure multiple output targets through the existing archive path controls already present in the compression dialog.

Update path deduplication
- In `UpdateGUI.cpp`, `AddUniquePath()` previously compared paths with a case-sensitive equality check.
- On Windows, this could fail to deduplicate logically identical paths that differ only by casing.
- The comparison is now case-insensitive (`IsEqualTo_NoCase()`), which matches expected Windows path behavior more closely and prevents duplicate item entries in the update/compression path collection stage.
- This is intentionally a minimal fix: it addresses case-only duplication without changing the broader path normalization rules.

Working directory safety
- `PrepareWorkingDirForArchivePath()` previously ignored the return value of `MyGetFullPathName()`.
- If full-path resolution failed, the code could continue with an invalid or empty path and derive an incorrect working directory.
- The function now exits early when `MyGetFullPathName()` fails, leaving `options.WorkingDir` empty.
- This is a conservative change: the downstream update flow already has fallback behavior for an empty working directory, so the patch avoids propagating bad path state without altering the existing fallback model.

Header consistency / non-functional cleanup
- `LangUtils.h` now provides a non-`Z7_LANG` fallback declaration for `LangString_OnlyFromLangFile()`.
- This is not a functional runtime change for normal localized builds, but it keeps the header interface consistent across configurations and avoids IDE/static-analysis mismatches where `Z7_LANG` is not visible to the parser.

Behavioral impact
- No archive format logic was changed.
- No codec behavior was changed.
- No new persistence format was introduced.
- The only runtime behavior changes are:
  - correct Windows ARP metadata,
  - removal of dead/duplicate UI,
  - case-insensitive deduplication of update input paths,
  - safer handling of full-path resolution failure before working directory derivation.

Validation
- The change set was validated with targeted code search and diff review to ensure the removed dialog/button plumbing no longer has remaining references.
- `git diff --check` was also run on the touched files.
- A full build/test pass has not yet been run in this environment.

Portuguese

Este patch responde ao feedback de revisão relacionado aos metadados do instalador no Windows e ao tratamento de caminhos no fluxo de atualização, além de remover uma superfície de UI parcialmente implementada que duplicava os controles já existentes para múltiplos caminhos de saída.

Instalador / metadados do item no Windows
- O instalador estava gravando valores hardcoded e específicos do fork na chave de desinstalação do Windows (`DisplayName`, `DisplayVersion`, `Publisher`).
- Isso criava divergência entre os metadados exibidos em “Aplicativos instalados / Programas e Recursos” e as macros oficiais de versão/autoria já utilizadas no restante do código-fonte.
- O patch remove esses valores fixos do fork e passa a derivar os valores diretamente das definições já existentes do 7-Zip:
  - `DisplayName` agora usa a string versionada já existente do produto (`7-Zip 26.00 (x64)` em builds 64-bit).
  - `DisplayVersion` agora vem de `MY_VERSION`.
  - `Publisher` agora vem de `MY_AUTHOR_NAME`.
- Com isso, a entrada instalada no Windows fica consistente com os demais metadados do build e deixa de exigir edição manual do código a cada release da branch.

Limpeza do diálogo de compressão
- O botão `Paths...` e a dialog auxiliar associada não faziam parte de um fluxo estável/finalizado.
- Neste momento, a janela principal de compressão já expõe o comportamento suportado de múltiplas saídas por meio do seletor de quantidade de saídas, dos campos de caminho de arquivo e dos botões de browse já existentes.
- Manter uma segunda dialog para edição textual desses caminhos aumentaria o custo de manutenção e duplicaria a lógica de estado/UI sem adicionar capacidade funcional nova.
- Por esse motivo, este patch remove completamente essa superfície incompleta/duplicada:
  - o botão `Paths...` foi removido do recurso da dialog,
  - a declaração e a implementação do handler foram removidas,
  - a classe auxiliar `COutputArcPathsDialog` foi removida,
  - os IDs de recurso associados e os helpers internos de parsing/serialização dessa dialog também foram removidos.
- A capacidade funcional permanece a mesma: o usuário continua podendo configurar múltiplos destinos pelos controles de caminhos de saída já existentes na própria janela de compressão.

Deduplicação de caminhos no fluxo de atualização
- Em `UpdateGUI.cpp`, a função `AddUniquePath()` comparava caminhos usando igualdade case-sensitive.
- Em Windows, isso podia falhar em deduplicar caminhos logicamente idênticos que diferiam apenas por maiúsculas/minúsculas.
- A comparação agora é case-insensitive (`IsEqualTo_NoCase()`), o que se alinha melhor ao comportamento esperado para caminhos no Windows e evita entradas duplicadas durante a coleta dos caminhos de itens no fluxo de atualização/compressão.
- A alteração foi mantida propositalmente mínima: ela corrige a duplicação por diferença de casing sem alterar as regras mais amplas de normalização de caminhos.

Segurança na preparação do diretório de trabalho
- `PrepareWorkingDirForArchivePath()` ignorava o valor de retorno de `MyGetFullPathName()`.
- Se a resolução para caminho absoluto falhasse, o código poderia continuar com um caminho inválido ou vazio e, a partir disso, derivar um diretório de trabalho incorreto.
- Agora a função retorna imediatamente quando `MyGetFullPathName()` falha, deixando `options.WorkingDir` vazio.
- Essa mudança é conservadora: o fluxo subsequente de atualização já possui fallback quando o diretório de trabalho está vazio, então o patch evita propagar um estado inconsistente sem alterar o modelo de fallback já existente.

Consistência de headers / limpeza não funcional
- `LangUtils.h` agora fornece um fallback para `LangString_OnlyFromLangFile()` também quando `Z7_LANG` não está definido.
- Isso não altera o comportamento funcional em builds localizados normais, mas mantém a interface do header consistente entre configurações e evita divergências em IDEs/analisadores estáticos quando `Z7_LANG` não está visível para o parser.

Impacto comportamental
- Nenhuma lógica de formato de arquivo foi alterada.
- Nenhum comportamento de codec foi alterado.
- Nenhum novo formato de persistência foi introduzido.
- As únicas mudanças observáveis em runtime são:
  - correção dos metadados exibidos pelo Windows para a instalação,
  - remoção de uma UI morta/duplicada,
  - deduplicação case-insensitive de caminhos no fluxo de atualização,
  - tratamento mais seguro de falha ao resolver caminho absoluto antes de derivar o diretório de trabalho.

Validação
- O conjunto de alterações foi validado com buscas direcionadas no código e revisão de diff para garantir que a plumbing removida da dialog/botão não deixou referências residuais.
- Também foi executado `git diff --check` nos arquivos alterados.
- Ainda não foi executado um ciclo completo de build/testes neste ambiente.
This commit is contained in:
Fernando Nillsson Cidade 2026-03-22 01:36:40 -03:00
parent 5a4388f2b8
commit 1eca44e7a7
11 changed files with 9 additions and 906 deletions

2
.gitattributes vendored
View file

@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

4
.gitignore vendored
View file

@ -1,4 +0,0 @@
/.vscode/
/dist/
/InstallerAssets/
/Lang/

View file

@ -59,9 +59,8 @@ typedef enum {
static LPCSTR const k_7zip = "7-Zip";
static LPCWSTR const k_Reg_Software_7zip = L"Software\\7-Zip";
static LPCWSTR const k_Fork_DisplayName = L"7-Zip 2026.3.19.0 (x64)";
static LPCWSTR const k_Fork_DisplayVersion = L"2026.3.19.0";
static LPCWSTR const k_Fork_Publisher = L"Fernando Nillsson Cidade";
static LPCWSTR const k_7zip_DisplayVersion = LLL(MY_VERSION);
static LPCWSTR const k_7zip_Publisher = LLL(MY_AUTHOR_NAME);
// #define Z7_64BIT_INSTALLER 1
@ -946,8 +945,8 @@ static void WriteShellEx(void)
LONG res = MyRegistry_CreateKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\7-Zip", &destKey);
if (res == ERROR_SUCCESS)
{
MyRegistry_SetString(destKey, L"DisplayName", k_Fork_DisplayName);
MyRegistry_SetString(destKey, L"DisplayVersion", k_Fork_DisplayVersion);
MyRegistry_SetString(destKey, L"DisplayName", k_7zip_with_Ver_str);
MyRegistry_SetString(destKey, L"DisplayVersion", k_7zip_DisplayVersion);
MyRegistry_SetString(destKey, L"DisplayIcon", destPath);
MyRegistry_SetString(destKey, L"InstallLocation", path);
@ -967,7 +966,7 @@ static void WriteShellEx(void)
MyRegistry_SetDWORD(destKey, L"VersionMajor", MY_VER_MAJOR);
MyRegistry_SetDWORD(destKey, L"VersionMinor", MY_VER_MINOR);
MyRegistry_SetString(destKey, L"Publisher", k_Fork_Publisher);
MyRegistry_SetString(destKey, L"Publisher", k_7zip_Publisher);
// MyRegistry_SetString(destKey, L"HelpLink", L"http://www.7-zip.org/support.html");
// MyRegistry_SetString(destKey, L"URLInfoAbout", L"http://www.7-zip.org/");

View file

@ -37,6 +37,7 @@ void LangString_OnlyFromLangFile(UInt32 langID, UString &dest);
inline UString LangString(UInt32 langID) { return NWindows::MyLoadString(langID); }
inline void LangString(UInt32 langID, UString &dest) { NWindows::MyLoadString(langID, dest); }
inline void AddLangString(UString &s, UInt32 langID) { s += NWindows::MyLoadString(langID); }
inline void LangString_OnlyFromLangFile(UInt32, UString &dest) { dest.Empty(); }
#endif

View file

@ -636,7 +636,6 @@ bool CCompressDialog::OnInit()
m_OutputPathCount.AddString_SetItemData(s, (LPARAM)i);
}
ShowItem_Bool(IDB_COMPRESS_OUTPUT_PATHS, false);
ShowItem_Bool(IDX_COMPRESS_SEPARATE_ITEMS, false);
if (IsMultiItemMode())
@ -1413,7 +1412,6 @@ void CCompressDialog::UpdateItemOutputGroupControls(unsigned groupIndex)
void CCompressDialog::UpdateSeparateItemModeControls()
{
ShowItem_Bool(IDX_COMPRESS_SEPARATE_ITEMS, false);
ShowItem_Bool(IDB_COMPRESS_OUTPUT_PATHS, false);
RefreshArchivePathInfo();
}
@ -2195,12 +2193,6 @@ bool CCompressDialog::BrowseArchivePath(UString &path, bool allowFormatChange, b
}
void CCompressDialog::OnButtonOutputPaths()
{
return;
}
void CCompressDialog::OnButtonSetArchivePath(unsigned index)
{
if (IsMultiItemMode())
@ -4745,325 +4737,6 @@ void CCompressDialog::ShowOptionsString()
}
static void OutputPathsToText(const UStringVector &paths, UString &s)
{
s.Empty();
FOR_VECTOR (i, paths)
{
if (i != 0)
s.Add_LF();
s += paths[i];
}
}
static void GetItemDisplayName(const UString &itemPath, UString &name)
{
UString dirPrefix;
SplitPathToParts_Smart(itemPath, dirPrefix, name);
if (name.IsEmpty())
name = ExtractFileNameFromPath(itemPath);
if (name.IsEmpty())
name = itemPath;
}
static void BuildItemDisplayNames(const UStringVector &itemPaths, UStringVector &displayNames)
{
UStringVector baseNames;
FOR_VECTOR (i, itemPaths)
{
UString name;
GetItemDisplayName(itemPaths[i], name);
baseNames.Add(name);
}
displayNames.Clear();
FOR_VECTOR (i, itemPaths)
{
bool needFullPath = false;
FOR_VECTOR (k, baseNames)
if (i != k && baseNames[i].IsEqualTo_NoCase(baseNames[k]))
{
needFullPath = true;
break;
}
displayNames.Add(needFullPath ? itemPaths[i] : baseNames[i]);
}
}
static bool ResolveItemDisplayName(
const UStringVector &availableItems,
const UStringVector &displayNames,
const UString &token,
UString &itemPath)
{
FOR_VECTOR (i, availableItems)
if (token.IsEqualTo_NoCase(displayNames[i]) ||
token.IsEqualTo_NoCase(availableItems[i]))
{
itemPath = availableItems[i];
return true;
}
return false;
}
static void OutputItemArcPathsToText(
const UStringVector &owners,
const UStringVector &paths,
const UStringVector &availableItems,
UString &s)
{
s.Empty();
UStringVector displayNames;
BuildItemDisplayNames(availableItems, displayNames);
unsigned numPairs = owners.Size();
if (numPairs > paths.Size())
numPairs = paths.Size();
for (unsigned i = 0; i < numPairs; i++)
{
if (i != 0)
s.Add_LF();
UString owner = owners[i];
FOR_VECTOR (k, availableItems)
if (availableItems[k] == owners[i])
{
owner = displayNames[k];
break;
}
s += owner;
s += L'>';
s += paths[i];
}
}
static void TextToOutputPaths(const UString &text, UStringVector &paths)
{
paths.Clear();
UString cur;
for (unsigned i = 0;; i++)
{
const wchar_t c = (i == text.Len()) ? 0 : text[i];
if (c == '\r')
continue;
if (c == '\n' || c == 0)
{
cur.Trim();
if (!cur.IsEmpty())
AddUniqueString(paths, cur);
cur.Empty();
if (c == 0)
break;
continue;
}
cur += c;
}
}
static bool TextToOutputItemArcPaths(
const UString &text,
const UStringVector &availableItems,
UStringVector &owners,
UStringVector &paths,
UString &errorMessage)
{
owners.Clear();
paths.Clear();
errorMessage.Empty();
UStringVector displayNames;
BuildItemDisplayNames(availableItems, displayNames);
UString cur;
unsigned lineNumber = 1;
for (unsigned i = 0;; i++)
{
const wchar_t c = (i == text.Len()) ? 0 : text[i];
if (c == '\r')
continue;
if (c == '\n' || c == 0)
{
cur.Trim();
if (!cur.IsEmpty())
{
const int sep = cur.Find(L'>');
if (sep < 0)
{
errorMessage = L"Line ";
errorMessage.Add_UInt32((UInt32)lineNumber);
errorMessage += L": use item>output path";
return false;
}
UString itemToken = cur.Left((unsigned)sep);
UString path = cur.Ptr(sep + 1);
itemToken.Trim();
path.Trim();
if (!itemToken.IsEmpty() && itemToken.Back() == L'-')
{
itemToken.DeleteBack();
itemToken.Trim();
}
if (!path.IsEmpty() && path.Back() == L';')
{
path.DeleteBack();
path.Trim();
}
if (itemToken.Len() >= 2 && itemToken[0] == L'"' && itemToken.Back() == L'"')
{
itemToken.DeleteBack();
itemToken.DeleteFrontal(1);
itemToken.Trim();
}
if (path.Len() >= 2 && path[0] == L'"' && path.Back() == L'"')
{
path.DeleteBack();
path.DeleteFrontal(1);
path.Trim();
}
if (itemToken.IsEmpty() || path.IsEmpty())
{
errorMessage = L"Line ";
errorMessage.Add_UInt32((UInt32)lineNumber);
errorMessage += L": use item>output path";
return false;
}
UString itemPath;
if (!ResolveItemDisplayName(availableItems, displayNames, itemToken, itemPath))
{
errorMessage = L"Line ";
errorMessage.Add_UInt32((UInt32)lineNumber);
errorMessage += L": unknown selected item: ";
errorMessage += itemToken;
return false;
}
owners.Add(itemPath);
paths.Add(path);
}
cur.Empty();
if (c == 0)
break;
lineNumber++;
continue;
}
cur += c;
}
return true;
}
bool COutputArcPathsDialog::OnInit()
{
_pathsEdit.Attach(GetItem(IDE_COMPRESS_OUTPUT_PATHS));
if (PerItemMode)
{
UString info = L"Use item>output path, one mapping per line:";
SetItemText(IDT_COMPRESS_OUTPUT_PATHS_INFO, info);
ShowItem_Bool(IDB_COMPRESS_OUTPUT_PATHS_ADD, false);
}
UString s;
if (PerItemMode)
OutputItemArcPathsToText(Owners, Paths, AvailableItems, s);
else
OutputPathsToText(Paths, s);
_pathsEdit.SetText(s);
NormalizePosition();
return CModalDialog::OnInit();
}
void COutputArcPathsDialog::AddPathFromBrowse()
{
UString currentText;
_pathsEdit.GetText(currentText);
UStringVector currentPaths;
TextToOutputPaths(currentText, currentPaths);
UString path;
if (!currentPaths.IsEmpty())
path = currentPaths.Back();
else if (!Paths.IsEmpty())
path = Paths.Back();
else if (!cd->_outputArcPaths.IsEmpty())
path = cd->_outputArcPaths.Back();
else
cd->GetFinalPath_Smart(path);
bool formatWasChanged;
if (!cd->BrowseArchivePath(path, false, formatWasChanged))
return;
UString s;
_pathsEdit.GetText(s);
s.TrimRight();
if (!s.IsEmpty())
s.Add_LF();
s += path;
_pathsEdit.SetText(s);
}
bool COutputArcPathsDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
{
switch (buttonID)
{
case IDB_COMPRESS_OUTPUT_PATHS_ADD:
{
AddPathFromBrowse();
return true;
}
}
return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
}
void COutputArcPathsDialog::OnOK()
{
UString s;
_pathsEdit.GetText(s);
if (PerItemMode)
{
UString errorMessage;
if (!TextToOutputItemArcPaths(s, AvailableItems, Owners, Paths, errorMessage))
{
MessageBoxW(*this, errorMessage, L"7-Zip", MB_ICONERROR);
return;
}
}
else
TextToOutputPaths(s, Paths);
if (Paths.IsEmpty())
{
MessageBoxW(*this, L"Specify at least one output path", L"7-Zip", MB_ICONERROR);
return;
}
CModalDialog::OnOK();
}
// ---------- OPTIONS ----------

View file

@ -148,7 +148,6 @@ struct CBool1
class CCompressDialog: public NWindows::NControl::CModalDialog
{
public:
friend class COutputArcPathsDialog;
CBool1 SymLinks;
CBool1 HardLinks;
CBool1 AltStreams;
@ -416,7 +415,6 @@ public:
void EnableMultiCombo(unsigned id);
void FormatChanged(bool isChanged);
void OnButtonOutputPaths();
void OnButtonSetArchivePath(unsigned index);
void OnButtonSetArchive();
bool IsSFX();
@ -463,36 +461,6 @@ public:
};
class COutputArcPathsDialog: public NWindows::NControl::CModalDialog
{
CCompressDialog *cd;
NWindows::NControl::CEdit _pathsEdit;
void AddPathFromBrowse();
virtual bool OnInit() Z7_override;
virtual bool OnButtonClicked(unsigned buttonID, HWND buttonHWND) Z7_override;
virtual void OnOK() Z7_override;
public:
UStringVector Owners;
UStringVector Paths;
bool PerItemMode;
UStringVector AvailableItems;
INT_PTR Create(HWND wndParent = NULL)
{
BIG_DIALOG_SIZE(320, 200);
return CModalDialog::Create(SIZED_DIALOG(IDD_COMPRESS_OUTPUT_PATHS), wndParent);
}
COutputArcPathsDialog(CCompressDialog *cdLoc):
cd(cdLoc),
PerItemMode(false)
{}
};
class COptionsDialog: public NWindows::NControl::CModalDialog

View file

@ -63,7 +63,6 @@ BEGIN
LTEXT "", IDT_COMPRESS_ARCHIVE_FOLDER, m + xArcFolderOffs, m, xc - xArcFolderOffs, 8
LTEXT "&Count:", IDT_COMPRESS_OUTPUT_PATHS_NUM, m, 20, xArcFolderOffs, 8
COMBOBOX IDC_COMPRESS_OUTPUT_PATHS_NUM, m + xArcFolderOffs, 18, 56, 80, MY_COMBO
PUSHBUTTON "Pa&ths...", IDB_COMPRESS_OUTPUT_PATHS, m + xArcFolderOffs + 62, 18, 54, bys
CONTROL "Map selected items to output archives", IDX_COMPRESS_SEPARATE_ITEMS, MY_CHECKBOX,
m + xArcFolderOffs + 122, 20, xc - xArcFolderOffs - 122, 10
@ -167,28 +166,6 @@ BEGIN
PUSHBUTTON "Help", IDHELP, bx1, by, bxs, bys
END
#define xcPaths 320
#define ycPaths 200
#define xsPaths (xcPaths + m + m)
#define ysPaths (ycPaths + m + m)
#define bxPaths1 (xsPaths - m - bxs)
#define bxPaths2 (bxPaths1 - m - bxs)
#define byPaths (ysPaths - m - bys)
IDD_COMPRESS_OUTPUT_PATHS DIALOG 0, 0, xsPaths, ysPaths MY_MODAL_DIALOG_STYLE MY_FONT
CAPTION "Output Paths"
BEGIN
LTEXT "One archive output path per line:", IDT_COMPRESS_OUTPUT_PATHS_INFO, m, m, xcPaths, 8
EDITTEXT IDE_COMPRESS_OUTPUT_PATHS, m, 20, xcPaths, ycPaths - bys - m - 24,
ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | WS_HSCROLL | ES_WANTRETURN
PUSHBUTTON "&Add...", IDB_COMPRESS_OUTPUT_PATHS_ADD, m, byPaths, bxs, bys
DEFPUSHBUTTON "OK", IDOK, bxPaths2, byPaths, bxs, bys, WS_GROUP
PUSHBUTTON "Cancel", IDCANCEL, bxPaths1, byPaths, bxs, bys
END
#ifdef UNDER_CE
#undef m

View file

@ -1,7 +1,6 @@
#define IDD_COMPRESS 4000
#define IDD_COMPRESS_2 14000
#define IDD_COMPRESS_OPTIONS 14001
#define IDD_COMPRESS_OUTPUT_PATHS 14002
#define IDC_COMPRESS_ARCHIVE 100
#define IDB_COMPRESS_SET_ARCHIVE 101
@ -23,14 +22,11 @@
#define IDG_COMPRESS_NTFS 115
#define IDC_COMPRESS_PATH_MODE 116
#define IDC_COMPRESS_MEM_USE 117
#define IDB_COMPRESS_OUTPUT_PATHS 118
// #define IDC_COMPRESS_DICTIONARY2 118
#define IDE_COMPRESS_PASSWORD1 120
#define IDE_COMPRESS_PASSWORD2 121
#define IDC_COMPRESS_ENCRYPTION_METHOD 122
#define IDE_COMPRESS_OUTPUT_PATHS 123
#define IDB_COMPRESS_OUTPUT_PATHS_ADD 124
#define IDC_COMPRESS_OUTPUT_PATHS_NUM 125
#define IDT_COMPRESS_ARCHIVE_FOLDER 130
@ -81,7 +77,6 @@
#define IDT_COMPRESS_MEMORY_DE 4018
#define IDX_COMPRESS_DEL 4019
#define IDT_COMPRESS_OUTPUT_PATHS_INFO 4020
#define IDT_COMPRESS_OUTPUT_PATHS_NUM 4021
#define IDX_COMPRESS_SEPARATE_ITEMS 4022

View file

@ -37,7 +37,7 @@ UString HResultToMessage(HRESULT errorCode);
static void AddUniquePath(UStringVector &paths, const UString &path)
{
FOR_VECTOR (i, paths)
if (paths[i] == path)
if (path.IsEqualTo_NoCase(paths[i]))
return;
paths.Add(path);
}
@ -50,7 +50,8 @@ static void PrepareWorkingDirForArchivePath(const UString &userArchivePath, CUpd
if (workDirInfo.Mode != NWorkDir::NMode::kCurrent)
{
FString fullPath;
MyGetFullPathName(us2fs(userArchivePath), fullPath);
if (!MyGetFullPathName(us2fs(userArchivePath), fullPath))
return;
FString namePart;
options.WorkingDir = GetWorkDir(workDirInfo, fullPath, namePart);
CreateComplexDir(options.WorkingDir);

View file

@ -1,2 +0,0 @@
# 7-Zip on GitHub
7-Zip website: [7-zip.org](https://7-zip.org)

View file

@ -1,503 +0,0 @@
# 7-Zip Fork 2026.3.19.0
Fork customizado do 7-Zip, desenvolvido para consolidar melhorias funcionais, visuais e operacionais sobre a base do projeto original.
Custom 7-Zip fork created to consolidate functional, visual, and operational improvements on top of the original project.
Projeto upstream: [7-zip.org](https://7-zip.org)
Fork repository: <https://github.com/fernandoncidade/7zip>
## Languages
- [Português](#portugues)
- [English](#english)
---
## Português
## Visão geral
Este repositório reúne um fork do 7-Zip com foco em ampliar o fluxo de empacotamento pela GUI, personalizar a identidade do projeto, consolidar a geração do instalador com ativos locais e melhorar a manutenção do build e da desinstalação.
## Objetivos deste fork
- permitir múltiplos caminhos de saída em uma única operação de compressão
- permitir múltiplos grupos independentes na janela `Adicionar ao arquivo compactado`
- manter identidade própria do fork na janela `Sobre`, no instalador e no Painel de Controle
- usar `Lang\` na raiz como base principal de traduções
- gerar o instalador localmente sem depender do download do instalador oficial
- tornar o processo de desinstalação mais limpo e menos intrusivo
## Implementações consolidadas
### 1. Janela `Adicionar ao arquivo compactado`
A interface de compressão foi ampliada para suportar cenários multi-saída e multi-item de forma nativa.
#### Múltiplas saídas por grupo
- o campo `Quantidade` define quantos caminhos de saída o grupo terá
- cada saída possui seu próprio campo `Arquivo` e seu próprio botão `...`
- o grupo suporta até 5 saídas
- apenas a primeira saída nasce preenchida com o caminho sugerido
- saídas adicionais passam a nascer vazias para preenchimento manual
- isso corrige o comportamento de replicar automaticamente `Download.7z` para todas as linhas extras
#### Grupos independentes por item selecionado
- quando o usuário seleciona mais de um arquivo ou pasta na GUI principal e clica em `Adicionar`, a janela de compressão abre um grupo por item selecionado
- cada grupo possui:
- identificação do item de origem
- campo `Quantidade`
- conjunto próprio de campos `Arquivo`
- exemplo:
- `D:\DOWNLOADS\Download`
- `D:\DOWNLOADS\teams-files-2026-03-19`
- a janela abre dois grupos independentes, um para cada item
#### Layout dinâmico em tempo de execução
- a altura da janela se ajusta conforme a quantidade de saídas habilitadas
- a interface deixa de reservar espaço fixo para cinco linhas o tempo todo
- ao aumentar ou reduzir a `Quantidade`, os controles são reposicionados dinamicamente
#### Regras e validações
- a primeira saída continua usando o caminho padrão sugerido
- saídas extras não são preenchidas automaticamente
- o sistema valida inconsistências entre caminhos antes de concluir a operação
- mudanças de formato e SFX atualizam os nomes de forma segura sem preencher linhas vazias artificialmente
#### Correções de nomes e extensões
- correção do problema `Download.7z.7z`
- correção do problema inicial `.7z..7z`
- correção do tratamento de diretórios com barra final
- atualização segura de extensões quando o formato muda
- preservação correta do nome-base do item selecionado
### 2. Janela `Sobre o 7-Zip`
A janela `Sobre o 7-Zip` foi customizada para manter as informações originais do projeto e incluir a identidade do fork.
Ela passou a exibir:
- bloco original do 7-Zip
- observação informando que esta versão refere-se a um fork customizado
- identificação de versão do fork
- data do fork
- copyright do desenvolvedor
- linha de repositório do fork
- linha final informando que o 7-Zip é software grátis
Conteudo adicional exibido:
- `Esta versão refere-se a um fork, customizado para atender as necessidades do desenvolvedor abaixo.`
- `7-Zip 2026.3.19.0 (x64)`
- `19/03/2026`
- `Copyright (c) 2026 Fernando Nillsson Cidade`
- `Repositório: https://github.com/fernandoncidade/7zip`
- `O 7-Zip é um software grátis`
### 3. Idiomas e localização
O sistema de idiomas foi reorganizado para adotar `Lang\` na raiz do repositório como fonte principal de verdade.
#### Estrutura adotada
- `Lang\` na raiz é a base canônica de idiomas
- as cópias usadas em runtime são sincronizadas para:
- `CPP\7zip\Bundles\Fm\x64\Lang`
- `CPP\7zip\UI\FileManager\x64\Lang`
- `dist\installer-full\stage\Lang`
#### Entradas novas de tradução
- `4021`: tradução de `Quantidade`
- bloco `2900`: extensão do conteúdo da janela `Sobre`
#### Ajustes realizados
- inclusão das novas traduções para a interface expandida
- normalização dos arquivos para evitar `Error in Lang file`
- sincronização automática dos diretórios de idioma usados em runtime
- alinhamento entre a janela `Sobre` e os arquivos `Lang`
### 4. Pipeline de build e geração do instalador
O fluxo de empacotamento foi reorganizado para produzir o instalador localmente, sem depender do download do instalador oficial.
#### O que mudou
- `dist\` passou a ser o diretório principal de saída
- o `stage` do instalador é montado a partir dos binários compilados localmente
- o processo deixou de depender de `Invoke-WebRequest` e da extração do instalador oficial
- arquivos estáticos passaram a ser obtidos do próprio repositório
#### Fontes locais usadas
- `InstallerAssets\7-zip.chm`
- `InstallerAssets\History.txt`
- `InstallerAssets\descript.ion`
- `DOC\License.txt`
- `DOC\readme.txt`
- `Lang\*`
- binários compilados localmente
#### Saída final do instalador
O build deste fork gera:
- `dist\7z2026.3.19.0-x64.exe`
### 5. Metadados no Painel de Controle
Ao instalar o pacote gerado por este fork, a entrada do Windows passa a exibir:
- Nome: `7-Zip 2026.3.19.0 (x64)`
- Editor: `Fernando Nillsson Cidade`
- Versão: `2026.3.19.0`
Esses dados são gravados pelo instalador na chave:
- `Software\Microsoft\Windows\CurrentVersion\Uninstall\7-Zip`
### 6. Desinstalador
O desinstalador foi ajustado para melhorar a remoção dos arquivos do produto, com foco especial nas DLLs usadas pelo shell.
#### Melhorias aplicadas
- limpeza explícita de arquivos temporários como `7-zip.dll.tmp`
- tentativa de elevação administrativa quando necessário
- descarregamento menos intrusivo da shell extension
- remoção mais segura de DLLs em uso
- preservação do Windows Explorer, evitando fechamento global das janelas e desaparecimento temporário da barra de tarefas
#### Comportamento esperado
- desinstalação mais limpa
- menor chance de sobras após remoção
- fallback para reinicialização apenas quando realmente necessário
### 7. Robustez do build local
Foram adicionados ajustes para reduzir problemas de build incremental inconsistente.
#### Consolidações feitas
- dependências explícitas entre `CompressDialog`, `UpdateGUI` e `Update.h`
- dependências explícitas de `AboutDialog.rc` e `AboutDialogRes.h` para regenerar `resource.res`
- espelhamento do `7zFM.exe` final para `CPP\7zip\UI\FileManager\x64\7zFM.exe`
- sincronização automática dos idiomas usados pelos binários locais
Isso reduz cenários como:
- alteração de recurso `.rc` sem refletir no binário
- relink com objetos antigos e cabeçalhos novos
- uso de diretórios `Lang` desatualizados ao lado do executável
## Estrutura relevante do projeto
- `CPP\`
- código-fonte principal do 7-Zip
- `C\Util\7zipInstall\`
- instalador
- `C\Util\7zipUninstall\`
- desinstalador
- `Lang\`
- base principal de idiomas
- `InstallerAssets\`
- arquivos estáticos do instalador
- `DOC\`
- documentação empacotada
- `dist\`
- saída final de build e distribuição
## Como gerar o projeto
O fluxo principal de build do instalador está no script:
- `.vscode\7zip-installer-task.ps1`
### Ações disponíveis
- `build`
- compila os binários, monta o stage e gera o instalador
- `run`
- executa o instalador já gerado
- `build-run`
- compila e executa
- `sync-lang`
- normaliza e sincroniza os arquivos de idioma
### Exemplo
```powershell
pwsh -NoProfile -ExecutionPolicy Bypass -File .vscode\7zip-installer-task.ps1 build
```
## Resultado consolidado deste fork
Este fork entrega um 7-Zip com:
- múltiplas saídas por compressão
- grupos independentes por item selecionado
- interface adaptativa em tempo de execução
- identidade própria na janela `Sobre`, no instalador e no sistema
- suporte de idioma para os novos elementos da interface
- instalador gerado localmente
- metadados personalizados no Painel de Controle
- desinstalador mais seguro
- fluxo de build mais confiável para manutenção local
## Créditos
- Projeto original: Igor Pavlov
- Fork e customizações deste repositório: Fernando Nillsson Cidade
---
## English
## Overview
This repository contains a 7-Zip fork focused on expanding the GUI compression workflow, customizing project identity, consolidating installer generation with local assets, and improving build and uninstall maintenance.
## Goals of this fork
- allow multiple output paths in a single compression operation
- allow multiple independent groups inside the `Add to Archive` window
- keep a custom fork identity in the About dialog, installer, and Control Panel entry
- use root `Lang\` as the main translation source
- generate the installer locally without depending on the official installer download
- make uninstall behavior cleaner and less intrusive
## Consolidated implementations
### 1. `Add to Archive` window
The compression interface was extended to support multi-output and multi-item workflows natively.
#### Multiple outputs per group
- the `Quantity` field defines how many output paths a group will have
- each output has its own `Archive` field and its own `...` button
- each group supports up to 5 outputs
- only the first output is prefilled with the suggested default path
- additional outputs start blank and ready for manual input
- this fixes the previous behavior where `Download.7z` was duplicated to all extra lines
#### Independent groups per selected item
- when the user selects more than one file or folder in the main GUI and clicks `Add`, the compression window opens one group per selected item
- each group has:
- source item identification
- `Quantity`
- its own `Archive` fields
- example:
- `D:\DOWNLOADS\Download`
- `D:\DOWNLOADS\teams-files-2026-03-19`
- the dialog opens two independent groups, one for each item
#### Dynamic runtime layout
- the window height adapts to the number of enabled outputs
- the UI no longer reserves space for five fixed lines all the time
- when `Quantity` changes, controls are repositioned dynamically
#### Rules and validations
- the first output keeps using the suggested default path
- extra outputs are not auto-filled
- the system validates conflicting paths before completing the operation
- format and SFX changes update names safely without artificially filling empty lines
#### Name and extension fixes
- fixed `Download.7z.7z`
- fixed the initial `.7z..7z`
- fixed directory names with trailing separators
- safely updates extensions when format changes
- preserves the correct base name of the selected item
### 2. `About 7-Zip` window
The `About 7-Zip` dialog was customized to preserve the original project information while adding the fork identity.
It now displays:
- the original 7-Zip block
- a note explaining that this build is a customized fork
- fork version identification
- fork date
- developer copyright
- fork repository line
- a final line stating that 7-Zip is free software
Additional fork content:
- `This version refers to a fork customized to meet the needs of the developer below.`
- `7-Zip 2026.3.19.0 (x64)`
- `19/03/2026`
- `Copyright (c) 2026 Fernando Nillsson Cidade`
- `Repository: https://github.com/fernandoncidade/7zip`
- `7-Zip is free software`
### 3. Languages and localization
The language system was reorganized so that root `Lang\` becomes the primary source of truth.
#### Adopted structure
- root `Lang\` is the canonical language base
- runtime copies are synchronized to:
- `CPP\7zip\Bundles\Fm\x64\Lang`
- `CPP\7zip\UI\FileManager\x64\Lang`
- `dist\installer-full\stage\Lang`
#### New translation entries
- `4021`: translation for `Quantity`
- `2900` block: extended content for the About dialog
#### Applied adjustments
- added translations for the expanded interface
- normalized language files to avoid `Error in Lang file`
- synchronized runtime language directories automatically
- aligned the About dialog content with `Lang` resources
### 4. Build pipeline and installer generation
The packaging flow was reorganized to produce the installer locally without relying on the official installer download.
#### What changed
- `dist\` became the main output directory
- the installer `stage` is assembled from locally compiled binaries
- the process no longer depends on `Invoke-WebRequest` or extracting the official installer
- static files now come from the repository itself
#### Local sources used by the installer
- `InstallerAssets\7-zip.chm`
- `InstallerAssets\History.txt`
- `InstallerAssets\descript.ion`
- `DOC\License.txt`
- `DOC\readme.txt`
- `Lang\*`
- locally compiled binaries
#### Final installer output
This fork now generates:
- `dist\7z2026.3.19.0-x64.exe`
### 5. Control Panel metadata
When the package generated by this fork is installed, the Windows entry shows:
- Name: `7-Zip 2026.3.19.0 (x64)`
- Publisher: `Fernando Nillsson Cidade`
- Version: `2026.3.19.0`
These values are written by the installer to:
- `Software\Microsoft\Windows\CurrentVersion\Uninstall\7-Zip`
### 6. Uninstaller
The uninstaller was adjusted to improve product file removal, with special attention to shell-related DLLs.
#### Applied improvements
- explicit cleanup of temporary files such as `7-zip.dll.tmp`
- administrative elevation attempt when needed
- less intrusive shell extension unloading
- safer removal of DLLs in use
- preserves Windows Explorer behavior, avoiding global window shutdown and temporary taskbar disappearance
#### Expected behavior
- cleaner uninstall flow
- lower chance of leftovers after removal
- reboot fallback only when truly necessary
### 7. Local build robustness
Additional safeguards were added to reduce inconsistent incremental build issues.
#### Consolidated changes
- explicit dependencies between `CompressDialog`, `UpdateGUI`, and `Update.h`
- explicit dependencies from `AboutDialog.rc` and `AboutDialogRes.h` to regenerate `resource.res`
- mirroring of the final `7zFM.exe` to `CPP\7zip\UI\FileManager\x64\7zFM.exe`
- automatic synchronization of the language folders used by local test binaries
This helps avoid cases such as:
- `.rc` changes not being reflected in the final binary
- relinking old objects with new headers
- running local binaries with outdated `Lang` folders next to the executable
## Relevant project structure
- `CPP\`
- main 7-Zip source code
- `C\Util\7zipInstall\`
- installer
- `C\Util\7zipUninstall\`
- uninstaller
- `Lang\`
- main language base
- `InstallerAssets\`
- local installer assets
- `DOC\`
- packaged documentation
- `dist\`
- final build and distribution output
## How to build
The main installer build flow is handled by:
- `.vscode\7zip-installer-task.ps1`
### Available actions
- `build`
- compiles binaries, assembles the stage, and generates the installer
- `run`
- runs the generated installer
- `build-run`
- builds and runs
- `sync-lang`
- normalizes and synchronizes language files
### Example
```powershell
pwsh -NoProfile -ExecutionPolicy Bypass -File .vscode\7zip-installer-task.ps1 build
```
## Consolidated result of this fork
This fork provides a 7-Zip build with:
- multiple outputs per compression operation
- independent groups per selected item
- runtime-adaptive UI behavior
- custom fork identity in the About dialog, installer, and system metadata
- language support for the new UI elements
- locally generated installer output
- customized Control Panel metadata
- safer uninstall behavior
- a more reliable local maintenance and build workflow
## Credits
- Original project: Igor Pavlov
- Fork and customizations in this repository: Fernando Nillsson Cidade