When loading Resources through reflection, how can I be sure I am using the correct Resource Name when I call DLLAssembly.GetManifestResourceNames()
The result of that call is a String[] containing 2 elements.
SDKSetupTool.MainForm.resources
SDKSetupTool.Properties.Resources.resources
If I make the same call to a VB Assembly, it returns 2 elements as well.
SDKSetupTool.MainForm.resources
SDKSetupTool.Resources.resources
The Last element has always been the correct Location containing the Resources from my tests. However, why must I have to parse out ".resources" in order for the ResourceManager to be able to load the Resources
//Load the DLL Assembly from the path that was passed in
var DLLAssembly = Assembly.LoadFrom(SelectedToolPath);
//Load the resource String so that we can bootstrap our Plugin
String RawResourceLocation = DLLAssembly.GetManifestResourceNames()[1];
String ResourceLocation = RawResourceLocation.Substring(0, RawResourceLocation.LastIndexOf('.'));
//Load the resources into a Resource Manager
System.Resources.ResourceManager rman = new System.Resources.ResourceManager(ResourceLocation, DLLAssembly);
//Plugin Name
String PluginName = rman.GetString("PluginName");
//Plugin Description
String PluginDesc = rman.GetString("PluginDescription");
//Plugin Language (Currently only C# and VB.NET Supported)
tring PluginLang = rman.GetString("PluginLanguage");
//Plugin's Entry Point (Because it is a DLL there is no Predefined Entry point. This is an alternative to that)
String PluginEntryPoint = rman.GetString("PluginEntryPoint");
//Discover our entry-point
var EntryPoint = DLLExtension.GetType(PluginEntryPoint);
//Grab the main method of the application
var MainMethod = EntryPoint.GetMethod("Main", new Type[] { });
MainMethod.Invoke(null, null);
If I can consistently be able to load the Resources of an Assembly I will be able to load the plugin based on the String contained in the resources.
Related
I have a use case in an application I'm writing where I have logic in an external DLL that is loaded dynamically. Now I need to add the ability to display shared views inside the ASP.NET MVC view that resides in the external DLL.
What I've done so far is to add the following in my ConfigureServices method:
UriBuilder uri = new UriBuilder(Assembly.GetEntryAssembly().CodeBase);
string fullPath = Uri.UnescapeDataString(uri.Path);
var mainDirectory = Path.GetDirectoryName(fullPath);
var assemblyFilePath = Path.Combine(mainDirectory, "MyLogic.dll");
var asmStream = File.OpenRead(assemblyFilePath);
var assembly = AssemblyLoadContext.Default.LoadFromStream(asmStream);
var part = new AssemblyPart(assembly);
services.AddControllersWithViews().ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part));
This works fine as long as the DLL is added as a reference to the project. If I remove the reference then I'm getting an error in my application when I try to load the partial view:
InvalidOperationException: The partial view 'MyView' was not found. The following locations were searched: /Views/Consent/MyView.cshtml /Views/Shared/MyView.cshtml
What I tried doing is to list all known view of the application using the following code:
var feature = new ViewsFeature();
applicationPartManager.PopulateFeature(feature);
var views = feature.ViewDescriptors.Select(x => x.RelativePath).ToList();
What I see is that when I add the DLL as a reference in the project I see MyView.cshtml in the list, and if not then I don't see it - and the above error makes sense.
But my use case dictates that the loaded DLL is not referenced. Is there a way to add the views from it when it's not a reference?
Mystery solved... instead of ConfigureApplicationPartManager need to use AddApplicationPart like this:
UriBuilder uri = new UriBuilder(Assembly.GetEntryAssembly().CodeBase);
string fullPath = Uri.UnescapeDataString(uri.Path);
var mainDirectory = Path.GetDirectoryName(fullPath);
var assemblyFilePath = Path.Combine(mainDirectory, "Risco.Auth.Demo.Bridge.dll");
var asmStream = File.OpenRead(assemblyFilePath);
var assembly = AssemblyLoadContext.Default.LoadFromStream(asmStream);
services.AddControllersWithViews().AddApplicationPart(assembly);
I'm trying to find out how to read/write to the extended file properties in C#
e.g. Comment, Bit Rate, Date Accessed, Category etc that you can see in Windows explorer.
Any ideas how to do this?
EDIT: I'll mainly be reading/writing to video files (AVI/DIVX/...)
For those of not crazy about VB, here it is in c#:
Note, you have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
public static void Main(string[] args)
{
List<string> arrHeaders = new List<string>();
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder;
objFolder = shell.NameSpace(#"C:\temp\testprop");
for( int i = 0; i < short.MaxValue; i++ )
{
string header = objFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
arrHeaders.Add(header);
}
foreach(Shell32.FolderItem2 item in objFolder.Items())
{
for (int i = 0; i < arrHeaders.Count; i++)
{
Console.WriteLine(
$"{i}\t{arrHeaders[i]}: {objFolder.GetDetailsOf(item, i)}");
}
}
}
Solution 2016
Add following NuGet packages to your project:
Microsoft.WindowsAPICodePack-Shell by Microsoft
Microsoft.WindowsAPICodePack-Core by Microsoft
Read and Write Properties
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
string filePath = #"C:\temp\example.docx";
var file = ShellFile.FromFilePath(filePath);
// Read and Write:
string[] oldAuthors = file.Properties.System.Author.Value;
string oldTitle = file.Properties.System.Title.Value;
file.Properties.System.Author.Value = new string[] { "Author #1", "Author #2" };
file.Properties.System.Title.Value = "Example Title";
// Alternate way to Write:
ShellPropertyWriter propertyWriter = file.Properties.GetPropertyWriter();
propertyWriter.WriteProperty(SystemProperties.System.Author, new string[] { "Author" });
propertyWriter.Close();
Important:
The file must be a valid one, created by the specific assigned software. Every file type has specific extended file properties and not all of them are writable.
If you right-click a file on desktop and cannot edit a property, you wont be able to edit it in code too.
Example:
Create txt file on desktop, rename its extension to docx. You can't
edit its Author or Title property.
Open it with Word, edit and save
it. Now you can.
So just make sure to use some try catch
Further Topic:
Microsoft Docs: Implementing Property Handlers
There's a CodeProject article for an ID3 reader. And a thread at kixtart.org that has more information for other properties. Basically, you need to call the GetDetailsOf() method on the folder shell object for shell32.dll.
This sample in VB.NET reads all extended properties:
Sub Main()
Dim arrHeaders(35)
Dim shell As New Shell32.Shell
Dim objFolder As Shell32.Folder
objFolder = shell.NameSpace("C:\tmp")
For i = 0 To 34
arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
Next
For Each strFileName In objfolder.Items
For i = 0 To 34
Console.WriteLine(i & vbTab & arrHeaders(i) & ": " & objfolder.GetDetailsOf(strFileName, i))
Next
Next
End Sub
You have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
Thank you guys for this thread! It helped me when I wanted to figure out an exe's file version. However, I needed to figure out the last bit myself of what is called Extended Properties.
If you open properties of an exe (or dll) file in Windows Explorer, you get a Version tab, and a view of Extended Properties of that file. I wanted to access one of those values.
The solution to this is the property indexer FolderItem.ExtendedProperty and if you drop all spaces in the property's name, you'll get the value. E.g. File Version goes FileVersion, and there you have it.
Hope this helps anyone else, just thought I'd add this info to this thread. Cheers!
GetDetailsOf() Method - Retrieves details about an item in a folder. For example, its size, type, or the time of its last modification. File Properties may vary based on the Windows-OS version.
List<string> arrHeaders = new List<string>();
Shell shell = new ShellClass();
Folder rFolder = shell.NameSpace(_rootPath);
FolderItem rFiles = rFolder.ParseName(filename);
for (int i = 0; i < short.MaxValue; i++)
{
string value = rFolder.GetDetailsOf(rFiles, i).Trim();
arrHeaders.Add(value);
}
Jerker's answer is little simpler. Here's sample code which works from MS:
var folder = new Shell().NameSpace(folderPath);
foreach (FolderItem2 item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
For those who can't reference shell32 statically, you can invoke it dynamically like this:
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
dynamic shellApp = Activator.CreateInstance(shellAppType);
var folder = shellApp.NameSpace(folderPath);
foreach (var item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
After looking at a number of solutions on this thread and elsewhere
the following code was put together. This is only to read a property.
I could not get the
Shell32.FolderItem2.ExtendedProperty function to work, it is supposed
to take a string value and return the correct value and type for that
property... this was always null for me and developer reference resources were very thin.
The WindowsApiCodePack seems
to have been abandoned by Microsoft which brings us the code below.
Use:
string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
Will return you the value of the extended property you want as a
string for the given file and property name.
Only loops until it found the specified property - not until
all properties are discovered like some sample code
Will work on Windows versions like Windows server 2008 where you will get the error "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'" if just trying to create the Shell32 Object normally.
public static string GetExtendedFileProperty(string filePath, string propertyName)
{
string value = string.Empty;
string baseFolder = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
//Method to load and execute the Shell object for Windows server 8 environment otherwise you get "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'"
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellAppType);
Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
//Parsename will find the specific file I'm looking for in the Shell32.Folder object
Shell32.FolderItem folderitem = shellFolder.ParseName(fileName);
if (folderitem != null)
{
for (int i = 0; i < short.MaxValue; i++)
{
//Get the property name for property index i
string property = shellFolder.GetDetailsOf(null, i);
//Will be empty when all possible properties has been looped through, break out of loop
if (String.IsNullOrEmpty(property)) break;
//Skip to next property if this is not the specified property
if (property != propertyName) continue;
//Read value of property
value = shellFolder.GetDetailsOf(folderitem, i);
}
}
//returns string.Empty if no value was found for the specified property
return value;
}
Here is a solution for reading - not writing - the extended properties based on what I found on this page and at help with shell32 objects.
To be clear this is a hack. It looks like this code will still run on Windows 10 but will hit on some empty properties. Previous version of Windows should use:
var i = 0;
while (true)
{
...
if (String.IsNullOrEmpty(header)) break;
...
i++;
On Windows 10 we assume that there are about 320 properties to read and simply skip the empty entries:
private Dictionary<string, string> GetExtendedProperties(string filePath)
{
var directory = Path.GetDirectoryName(filePath);
var shell = new Shell32.Shell();
var shellFolder = shell.NameSpace(directory);
var fileName = Path.GetFileName(filePath);
var folderitem = shellFolder.ParseName(fileName);
var dictionary = new Dictionary<string, string>();
var i = -1;
while (++i < 320)
{
var header = shellFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header)) continue;
var value = shellFolder.GetDetailsOf(folderitem, i);
if (!dictionary.ContainsKey(header)) dictionary.Add(header, value);
Console.WriteLine(header +": " + value);
}
Marshal.ReleaseComObject(shell);
Marshal.ReleaseComObject(shellFolder);
return dictionary;
}
As mentioned you need to reference the Com assembly Interop.Shell32.
If you get an STA related exception, you will find the solution here:
Exception when using Shell32 to get File extended properties
I have no idea what those properties names would be like on a foreign system and couldn't find information about which localizable constants to use in order to access the dictionary. I also found that not all the properties from the Properties dialog were present in the dictionary returned.
BTW this is terribly slow and - at least on Windows 10 - parsing dates in the string retrieved would be a challenge so using this seems to be a bad idea to start with.
On Windows 10 you should definitely use the Windows.Storage library which contains the SystemPhotoProperties, SystemMusicProperties etc.
https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties
And finally, I posted a much better solution that uses WindowsAPICodePack there
I'm not sure what types of files you are trying to write the properties for but taglib-sharp is an excellent open source tagging library that wraps up all this functionality nicely. It has a lot of built in support for most of the popular media file types but also allows you to do more advanced tagging with pretty much any file.
EDIT: I've updated the link to taglib sharp. The old link no longer worked.
EDIT: Updated the link once again per kzu's comment.
I need to do localization in WinRT. I have a resource file named resource.resw and I have given name, value and comment. I tried using the resource loader to call the localization, but it didn't work. What is the proper syntax?
var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var str = loader.GetString("farewell");
Looks like you are missing GetForCurrentView() on your first line.
var loader = new Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
var text = loader.GetString("Farewell");
Short version:
How do I load a WF4 workflow from XAML?
Important detail: The code that loads the workflow shouldn't need to know beforehand which types are used in the workflow.
Long version:
I am having a very hard time loading a WF4 workflow from the XAML file create by Visual Studio.
My scenario is that I want to put this file into the database to be able to modify it centrally without recompiling the Workflow invoker.
I am currently using this code:
var xamlSchemaContext = new XamlSchemaContext(GetWFAssemblies());
var xmlReaderSettings = new XamlXmlReaderSettings();
xmlReaderSettings.LocalAssembly = typeof(WaitForAnySoundActivity).Assembly;
var xamlReader = ActivityXamlServices.CreateBuilderReader(
new XamlXmlReader(stream, xmlReaderSettings),
xamlSchemaContext);
var activityBuilder = (ActivityBuilder)XamlServices.Load(xamlReader);
var activity = activityBuilder.Implementation;
var validationResult = ActivityValidationServices.Validate(activity);
This gives me a whole lot of errors, which fall into two categories:
Category 1:
Types from my assemblies are not known, although I provided the correct assemblies to the constructor of XamlSchemaContext.
ValidationError { Message = Compiler error(s) encountered processing expression "GreetingActivationResult.WrongPin".
'GreetingActivationResult' is not declared. It may be inaccessible due to its protection level.
, Source = 10: VisualBasicValue, PropertyName = , IsWarning = False }
This can be solved by using the technique described here, which basically adds the assemblies and namespaces of all used types to some VisualBasicSettings instance:
var settings = new VisualBasicSettings();
settings.ImportReferences.Add(new VisualBasicImportReference
{
Assembly = typeof(GreetingActivationResult).Assembly.GetName().Name,
Import = typeof(GreetingActivationResult).Namespace
});
// ...
VisualBasic.SetSettings(activity, settings);
// ... Validate here
This works but makes the whole "dynamic loading" part of the Workflow a joke, as the code still needs to know all used namespaces.
Question 1: Is there another way to get rid of these validation errors without the need to know beforehand which namespaces and assemblies are used?
Category 2:
All my input arguments are unknown. I can see them just fine in activityBuilder.Properties but I still get validation errors saying they are unknown:
ValidationError { Message = Compiler error(s) encountered processing expression
"Pin".
'Pin' is not declared. It may be inaccessible due to its protection level.
, Source = 61: VisualBasicValue, PropertyName = , IsWarning = False }
No solution so far.
Question 2: How to tell WF4 to use the arguments defined in the XAML file?
Question 2:
You can´t execute an ActivityBuilder, it´s just for design. You have to load a DynamicActivity (only through ActivityXamlServices). It should work that way (without using a special XamlSchemaContext), but you must have loaded all used assemblies in advance (placing them in the bin directory should also work, so far about Question 1, DynamicActivity might make things a little bit easier):
var dynamicActivity = ActivityXamlServices.Load(stream) as DynamicActivity;
WorkflowInvoker.Invoke(dynamicActivity);
In general, I got the impression that you´re trying to implement your own "ActivityDesigner" (like VS). I tried this myself, and it was quite hard to deal with DynamicActivity and ActivityBuilder (as DynamicActivity is not serializable but ActivityBuilder cannot be executed), so I ended up with an own activity type that internally converts one type into the other. If you want to have a look at my results, read the last sections of this article.
I have a project that does this - the assemblies are also stored in a database.
When it is time to instantiate a workflow instance I do the following:
Download the assemblies from the database to a cache location
Create a new AppDomain passing the assembly paths into it.
From the new AppDomain load each assembly - you may also need to load assemblies required by your hosting environment too.
I didn't need to mess around with VisualBasic settings - at least as far as I can see having taken a quick look in my code but I'm sure I've seen it somewhere...
In my case while I don't know the input names or types, the caller is expected to have built a request that contains the input names and values (as strings) which are then converted into the correct types via a reflection helper class.
At this point I can instantiate the workflow.
My AppDomain initialisation code looks like this:
/// <summary>
/// Initializes a new instance of the <see cref="OperationWorkflowManagerDomain"/> class.
/// </summary>
/// <param name="requestHandlerId">The request handler id.</param>
public OperationWorkflowManagerDomain(Guid requestHandlerId)
{
// Cache the id and download dependent assemblies
RequestHandlerId = requestHandlerId;
DownloadAssemblies();
if (!IsIsolated)
{
Domain = AppDomain.CurrentDomain;
_manager = new OperationWorkflowManager(requestHandlerId);
}
else
{
// Build list of assemblies that must be loaded into the appdomain
List<string> assembliesToLoad = new List<string>(ReferenceAssemblyPaths);
assembliesToLoad.Add(Assembly.GetExecutingAssembly().Location);
// Create new application domain
// NOTE: We do not extend the configuration system
// each app-domain reuses the app.config for the service
// instance - for now...
string appDomainName = string.Format(
"Aero Operations Workflow Handler {0} AppDomain",
requestHandlerId);
AppDomainSetup ads =
new AppDomainSetup
{
AppDomainInitializer = new AppDomainInitializer(DomainInit),
AppDomainInitializerArguments = assembliesToLoad.ToArray(),
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
PrivateBinPathProbe = null,
PrivateBinPath = PrivateBinPath,
ApplicationName = "Aero Operations Engine",
ConfigurationFile = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "ZenAeroOps.exe.config")
};
// TODO: Setup evidence correctly...
Evidence evidence = AppDomain.CurrentDomain.Evidence;
Domain = AppDomain.CreateDomain(appDomainName, evidence, ads);
// Create app-domain variant of operation workflow manager
// TODO: Handle lifetime leasing correctly
_managerProxy = (OperationWorkflowManagerProxy)Domain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().GetName().Name,
typeof(OperationWorkflowManagerProxy).FullName);
_proxyLease = (ILease)_managerProxy.GetLifetimeService();
if (_proxyLease != null)
{
//_proxyLease.Register(this);
}
}
}
The download assemblies code is easy enough:
private void DownloadAssemblies()
{
List<string> refAssemblyPathList = new List<string>();
using (ZenAeroOpsEntities context = new ZenAeroOpsEntities())
{
DbRequestHandler dbHandler = context
.DbRequestHandlers
.Include("ReferenceAssemblies")
.FirstOrDefault((item) => item.RequestHandlerId == RequestHandlerId);
if (dbHandler == null)
{
throw new ArgumentException(string.Format(
"Request handler {0} not found.", RequestHandlerId), "requestWorkflowId");
}
// If there are no referenced assemblies then we can host
// in the main app-domain
if (dbHandler.ReferenceAssemblies.Count == 0)
{
IsIsolated = false;
ReferenceAssemblyPaths = new string[0];
return;
}
// Create folder
if (!Directory.Exists(PrivateBinPath))
{
Directory.CreateDirectory(PrivateBinPath);
}
// Download assemblies as required
foreach (DbRequestHandlerReferenceAssembly dbAssembly in dbHandler.ReferenceAssemblies)
{
AssemblyName an = new AssemblyName(dbAssembly.AssemblyName);
// Determine the local assembly path
string assemblyPathName = Path.Combine(
PrivateBinPath,
string.Format("{0}.dll", an.Name));
// TODO: If the file exists then check it's SHA1 hash
if (!File.Exists(assemblyPathName))
{
// TODO: Setup security descriptor
using (FileStream stream = new FileStream(
assemblyPathName, FileMode.Create, FileAccess.Write))
{
stream.Write(dbAssembly.AssemblyPayload, 0, dbAssembly.AssemblyPayload.Length);
}
}
refAssemblyPathList.Add(assemblyPathName);
}
}
ReferenceAssemblyPaths = refAssemblyPathList.ToArray();
IsIsolated = true;
}
And finally the AppDomain initialisation code:
private static void DomainInit(string[] args)
{
foreach (string arg in args)
{
// Treat each string as an assembly to load
AssemblyName an = AssemblyName.GetAssemblyName(arg);
AppDomain.CurrentDomain.Load(an);
}
}
Your proxy class needs to implement MarshalByRefObject and serves as your communication link between your app and the new appdomain.
I find that I am able to load workflows and get the root activity instance without any problem.
EDIT 29/07/12 **
Even if you only store the XAML in the database you will need to track the referenced assemblies. Either your list of referenced assemblies will tracked in an additional table by name or you will have to upload (and obviously support download) the assemblies referenced by the workflow.
Then you may simply enumerate all the reference assemblies and add ALL namespaces from ALL public types to the VisualBasicSettings object - like this...
VisualBasicSettings vbs =
VisualBasic.GetSettings(root) ?? new VisualBasicSettings();
var namespaces = (from type in assembly.GetTypes()
select type.Namespace).Distinct();
var fullName = assembly.FullName;
foreach (var name in namespaces)
{
var import = new VisualBasicImportReference()
{
Assembly = fullName,
Import = name
};
vbs.ImportReferences.Add(import);
}
VisualBasic.SetSettings(root, vbs);
Finally don't forget to add namespaces from the environment assemblies - I add namespaces from the following assemblies:
mscorlib
System
System.Activities
System.Core
System.Xml
So in summary:
1. Track the assembly referenced by the user's workflow (since you will be rehosting the workflow designer this will be trivial)
2. Build a list of assemblies from which namespaces will be imported - this will be a union of the default environment assemblies and the user referenced assemblies.
3. Update the VisualBasicSettings with the namespaces and reapply to the root activity.
You will need to do this in the project that executes workflow instances and in the project that rehosts the workflow designer.
One system that I know which does the same job that you are trying to do is the Team Foundation 2010's build system. When you execute a custom build workflow on a controller, you need to point the build controller to a path in TFS where you keep your custom assemblies. The controller then recursively loads up all the assemblies from that location as it starts processing the workflow.
You mentioned that you need to keep the file in a database. Can you not also store the location or meta data information about the required assemblies in the same database and use Reflection to load them recursively before you invoke your workflow?
You can then selectively add/remove assemblies from this path without having to alter the code that dynamically load assemblies using the
var settings = new VisualBasicSettings();
settings.ImportReferences.Add(new VisualBasicImportReference
{
Assembly = typeof(GreetingActivationResult).Assembly.GetName().Name,
Import = typeof(GreetingActivationResult).Namespace
});
// ...
VisualBasic.SetSettings(activity, settings);
// ... Validate here
approach.
This is the way how I load xaml embeded resource (default workflow) to a Workflow Designer:
//UCM.WFDesigner is my assembly name,
//Resources.Flows is the folder name,
//and DefaultFlow.xaml is the xaml name.
private const string ConstDefaultFlowFullName = #"UCM.WFDesigner.Resources.Flows.DefaultFlow.xaml";
private void CreateNewWorkflow(object param)
{
//loading default activity embeded resource
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ConstDefaultFlowFullName))
{
StreamReader sReader = new StreamReader(stream);
string content = sReader.ReadToEnd();
//createion ActivityBuilder from string
ActivityBuilder activityBuilder = XamlServices.Load( ActivityXamlServices
.CreateBuilderReader(new XamlXmlReader(new StringReader(content)))) as ActivityBuilder;
//loading new ActivityBuilder to Workflow Designer
_workflowDesigner.Load(activityBuilder);
OnPropertyChanged("View");
}
}
I already figured out how to load another assembly from my C# application, and extract the resources embedded to that assembly. My problem is that I'd like to filter the resources by type, i.e. I want to get only text resources, but not icons and other stuff.
The code I use at the moment looks like this:
string[] list = target.GetManifestResourceNames();
foreach (var listentry in list)
{
Stream resourceStream = target.GetManifestResourceStream(listentry);
var rr = new ResourceReader(resourceStream);
IDictionaryEnumerator dict = rr.GetEnumerator();
int ctr = 0;
while (dict.MoveNext())
{
ctr++;
string entry = dict.Value; //I'd like to know what kind of resource this is, how can I do that?
}
rr.Close();
}
How can I determine which kind of resource entry I currently get, i.e. if it's an icon, a text resource, or something else?
Thanks a lot.