在提交到 CodeProject 之前剥离 zip 文件中不需要的文件的工具 - 第 2 版






4.17/5 (6投票s)
一篇关于 CPZipStripper 一些小修改的文章

引言
我最好是复制 Nish 最初文章中的介绍。
CPZipStripper 是一个简单的工具,我在编辑和提交 CodeProject 文章时一直在使用它,它所做的就是从 zip 文件中删除不需要的文件——例如debug 和 obj 文件夹文件、SUO 文件、PDB 文件、APS 文件等,仅举几例。它是一个一键式工具——这样您就不必花费时间在 WinZip 或类似工具中打开 zip 文件,然后手动删除不需要的文件。
如果您尝试使用 CPZipStripper 的原始版本,并且“Modify Configuration”button
在您喜欢的编辑器中打开了配置文件进行编辑,那么您就不需要此版本。
我在下载(上方)中包含了此工具的两个版本。版本 1.1 已方便地使用 VS 2003 和 .NET 1.1 构建。版本 1.2 使用 VS 2008 以 .NET 2.0 为目标构建。
背景
最近在写我为 CP 写的第一篇文章时,我偶然发现了 CPZipStripper,并发现它是一个非常有用的工具。它在创建包含源代码的 zip 文件以包含在文章中时节省了很多时间。在使用它来撰写文章后,我开始探索它的一些其他功能。当我点击“Modify Configuration”button
查看它做了什么时,我惊讶地收到一条错误消息,提示“您的系统没有指定的 XML 编辑器”,因为我确实有一个 XML 编辑器。我喜欢的一个。
在调查代码时,我找到了导致我系统出现此问题的部分。我使用的 XML 编辑器在安装时没有将自己设置为“Editor”,它已经做了所有必要的注册表条目,以便在资源管理器中双击 XML 文件时使用它,但所有这些都是使用 **Open verb** 完成的。CPZipStripper 正在寻找 **Edit verb**。
首先,我开始摆弄注册表,在备份之后,但很快意识到它太复杂了,而且,我真的不知道我在做什么。我想到修改源代码会更简单,所以我恢复了我的注册表并开始修改源代码。现在有些人会说我也不知道我在做什么,但我还是继续了。除了更改代码以允许它尝试 `Open` 编辑器(如果 `Edit` 选项失败)外,我还决定允许用户为该工具选择一个默认编辑器。在做出适合我自己的更改后,我想到其他人可能会发现这很有用。因此,我联系了 CPZIPStripper 的原始作者 Nishant Sivakumar,他欣然同意我提交这篇文章。
Using the Code
请同时阅读原始的 `CPZipStripper` 文章和这篇。
我对原始工具代码所做的更改主要在“Modify Configuration”`button` 的 Click 事件处理程序中。
我选择实施更改的方法需要存储用户的首选项,因此我开发了一个设置类,在下面的代码中引用为 `zsSettings`。`Settings Class` 稍后介绍。
原始代码
private void BtnConfig_Click(object sender, System.EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo();
try
{
psi.FileName = cfgxmlpath;
psi.Verb = "Edit";
psi.UseShellExecute = true;
Process.Start(psi);
}
catch(Exception)
{
MessageBox.Show(
"Your system does not have a specified XML editor.",
"Error : XML editor missing!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
V 1.1 的新代码
private void BtnConfig_Click(object sender, System.EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo();
bool edited = false;
if (!this.zsSettings.SetByUser)
{
try
{
// Look for system default Editor
psi.FileName = cfgxmlpath;
psi.Verb = "Edit";
psi.UseShellExecute = true;
Process.Start(psi);
edited = true;
}
catch (Exception)
{
// No App listed for Edit, try Open.
try
{
psi.FileName = cfgxmlpath;
psi.Verb = "Open";
psi.UseShellExecute = true;
Process.Start(psi);
edited = true;
}
catch
{
if (!this.zsSettings.SetByUser)
{
// No Edit OR Open, Ask user for App.
if (MessageBox.Show("Your system does not have a
specified XML editor." + Environment.NewLine +
"Do you want to select a default application
to edit XML files?", "No Default XML Editor Found!",
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) ==
DialogResult.Yes)
{
this.GetNewXmlEditor();
}
}
}
}
}
if (!edited)
{
try
{
string editorfile = (string)this.zsSettings.XmlEditor;
if (cfgxmlpath.IndexOf(" ") >= 0)
{
Process.Start(editorfile, "\"" + cfgxmlpath + "\"");
}
else
{
Process.Start(editorfile, cfgxmlpath);
}
}
catch
{
MessageBox.Show("This tool is unable to edit the configuration file." +
Environment.NewLine +
"Close this application and try editing it manually,
then restart this program.");
}
}
}
V 1.2 的新代码
private void BtnConfig_Click(object sender, System.EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo();
bool edited = false;
if (!this.zsSettings.SetByUser)
{
try
{
// Look for a system default editor
psi.FileName = configXmlPath;
psi.Verb = "Edit";
psi.UseShellExecute = true;
Process.Start(psi);
edited = true;
}
catch (Exception)
{
// No App listed for Edit, try Open.
try
{
psi.FileName = configXmlPath;
psi.Verb = "Open";
psi.UseShellExecute = true;
Process.Start(psi);
edited = true;
}
catch
{
if (!this.zsSettings.SetByUser)
{
// No Edit OR Open, Ask user for App.
if (MessageBox.Show("Your system does not
have a specified XML editor." +
Environment.NewLine + "Do you want to
select a default application
to edit XML files?",
"No Default XML Editor Found!",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning) ==
DialogResult.Yes)
{
this.GetNewXmlEditor();
}
}
}
}
}
if (!edited)
{
try
{
string editorfile = this.zsSettings.XmlEditor;
if (this.configXmlPath.Contains(" "))
{
Process.Start(editorfile, "\"" +
this.configXmlPath + "\"");
}
else
{
Process.Start(editorfile, this.configXmlPath);
}
}
catch
{
MessageBox.Show("This tool is unable to edit the
configuration file." + Environment.NewLine +
"Close this application and try editing it
manually, then restart this program.");
}
}
}
两个新版本几乎相同,任何差异都是为了适应 .NET 1.1 和 .NET 2.0 的变化。
方法 উপরের部分 包含在一个 `if
` 语句中,该语句测试用户是否为该工具设置了明确的默认编辑器。如果他们设置了,就没有必要在 Windows 中搜索系统默认值。
在 `if
` 块内有两个嵌套的 `try/catch `块。外层块是来自原始 `CPZipStripper` 的代码。如果它找到已注册的编辑器,则没问题,它会加载配置文件进行编辑。编辑器关闭后,它会设置一个标志以供以后使用。如果发生 `Exception`,则执行内部 `try/catch `块。`try
` 部分基本上重复了外部块的过程,但用 `Open` 替换了 **Edit verb**。同样,如果找到已注册的编辑器,则使用它,然后设置标志。内部块的 `Exception` 会导致用户有机会选择一个默认编辑器,或者不选择。如果他们选择了一个编辑器,他们会看到 `"#xmlec">XMLEditorChooser` 对话框(下面介绍),然后控制权才会传递到方法的最后一部分,如果他们选择不设置默认编辑器也是如此。最后一部分检查配置是否已编辑。如果已编辑,则方法返回。否则,编辑器设置将存储在 `string
` 中。(我最初是这样做的,同时在调试,而且它确实没有引起任何问题,而且它更具可读性,所以我把它留下了)。检查配置文件路径是否存在空格。如果存在任何空格,则路径将用引号括起来。
编辑器文件以配置文件作为唯一 `parameter` 启动。如果这导致 `Exception`,则会通知用户他们没有希望了,最好放弃。
XMLEditorChooser 对话框
这两个版本与原始版本在视觉上的明显区别是添加了一个“Set XML Editor”`button`。单击此 `button` 会显示一个对话框,允许您执行与选择该工具将使用的 XML 编辑器相关的各种任务。
这是对话框

有三个互斥的选项。从下往上
- 如果您选中“Remove Current Settings”`CheckBox`,无论表单上的任何其他设置如何,当您单击 OK 时,设置将恢复为默认值
XMLEditor
- notepad.exeSetByUser
-false
这意味着,如果您单击主窗体上的“Modify Configuration”`button`,没有默认编辑器,并且拒绝选择一个,则 Notepad 将被用作默认编辑器。
我这样做是因为我知道你们所有这些“男人”都会只使用 Notepad 来编辑 XML 和 HTML。- 如果您选中“Default to Notepad”`CheckBox`,但不选中“Remove Current Settings”`CheckBox`,无论编辑器 `TextBox `的内容如何,设置都将设置为
XMLEditor
- notepad.exeSetByUser
-true
这意味着,当您单击主窗体上的“Modify Configuration”`button` 时,Notepad 将用作编辑器,而无需进一步选择。- 如果您将编辑器 `TextBox `内容设置为有效的可执行文件,但没有选中任何 `CheckBoxes`,则设置将设置为
XMLEditor
- 来自 `TextBox` 的可执行文件SetByUser
-true
这意味着,当您单击主窗体上的“Modify Configuration”`button` 时,您选择的可执行文件将被用作编辑器,而无需进一步考虑。
对话框代码
public partial class XMLEditorChooser : Form
{
private CPZipStripperSettings settings;
protected XMLEditorChooser()
{
InitializeComponent();
}
internal static DialogResult Show(CPZipStripperSettings settings)
{
XMLEditorChooser chooser = new XMLEditorChooser();
// save a reference to the settings in the dialog.
chooser.settings = settings;
// Set up defaults for FileChooser
chooser.editorFileChooser.SelectedFile = settings.XmlEditor;
chooser.editorFileChooser.FileDialog.Title =
"Select a Default XML Editor";
chooser.editorFileChooser.FileDialog.Filter =
"Executable Files (*.EXE)|*.exe";
chooser.editorFileChooser.FileDialog.Multiselect = false;
return chooser.ShowDialog();
}
private void btnOK_Click(object sender, EventArgs e)
{
if (this.chboxRemoveCurrent.Checked)
{
// default settings - notepad, not set by user
this.settings.XmlEditor = "notepad.exe";
this.settings.SetByUser = false;
}
else if (this.chboxNotePad.Checked)
{
// notepad, set by user
this.settings.XmlEditor = "notepad.exe";
this.settings.SetByUser = true;
}
else
{
// Only set if file exists.
if (File.Exists(this.editorFileChooser.SelectedFile))
{
this.settings.XmlEditor =
this.editorFileChooser.SelectedFile;
this.settings.SetByUser = true;
}
}
}
}
修改设置的所有工作都在 OK `button` 的 Click 事件处理程序中完成。因此,无论您是否修改 `CheckBox`es 或 `TextBox `内容,如果您单击 Cancel,都不会进行任何更改。
我将简要提及用于选择默认编辑器的 `chooser` 控件。这是我在决定写这篇文章时为自己开发的控件。我刚刚开始开发 `FileChooserLibrary`,为了不延迟这篇文章,我做了一些糟糕的妥协,比如直接访问 `OpenFileDialog`,而不是公开相应的属性。我已经将这个库的代码包含在源代码的 .NET 1.1 版本中,但 .NET 2.0 版本只有 DLL。请随意以此为基础开发您自己的控件,但肯定有更好、更完整的解释,其中一些很可能在这里 CodeProject 上。
CPZipStripperSettings 类
之前展示的大部分代码对于 .NET 1.1 或 .NET 2.0 来说都是相同的。然而,这个类差异更大,主要是由于 .NET 2.0 处理配置文件方式的改进。
适用于 .NET 1.1 的代码
public class CPZipStripperSettings
{
private bool appSettingsChanged;
// Variables used to store the application settings.
private string xmlEditor = "notepad.exe";
private bool setByUser = false;
private string cpzsSettingsFile;
public CPZipStripperSettings()
{
//
// TODO: Add constructor logic here
//
cpzsSettingsFile = Application.LocalUserAppDataPath +
@"\CPZipStripper.config";
}
// Properties used to access the application settings variables.
public string XmlEditor
{
get
{
return this.xmlEditor;
}
set
{
if (value != this.xmlEditor)
{
this.xmlEditor = value;
appSettingsChanged = true;
}
}
}
public bool SetByUser
{
get
{
return this.setByUser;
}
set
{
if (value != this.setByUser)
{
this.setByUser = value;
appSettingsChanged = true;
}
}
}
// Serializes the class to the config file
// if any of the settings have changed.
public bool SaveAppSettings()
{
if (this.appSettingsChanged)
{
XmlSerializer cpzssSerializer = null;
try
{
// Create an XmlSerializer for the
// CPZipStripperSettings type.
cpzssSerializer = new XmlSerializer(typeof(CPZipStripperSettings));
using (StreamWriter cpzssWriter =
new StreamWriter(cpzsSettingsFile, false))
{
// Serialize this instance of the CPZipStripperSettings
// class to the config file.
cpzssSerializer.Serialize(cpzssWriter, this);
// If the FileStream is open, close it.
cpzssWriter.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
return appSettingsChanged;
}
// Deserializes the class from the config file.
public bool LoadAppSettings()
{
XmlSerializer cpzssSerializer = null;
bool fileExists = false;
try
{
// Create an XmlSerializer for the CPZipStripperSettings type.
cpzssSerializer = new XmlSerializer(typeof(CPZipStripperSettings));
FileInfo fi = new FileInfo(cpzsSettingsFile);
// If the config file exists, open it.
if (fi.Exists)
{
using (FileStream myFileStream = fi.OpenRead())
{
// Create a new instance of the CPZipStripperSettings by
// deserializing the config file.
CPZipStripperSettings myAppSettings =
(CPZipStripperSettings)cpzssSerializer.Deserialize
(myFileStream);
// Assign the property values to this instance of
// the CPZipStripperSettings class.
this.XmlEditor = myAppSettings.XmlEditor;
this.setByUser = myAppSettings.SetByUser;
fileExists = true;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (XmlEditor == null)
{
// If xmlEditor is not set, default
// to notepad.
this.XmlEditor = "notepad.exe";
this.SetByUser = false;
this.appSettingsChanged = true;
}
return fileExists;
}
}
适用于 .NET 2.0 的代码
internal sealed class CPZipStripperSettings : ApplicationSettingsBase
{
// If you used [ApplicationScopedSetting()] instead of [UserScopedSetting()],
// you would NOT be able to persist any data changes!
[UserScopedSetting()]
[SettingsSerializeAs(System.Configuration.SettingsSerializeAs.String)]
[DefaultSettingValue("notepad.exe")]
public string XmlEditor
{
get
{
return ((string)this["XmlEditor"]);
}
set
{
this["XmlEditor"] = value;
}
}
// If you used [ApplicationScopedSetting()] instead of [UserScopedSetting()],
// you would NOT be able to persist any data changes!
[UserScopedSetting()]
[SettingsSerializeAs(System.Configuration.SettingsSerializeAs.String)]
[DefaultSettingValue("false")]
public bool SetByUser
{
get
{
return (bool)this["SetByUser"];
}
set
{
this["SetByUser"] = value;
}
}
}
.NET 2.0 版本要短得多,因为它继承自 `ApplicationSettingsBase`,该类已经实现了加载和保存设置的方法。 .NET 1.1 的类必须包含这些方法。诚然,加载和保存的实际代码并不复杂,是相当标准的 `Serializer`/`FileStream` 东西,只是需要做的麻烦。另外,.NET 2.0 版本大量使用了 Attributes,一个用于指定设置的作用域,一个用于序列化类型(字符串/XML/二进制等),最后一个用于每个设置的默认值。
关注点
我在为这篇文章收集资料时学到了几件事。其中一件事是,那些编写了非常好的应用程序,无论系统如何设置都能正常工作的程序员,他们赚的钱是理所当然的!当 Nish 最初编写 `CPZipStripper` 时,它在他的系统上有效,而且几乎肯定在许多其他系统上有效。然而,它在我的系统上无效,因为我的系统上的一个应用程序的设置方式与 Nish 的系统上的相应应用程序不同。谁能想到!
历史
- 2008 年 12 月 24 日:本文首次发布