首页 > 解决方案 > 如何在 C# 应用程序中将 Assembly 中的嵌入式文件用于 SoundPlayer 上下文

问题描述

你好 Stack Overflow 社区,

我一直在为我正在制作的游戏制作自定义安装程序/启动器,它目前在非便携式环境中工作,但是如果我想把它放在游戏中进行分发,它必须在一个可移植的上下文(即它不应该访问驱动器以满足它自己的任何需求,只有其他软件的需求)/

目前,它会从驱动器中加载一首歌曲来播放,如果启动器之前从未打开过或未成功完成,它还会加载游戏的先决条件。这些文件都在 SharpDevelop 中设置为“EmbeddedResource”,它们是最终编译脚本的一部分。

但是,在代码的当前上下文中,脚本仍然必须访问驱动器才能执行所有这些功能,即使它们嵌入到最终程序中也是如此。

我到目前为止的当前代码如下,“programpath”是指正在执行文件的目录,“MainText”是主输出窗口,在这个 Stack Overflow question中可以更好地看到,label1 是一个调试行,仅用于显示当前运行命令的路径(嵌入所有东西后将被删除)。

public MainForm()
    {
        //Open Window
        InitializeComponent();
        CenterToScreen();
        //Start song
        System.Media.SoundPlayer player = new System.Media.SoundPlayer(programpath+"\\Resonance.wav");
        player.PlayLooping();
        this.Shown+=(s,e)=>{
            if(File.Exists(programpath+"\\FirstLaunch.lic")&&File.ReadAllText(programpath+"\\FirstLaunch.lic").IndexOf("Installation Successful")>=0){
            BackColor=System.Drawing.Color.OrangeRed;
            MainText.BackColor=System.Drawing.Color.OrangeRed;
            MainText.ForeColor=System.Drawing.Color.Aquamarine;
            MainText.Text="Starting game..";
            Process game = new Process();
            //placeholder executable, will be finished game executable
            game.StartInfo.FileName="D:\\UT2004\\System\\UT2004.exe";
            game.StartInfo.ErrorDialog=true;
            game.Start();
            //stop playing music, 
            player.Stop();
            //when game stops, close the launcher
            game.WaitForExit();
            Application.Exit();
            }else{
                //Start prerequisite installation
                newLaunch();
            }
        };
}
    //if the launcher has not been open before OR the last installation was not successful
        void newLaunch(){
        //Creates and makes a stream to the file
        StreamWriter writer = new StreamWriter(programpath+"\\FirstLaunch.lic");
        //Changing color scheme
            BackColor=System.Drawing.Color.Chartreuse;
            MainText.BackColor=System.Drawing.Color.Chartreuse;
            MainText.ForeColor=System.Drawing.Color.Black;
            MainText.Text="Configuring Prerequisites....\nInstalling DirectX";
            //write to file about stage
            writer.Write("Installing DirectX");
            //start DirectX installer
            Process prerequisite = new Process();
            prerequisite.StartInfo.FileName=programpath+"\\dxwebsetup.exe";
            prerequisite.StartInfo.ErrorDialog=true;
            prerequisite.Start();
            //debug line
            label1.Text=programpath+"\\dxwebsetup.exe";
            //wait for installer to finish and close
            prerequisite.WaitForExit();
            //remove refernce to DirectX installer
            prerequisite.Close();
            //write to file about stage
            writer.WriteLine("...true");
            //Changing color scheme
            BackColor=System.Drawing.Color.DarkMagenta;
            MainText.BackColor=System.Drawing.Color.DarkMagenta;
            MainText.ForeColor=System.Drawing.Color.Yellow;
            MainText.Text="Configuring Prerequisites....\nInstalling Microsoft VC++2015 Update RC3";
            //write to file about stage
            writer.Write("Installing VCRedist");
            //start VC Redistributable installer
            prerequisite.StartInfo.FileName=programpath+"\\vc_redist.x86.exe";
            prerequisite.StartInfo.ErrorDialog=true;
            prerequisite.Start();
            //debug line
            label1.Text=programpath+"\\vc_redist.x86.exe";
            //wait for installer to finish and close
            prerequisite.WaitForExit();
            //remove reference to VC Redistributable installer
            prerequisite.Close();
            //write to file about stage
            writer.WriteLine("...true");
            writer.WriteLine("Installation Successful");
            writer.Close();
            //re-open launcher from open context
            label1.Text=programpath+"\\ColorLoop.exe";
            Process.Start(programpath+"\\ColorLoop.exe");
            Application.Exit();
        }
    }
}

如何让程序播放音乐,并从其自身加载先决条件,而不是从代码中的单独驱动器文件加载。它都已经嵌入,只是没有被使用。

标签: c#windowswinforms.net-assemblyembedded-resource

解决方案


我终于弄明白了,所以我正在回答我自己的问题,希望将来挣扎的人可以弄明白。

使用@Jimi 提供的在 SharpDevelop 中访问资源的链接,并向下滚动到“直接嵌入文件”部分,显示访问嵌入式资源的上下文。这里需要注意的是 GetManifestStream(string) 的返回类型是一个System.IO.Stream对象。这很重要,因为我们需要 SoundPlayer 以某种方式接受 System.IO.Stream 对象。这可以通过两种方式完成,通过使用重载的构造函数或 SoundPlayer 类的属性。正如SoundPlayer Class 的 MSDN 页面所定义的那样。

但是,当我对此进行测试时,我无法播放声音。进一步研究,我发现 Assembly.GetExecutingAssembly() 有一个名为“GetManifestResourceNames()”的可访问方法,它返回一个包含所有资源及其更正名称的字符串数组以访问它们。通过创建一个名为“ExecutingAssem”的 Windows 窗体标签,并使用DotNetPerls 的指令创建一个名为“ConvertStringArrayToStringJoin()”的静态方法,我能够看到返回,但将其分隔符从“.”更改为 到“|” 更好地阅读资产。

有了它,最终程序将显示嵌入资源列表,其中包含以下行: ExecutingAssem.Text=ConvertStringArrayToStringJoin(Assembly.GetExecutingAssembly().GetManifestResourceNames());

最终程序显示为: 显示角落中所有资源的程序

这里有趣的是,歌曲资源(Resonance)实际上并不是 Namespace 或 MainForm 的一部分……而是它自己的名称作为资源标题。

找到丢失的信息后,我需要将程序从读取驱动器更改为读取清单流。使用使用 System.IO.Stream 对象作为参数而不是文件位置的字符串的重载构造函数,我更改了启动播放器对象的位置以使用该构造函数。

SoundPlayer player =new SoundPlayer(Assembly.GetExecutingAssembly().GetManifestResourceStream("Resonance"));

最后,最终的应用程序播放了歌曲,能够将 .exe 文件移动到其他位置,并且仍然能够播放同一首歌曲,而无需将“Resonance.wav”放在同一目录中。


推荐阅读