65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (6投票s)

2008 年 12 月 24 日

CPOL

8分钟阅读

viewsIcon

33292

downloadIcon

389

一篇关于 CPZipStripper 一些小修改的文章

CPZipStripper Mainform Image

引言

我最好是复制 Nish 最初文章中的介绍。

CPZipStripper 是一个简单的工具,我在编辑和提交 CodeProject 文章时一直在使用它,它所做的就是从 zip 文件中删除不需要的文件——例如debugobj 文件夹文件、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 编辑器相关的各种任务。

这是对话框

CPZipStripper SetEditor Dialog Image

有三个互斥的选项。从下往上

  1. 如果您选中“Remove Current Settings”`CheckBox`,无论表单上的任何其他设置如何,当您单击 OK 时,设置将恢复为默认值
    • XMLEditor - notepad.exe
    • SetByUser - false
      这意味着,如果您单击主窗体上的“Modify Configuration”`button`,没有默认编辑器,并且拒绝选择一个,则 Notepad 将被用作默认编辑器。
      我这样做是因为我知道你们所有这些“男人”都会只使用 Notepad 来编辑 XML 和 HTML。
  2. 如果您选中“Default to Notepad”`CheckBox`,但不选中“Remove Current Settings”`CheckBox`,无论编辑器 `TextBox `的内容如何,设置都将设置为
    • XMLEditor - notepad.exe
    • SetByUser - true
      这意味着,当您单击主窗体上的“Modify Configuration”`button` 时,Notepad 将用作编辑器,而无需进一步选择。
  3. 如果您将编辑器 `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 日:本文首次发布
© . All rights reserved.