如何在 C# 应用程序中使用来自 Assembly 的嵌入式文件用于 SoundPlayer 上下文

How to use a Embedded file from Assembly for SoundPlayer contexts inside of a C# application

你好 Stack Overflow 社区,

我一直在为我正在制作的游戏制作自定义 installer/launcher,它目前在非便携式环境中工作,但是如果我想将它放入游戏中进行分发,它必须在可移植的环境中工作(即它不应该为了自己的任何需要而访问驱动器,只有其他软件的需要)/

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

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

我目前的代码如下,"programpath" 指的是从中执行文件的目录,"MainText" 是主要输出 window,这是在this 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();
        }
    }
}

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

private void button1_Click(object sender, EventArgs e)
{
    SoundPlayer player = new SoundPlayer();

    //Add TestSound.wav file to the built-in resource file Project>Properties>Resources.resx
    player.Stream = Properties.Resources.TestSound;

    //Add TestSound.wav file to a new resource file Resource1.resx
    //player.Stream = Resource1.TestSound;
    player.Play();
}

我终于弄明白了,所以我来回答我自己的问题,希望以后有困难的人能弄明白。

在 SharpDevelop 中使用 link provided by @Jimi 访问资源,并向下滚动到 "Embedding Files directly" 部分,显示访问嵌入式资源的上下文。这里需要注意的是GetManifestStream(string)的return类型是一个System.IO.Streamobject。 这很重要,因为我们需要 SoundPlayer 以某种方式接受 System.IO.Stream object。这可以通过两种方式完成,通过使用重载的构造函数或 SoundPlayer class 的 属性。由 MSDN page for the SoundPlayer Class 定义。

然而,当我测试这个时,我无法播放声音。进一步研究,我发现 Assembly.GetExecutingAssembly() 有一个名为 "GetManifestResourceNames()" 的可访问方法,return 是一个包含所有资源及其更正的 字符串数组 访问它们的名称。我能够通过创建一个名为 "ExecutingAssem" 的 Windows 表单标签并使用 instructions from DotNetPerls 创建一个名为 "ConvertStringArrayToStringJoin()" 的静态方法来查看 return,但是更改它与“。”的分隔符到“|”更好地阅读资产。

完成后,最终程序会显示嵌入式资源列表,其中包含以下行: ExecutingAssem.Text=ConvertStringArrayToStringJoin(Assembly.GetExecutingAssembly().GetManifestResourceNames());

最后的程序显示为: Program showing all the resources in the corner

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

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

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

最终,最终的应用程序播放了歌曲,并且能够将 .exe 文件移动到其他地方,并且仍然能够播放同一首歌曲,而不需要 "Resonance.wav" 需要在同一目录中。