Программно загрузить встроенный файл ресурсов
Я недавно реализовал следующий код для программной сборки проекта /exe. В этой сборке exe я хотел хранить кучу "реальных файлов" внутри ресурсов в виде потоков.
Вот как я добавляю файлы в файл ресурсов (в единственном числе), а затем встраиваю этот файл ресурсов в параметры компилятора:
List<string> UserFiles = new List<string>();
UserFiles.AddRange(Helpers.GetFilesInFolder(this.txt_Publish_Folder.Text));
string folder = this.txt_Package_Location.Text;
folder = folder + "\\Package_" + DateTime.Now.ToLongTimeString().Replace(":", "_").Replace(".", "_");
Directory.CreateDirectory(folder);
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.IncludeDebugInformation = true;
parameters.GenerateInMemory = false;
parameters.WarningLevel = 3;
parameters.CompilerOptions = "/optimize";
parameters.OutputAssembly = folder + "\\Install_" + this.txt_AppName.Text + ".exe";
parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
if (codeProvider.Supports(GeneratorSupport.Resources))
{
string temp = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
//create temp file first, because we want to append to it so as to have a single resource file with multiple stream entries...
File.WriteAllText(temp, null);
for (int i = 0; i < UserFiles.Count; i++ )
{
byte[] FileBytes = File.ReadAllBytes(UserFiles[i]);
using (FileStream stream = new FileStream(temp, FileMode.Append))
{
using (ResourceWriter writer = new ResourceWriter(stream))
{
writer.AddResource(Path.GetFileName(UserFiles[i]), FileBytes);
}
}
}
parameters.EmbeddedResources.Add(temp);
}
CompilerResults res = codeProvider.CompileAssemblyFromFile(parameters, @"C:\HIDDENPATH\Program.cs");
Это прекрасно работает, не выдает никаких ошибок, и я могу фактически получить файл встроенного ресурса в Консольном приложении, которое я построил (тот, на который ссылается выше как Prorgam.cs), с помощью кода ниже. Однако проблема, с которой я столкнулся, заключается в том, чтобы "загрузить" этот файл ресурсов в сборку приложения / каким-то образом получить его значения... вот код, который у меня есть для этого:
static void Main(string[] args)
{
Console.WriteLine("Checking for resources... please wait...");
Assembly thisExe;
thisExe = Assembly.GetExecutingAssembly();
List<string> resources = thisExe.GetManifestResourceNames().ToList();
if(resources.Count >= 1)
{
try
{
string baseName = resources[0];
ResourceManager mgr = new ResourceManager(baseName, thisExe);
Console.WriteLine("retrieved manager...");
Console.ReadLine();
ResourceSet set = mgr.GetResourceSet(Thread.CurrentThread.CurrentCulture, true, true);
int count = set.Cast<object>().Count();
Console.WriteLine("Found [" + count.ToString() + "] embedded resources. Would you like to enumerate them?");
ConsoleKeyInfo input = Console.ReadKey();
if (input.Key == ConsoleKey.Y)
{
// Build the string of resources.
foreach (string resource in resources)
Console.WriteLine(resource);
}
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.ReadLine();
}
}
Console.ReadLine();
}
Всякий раз, когда я запускаю встроенный exe-файл, я получаю следующий результат:
Проверка ресурсов... пожалуйста, подождите... восстановленный менеджер... Не удалось найти ресурсы, подходящие для указанной культуры или нейтральной культуры. Убедитесь, что tmpCC59.tmp.resources был правильно встроен или связан со сборкой "Install_testing" во время компиляции, или что все требуемые сателлитные сборки являются загружаемыми и полностью подписаны.
Может кто-нибудь сказать, пожалуйста, почему это происходит? Я перепробовал все разные наборы культур, о которых только мог подумать, но до сих пор ничего не выходит Проблема связана с тем, как я "Встраивание" или как я "Загрузка"?
1 ответ
Хорошо, ребята, так что это то, чем я закончил, что на самом деле исправило все мои проблемы.
Вместо того, чтобы пытаться добавить "Записи ресурсов" в один "Файл ресурсов", я теперь добавляю каждый файл в качестве своего собственного ресурса непосредственно из исходного файла (не знаю, почему я подумал сделать временную копию оригинала и использование потока было необходимо, но это не так)
for (int i = 0; i < WebAppFiles.Count; i++) { parameters.EmbeddedResources.Add(WebAppFiles[i]); }
Затем, когда приходит время извлечь фактические файлы, вместо того, чтобы пытаться загрузить "набор" из одного "файла ресурсов", я просто извлекаю данные из каждого встроенного ресурса, например так:
for (int i = 0; i < resources.Count; i++) { Console.WriteLine("Extracting file: " + resources[i] + "..."); Stream stream = thisExe.GetManifestResourceStream(resources[i]); byte[] bytes = new byte[(int)stream.Length]; stream.Read(bytes, 0, bytes.Length); File.WriteAllBytes(dir + resources[i], bytes); }
Этот новый способ в основном означает, что вы сообщаете компилятору, что "эти файлы необходимы для исполняемого файла и встроены", поэтому, когда вы вызываете команду "build", он фактически выполняет всю работу за вас и читает файлы в виде байтовых массивов. и встраивает их в исполняемый файл.
Затем, с другой стороны, вы просто указываете программе, что хотите сохранить встроенные ресурсы в виде файлов, используя собственные имена файлов.
Надеюсь, это поможет кому-то еще, кто пытается это сделать... так как у каждого поста для этого типа вопроса были некоторые более сложные ответы, которые были либо неполными (например: как встраивать, но не извлекать), либо фактически не работали.