-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: How do I properly use ResourceDescription to embed resource information? #7791
Comments
RESXs don't actually get embedded in dlls, instead, they get transformed via ResGen.exe into a binary ".resources" file (ResourceManager format) and that gets embedded into the dll. To do this, you can either call ResGen yourself, either directly or via the GenerateResource MSBuild task. Alternatively, but harder, would be to directly use the ResXResourceReader to read from RESX and ResourceWriter to write directly to this binary format. You can see the code that the GenerateResource task does to do this here: https://github.com/Microsoft/msbuild/blob/a4e0dd5f4b31a6c9acb1bab25ac401c438c3dfac/src/XMakeTasks/GenerateResource.cs. |
Thanks, I'll take a look at the links you've provided and try to work on a temporary solution until I can better plan how we'll handle MSBuild tasks in general. One last question, so does the |
One other question: I'm emitting the DLLs to memory, I'm guessing there's no way to co-erce MSBuild ot |
@JoshVarty: nope, unfortunately not. Fundamentally, MSBuild isn't designed to be a "zero-impact" build system. It'd be great if all operations operated on streams and you could build without impacting the disk, but unless you've got a time machine that's not going to happen. |
That's fine, I don't mind doing some extra work here. Just a quick sanity check before I do: If I use |
I'm new to Rolsyn, so I'll have to let @jasonmalinowski answer that. |
I have no idea actually. We should ask @dotnet/roslyn-compiler. |
I just went ahead and implemented it and it seemed to work. |
I think I'll go ahead and close this now as I've gotten the base case working. Once I'm done I'll probably write something up for others who are interested in using this API. Thanks guys! |
@JoshVarty "Once I'm done I'll probably write something up for others who are interested in using this API." Did you ever get around to doing this? Or is the code you wrote that implemented this available in one of your projects? Thanks. |
If you want to support all conceivable resource files (eg. If you want to support var resourceDescription = new Microsoft.CodeAnalysis.ResourceDescription(resourceFullName, () => ProcessFile(resourceFilePath), isPublic: true); Where my implementation of private static MemoryStream ProcessFile(string inFile)
{
var readers = new List<ReaderInfo>();
var resources = readResources(inFile);
using (var outStream = writeResources(resources))
{
//outstream is closed, so we create a new memory stream based on its buffer.
var openStream = new MemoryStream(outStream.GetBuffer());
return openStream;
}
}
private static MemoryStream writeResources(ReaderInfo resources)
{
var memoryStream = new MemoryStream();
using (var resourceWriter = new ResourceWriter(memoryStream))
{
writeResources(resources, resourceWriter);
}
return memoryStream;
}
private static void writeResources(ReaderInfo readerInfo, ResourceWriter resourceWriter)
{
foreach (ResourceEntry entry in readerInfo.resources)
{
string key = entry.Name;
object value = entry.Value;
resourceWriter.AddResource(key, value);
}
}
private static ReaderInfo readResources(string fileName)
{
ReaderInfo readerInfo = new ReaderInfo();
var path = Path.GetDirectoryName(fileName);
var resXReader = new ResXResourceReader(fileName);
resXReader.BasePath = path;
using (resXReader)
{
IDictionaryEnumerator resEnum = resXReader.GetEnumerator();
while (resEnum.MoveNext())
{
string name = (string)resEnum.Key;
object value = resEnum.Value;
addResource(readerInfo, name, value, fileName);
}
}
return readerInfo;
}
private static void addResource(ReaderInfo readerInfo, string name, object value, string fileName)
{
ResourceEntry entry = new ResourceEntry(name, value);
if (readerInfo.resourcesHashTable.ContainsKey(name))
{
//Duplicate resource name. We'll ignore and continue.
return;
}
readerInfo.resources.Add(entry);
readerInfo.resourcesHashTable.Add(name, value);
}
internal sealed class ReaderInfo
{
// We use a list to preserve the resource ordering (primarily for easier testing),
// but also use a hash table to check for duplicate names.
public ArrayList resources { get; }
public Hashtable resourcesHashTable { get; }
public ReaderInfo()
{
resources = new ArrayList();
resourcesHashTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
}
}
/// <summary>
/// Name value resource pair to go in resources list
/// </summary>
private class ResourceEntry
{
public ResourceEntry(string name, object value)
{
this.Name = name;
this.Value = value;
}
public string Name { get; }
public object Value { get; }
} |
@JoshVarty Thank you very much for your reply. After many difficulties I've come up with something that seems to work for me, at least for .resx and for .txt embedded resources.
If you have any suggestions I'd be glad to hear them. Thanks again. |
This is a followup to: #7772 and a recent Stack Overflow question
I should preface this by saying I have only a cursory understanding of how resources are embedded within DLLs, so it's possible I'm misusing or misunderstanding this API.
Scenario: I'm working on a Visual Studio plugin that compiles and runs our customer's code as they write it. We've run into some issues when emitting from the Visual Studio Workspace in which resource files are not automatically embedded in emitted DLLs. @jasonmalinowski explained that the
VisualStudioWorkspace
wasn't built to support emitting directly, so this is understandable behavior.Yesterday I worked on a quick workaround that used
EnvDTE
to discover resource files. My plan was to create any requiredResoureDescription
automatically and pass them toEmit()
.My approach is essentially:
However, when I run this DLL I'm still receiving an error telling me the resources weren't embedded properly:
My next step was to compare the DLL produced when building the same project (
ConsoleApplication1
) in VS with the output DLL I've emitted. I just opened both up in Notepad++ and noticed a few differences:In my output DLL, there is a full plaintext XML ResX schema. The keys and values are present.
In Visual Studio's output DLL, the keys and values are present, but there isn't a plaintext resx schema. It looks like it's been serialized or something. (Once again, I'm a little over my head here)
Am I misusing the API? Could someone help point me in the right direction on this one?
The text was updated successfully, but these errors were encountered: