mickle026 404 Posted November 7, 2022 Share Posted November 7, 2022 I am trying to copy an embeded resource file to disk The file is an embeded resource, type text file called Search.php. My Resource file is called MyEmbeds.resx, the php text file is saved in the resource simply as Search, MyEmbeds.Search I am trying using (Stream output = File.Create(Path.Combine(config.CreatePersonApiServerString, "Search.php"))) { Assembly.GetExecutingAssembly().GetManifestResourceStream(MyEmbeds.Search).CopyTo(output); } My file is created so the output path is correct but it has no contents so the resource path must be the problem. ie the problem must be where its looking for the resource. Assembly.GetExecutingAssembly() So with this in mind as I might be getting the refering assembly I tried Assembly.GetAssembly(GetType()).GetManifestResourceStream(MyEmbeds.Search).CopyTo(output); And my output is still blank. What is the correct path to the assembly file of a plugin? Link to comment Share on other sites More sharing options...
Luke 37253 Posted November 7, 2022 Share Posted November 7, 2022 If there's an exception in the server log that might clue you in to the problem. Link to comment Share on other sites More sharing options...
chef 3749 Posted November 7, 2022 Share Posted November 7, 2022 I know how to do that, I do that all the time. I'm at work so I do t have the exact code which I can post later. But, get the embedded resource stream and CopyTo FileStream with your path data filled in. But yeah, FileStream and make sure you dispose. Also make sure your reflected namespace is pointing to the proper embedded resource. I can post exactly what you have to later if there is still a problem. Link to comment Share on other sites More sharing options...
mickle026 404 Posted November 7, 2022 Author Share Posted November 7, 2022 46 minutes ago, Luke said: If there's an exception in the server log that might clue you in to the problem. There's no errors in the logfile @chef, I'm at work myself now but if you can point me to the correct path, that would be brilliant, thanks. I'm doing a using clause so it's automatically disposed of. Quote Also make sure your reflected namespace is pointing to the proper embedded resource This is what I believe the problem is. Link to comment Share on other sites More sharing options...
pünktchen 1261 Posted November 7, 2022 Share Posted November 7, 2022 Something like this? using (Stream fileStream = File.Create(Path.Combine(config.CreatePersonApiServerString, "Search.php"))) { Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(MyEmbeds.Search); resourceStream.Seek(0, SeekOrigin.Begin); resourceStream.CopyTo(fileStream); } 1 Link to comment Share on other sites More sharing options...
softworkz 3349 Posted November 8, 2022 Share Posted November 8, 2022 (edited) @mickle026 - you are mixing up two fundamentally different and separate ways of dealing with resources in C# I will show you how to work with your resource in both cases 1. RESX Resources You define RESX resource by adding a RESX file. You can add many different kinds of resources to a RESX files, like strings, images, icons, binary files and text files. Strings are stored directly in the RESX file. Assuming you have MyResources.resx and added a string with the key 'MyString' and the value 'abc', you can access that value at runtime with MyResources.MyString, which will evaluate to 'abc'. But you can also add files from your project to a RESX resource file, and depending on the type of file, it will be available in the same way as a property of the resource class, either of type string or as a byte array (byte[]) in case of binary files. In your case, you have done the latter. You have added the file Search.php to your MyEmbeds.resx file as text file. (I know that from your other snippets which wouldn't have compiled otherwise). And that means, that at runtime, The MyEmbeds.Search property already contains the contents of Search.php as string!. So, the code you are looking for is File.WriteAllText(Path.Combine(config.CreatePersonApiServerString, "Search.php"), MyEmbeds.Search); Note: For this approach you do NOT need to set the Build Action of the file to "Embedded Resource" - you just leave it set to "None". 2. Embedded Resources When you want to go the way of including the file as an Embedded Resource, There is no RESX file involved You set the Build Action of the file to "Embedded Resource" It is important where the file is located within the project folder structure (because this determines the resource name) The resource name of an embedded resource is determined by several components: The project's "Default Namespace" e.g. Softworkz.MyGreatPlugin The filename e.g. MyTextFile.txt The relative folder path, separated by dots assuming the file's relative path in the project is Web\Resources\MyTextFile.txt it would be Web.Resources Now, putting it all together, the resource path would be Softworkz.MyGreatPlugin.Web.Resources.MyTextFile.txt And that's the path you need to use for calling GetManifestResourceStream(). But as developers, we should always avoid hardcoding any strings (as they might stop working when making changes or refactorings). So, a better way would be to use an existing type for reference, ideally a type that resides in the same folder as your resource file (e.g. a class MyClass). Then you would be able to do like this: using (Stream output = File.Create(Path.Combine(config.CreatePersonApiServerString, "Search.php"))) { var type = typeof(MyClass); using (var source = type.Assembly.GetManifestResourceStream(type.Namespace + ".Search.php")) { source.CopyTo(output); } } Please note the Using for the resource stream to make sure it's getting closed after use. Also note the prefix dot for the resource name building. Edited November 8, 2022 by softworkz 1 1 Link to comment Share on other sites More sharing options...
mickle026 404 Posted November 8, 2022 Author Share Posted November 8, 2022 4 hours ago, softworkz said: @mickle026 - you are mixing up two fundamentally different and separate ways of dealing with resources in C# I will show you how to work with your resource in both cases 1. RESX Resources You define RESX resource by adding a RESX file. You can add many different kinds of resources to a RESX files, like strings, images, icons, binary files and text files. Strings are stored directly in the RESX file. Assuming you have MyResources.resx and added a string with the key 'MyString' and the value 'abc', you can access that value at runtime with MyResources.MyString, which will evaluate to 'abc'. But you can also add files from your project to a RESX resource file, and depending on the type of file, it will be available in the same way as a property of the resource class, either of type string or as a byte array (byte[]) in case of binary files. In your case, you have done the latter. You have added the file Search.php to your MyEmbeds.resx file as text file. (I know that from your other snippets which wouldn't have compiled otherwise). And that means, that at runtime, The MyEmbeds.Search property already contains the contents of Search.php as string!. So, the code you are looking for is File.WriteAllText(Path.Combine(config.CreatePersonApiServerString, "Search.php"), MyEmbeds.Search); Note: For this approach you do NOT need to set the Build Action of the file to "Embedded Resource" - you just leave it set to "None". 2. Embedded Resources When you want to go the way of including the file as an Embedded Resource, There is no RESX file involved You set the Build Action of the file to "Embedded Resource" It is important where the file is located within the project folder structure (because this determines the resource name) The resource name of an embedded resource is determined by several components: The project's "Default Namespace" e.g. Softworkz.MyGreatPlugin The filename e.g. MyTextFile.txt The relative folder path, separated by dots assuming the file's relative path in the project is Web\Resources\MyTextFile.txt it would be Web.Resources Now, putting it all together, the resource path would be Softworkz.MyGreatPlugin.Web.Resources.MyTextFile.txt And that's the path you need to use for calling GetManifestResourceStream(). But as developers, we should always avoid hardcoding any strings (as they might stop working when making changes or refactorings). So, a better way would be to use an existing type for reference, ideally a type that resides in the same folder as your resource file (e.g. a class MyClass). Then you would be able to do like this: using (Stream output = File.Create(Path.Combine(config.CreatePersonApiServerString, "Search.php"))) { var type = typeof(MyClass); using (var source = type.Assembly.GetManifestResourceStream(type.Namespace + ".Search.php")) { source.CopyTo(output); } } Please note the Using for the resource stream to make sure it's getting closed after use. Also note the prefix dot for the resource name building. Thank you all for replying. Thank you to @softworkzfor the very detailed reply. I now understand what I was trying to do! All I needed was simply this: File.WriteAllText(Path.Combine(config.CreatePersonApiServerString, "Search.php"), MyEmbeds.Search); As it was an embeded resource I was trying to reference it like the plugin does to the icon file, and then read the resx container. I didnt need to reference the path to the embeded resource , all i needed to do was write the file and use the RESX container and key as the string contents! (MyEmbeds.Search). So simple when you know how! I made this much harder than it needed to be. Many thanks! 1 Link to comment Share on other sites More sharing options...
softworkz 3349 Posted November 8, 2022 Share Posted November 8, 2022 4 minutes ago, mickle026 said: I made this much harder than it needed to be. Yea, correct! 4 minutes ago, mickle026 said: Many thanks! You're welcome! Link to comment Share on other sites More sharing options...
chef 3749 Posted November 8, 2022 Share Posted November 8, 2022 (edited) Check this out it It loads binaries that aren't included in emby in an IServerEntryPoint (so on load). I will share here, because I'm kind of proud of the code I call it AssembyLoader. I think it's cool. using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Logging; namespace Emby.MovieLens.ML_Net { // ReSharper disable once InconsistentNaming // ReSharper disable once UnusedType.Global public class AssemblyManager : IServerEntryPoint { private static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); private static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); private IApplicationPaths ApplicationPaths { get; } private ILogger Log { get; } public static AssemblyManager Instance { get; set; } public AssemblyManager(IApplicationPaths paths, ILogManager logManager) { ApplicationPaths = paths; Log = logManager.GetLogger(Plugin.Instance.Name); Instance = this; } private string GetMaxFactorizationNativeAssemblyName() { if (IsLinux()) return "libMatrixFactorizationNative.so"; if (IsWindows()) return "MatrixFactorizationNative.dll"; if (IsMacOS()) return "libMatrixFactorizationNative.dylib"; throw new Exception("Unable to find MaxFactorization Library."); } //These are the runtimes. The only file we need to copy into Emby root (system folder) //But, it doesn't have to be there when emby loads because we are going to load all the other Link Librarys on command. //It only has to be there when the dynamic librarys are looking for it. Amazing! //The epitome of side loading! private static string MatrixFactorizationNativeEmbeddedResourceAssembly() { var location = string.Empty; var architecture = RuntimeInformation.OSArchitecture; if (IsLinux()) { switch (architecture) { case Architecture.X64 : location += "libMatrixFactorizationNative_Linux64.so"; break; case Architecture.Arm : location += "libMatrixFactorizationNative_LinuxArm.so"; break; case Architecture.Arm64 : location += "libMatrixFactorizationNative_LinuxArm64.so"; break; } } if (IsWindows()) { switch (architecture) { case Architecture.X64 : location += "MatrixFactorizationNative_Win64.dll"; break; case Architecture.X86 : location += "MatrixFactorizationNative_Win86.dll"; break; } } if (IsMacOS()) { switch (architecture) { case Architecture.X64 : location += "libMatrixFactorizationNative_OSX64.dylib"; break; case Architecture.Arm64 : location += "libMatrixFactorizationNative_OSXARM64.dylib"; break; } } return location; } public void Dispose() { } public void Run() { // MaxFactorizationNative is a dependency for the ML.Net library. // It needs to live the Application Root (System folder). // Copy over the appropriate version for the appropriate OS. var maxFactorizationNativeLibraryName = GetMaxFactorizationNativeAssemblyName(); Log.Info($"ML.Net loading dependency {maxFactorizationNativeLibraryName}"); var matrixFactorizationNativeLibraryEmbeddedResourceStream = GetEmbeddedResourceStream(MatrixFactorizationNativeEmbeddedResourceAssembly()); //Copy the resource into the system root. using (var fileStream = new FileStream(Path.Combine(ApplicationPaths.ProgramSystemPath, maxFactorizationNativeLibraryName), FileMode.Create, FileAccess.Write)) { matrixFactorizationNativeLibraryEmbeddedResourceStream?.CopyTo(fileStream); } //This event will take care of loading the rest of the library we we need to run ML.Net at runtime. AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { //Don't try and load items that are not in the Microsoft.ML namespace if (!args.Name.Contains(".ML") && !args.Name.Contains("Newtonsoft")) return null; //Don't load the assembly twice var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); if (assembly != null) return assembly; Log.Info($"ML.Net loading assembly {args.Name} {args.RequestingAssembly}"); var r1 = Assembly.GetExecutingAssembly().GetManifestResourceNames().FirstOrDefault(s => s.Contains(args.Name.Split(',')[0])); if (r1 is null) return null; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(r1)) { byte[] assemblyData = new byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } } public Stream GetEmbeddedResourceStream(string resourceName) { var assembly = Assembly.GetExecutingAssembly(); //might not know the extact path, so as long as it ends with the name. What could go wrong???.... var name = assembly.GetManifestResourceNames().FirstOrDefault(s => s.EndsWith(resourceName)); return GetType().Assembly.GetManifestResourceStream(name); } public async Task SaveEmbeddedResourceToFileAsync(Stream embeddedResourceStream, string output) { using (var fileStream = new FileStream(output, FileMode.Create, FileAccess.Write)) { await embeddedResourceStream.CopyToAsync(fileStream); } } } } Isn't it cool? Edited November 8, 2022 by chef 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now