Should the loading of OnDemand Prism modules work in an OOB scenerio? If so, I cannot seem to make it work. Everything is currently working in browser without any problems. Specifically I:
register my modules in code:
protected override IModuleCatalog GetModuleCatalog() {
var catalog = new ModuleCatalog();
Uri source;
if( Application.Current.IsRunningOutOfBrowser ) {
source = IsolatedStorageSettings.ApplicationSettings[SOURCEURI] as Uri;
}
else {
var src = Application.Current.Host.Source.ToString();
src = src.Substring( 0, src.LastIndexOf( '/' ) + 1 );
source = new Uri( src );
IsolatedStorageSettings.ApplicationSettings[SOURCEURI] = source;
IsolatedStorageSettings.ApplicationSettings.Save();
}
if( source != null ) {
var mod2 = new ModuleInfo { InitializationMode = InitializationMode.OnDemand,
ModuleName = ModuleNames.mod2,
ModuleType = "mod2.Module, mod2.Directory, '1.0.0.0', Culture=neutral, PublicKeyToken=null" ),
Ref = ( new Uri( source, "mod2.xap" )).AbsoluteUri };
catalog.AddModule( mod2 );
}
// per Jeremy Likeness - did not help.
Application.Current.RootVisual = new Grid();
return ( catalog );
}
later request for the module to be loaded is made:
mModuleManager.LoadModule( ModuleNames.mod2 );
and wait for a response to an event published during the initialization of that loaded module.
The module appears to never be loaded, and when the application is running under the debugger there will be a message box that states that the web server returned a 'not found' error. I can take the requesting url for the module and enter it into Firefox and download the module with no problem.
I have not been able to find any reference to this actually being workable, but it seems as though it should. The most I have found on the subject is a blog entry by Jeremy Likeness, which covers loading modules in MEF, but applying his knowledge here did not help.
The server is localhost (I have heard it mentioned that this might cause problems). The server has a clientaccesspolicy.xml file - although I don't expect that is needed.
I am using the client stack and register it during app construction:
WebRequest.RegisterPrefix( Current.Host.Source.GetComponents( UriComponents.SchemeAndServer, UriFormat.UriEscaped ), WebRequestCreator.ClientHttp );
Followup questions:
Can all of the xaps be installed to the client desktop in some manner - or only the main application xap? specify them in appmanifest.xml somehow??
Is it worth it make this work if only the application.xap is installed and the rest of the xaps must be downloaded anyway?
Once I worked on a similar scenario. The trick is having the modules stored in isolated storage and use a module loader that reads from isolated storage when working offline.
This is because otherwise, you can't get download the modules that are in a different .xap file than the Shell.
Thanks,
Damian
It is possible to hook custom module loaders into Prism if you're willing to tweak the Prism source and build it yourself. I was actually able to get this to work pretty easily - in our app, I look on disk first for the module, and if it's not found, I fall back to loading it from the server via a third-party commercial HTTP stack that supports client certificates.
To do this, download the Prism source code, and locate the Microsoft.Practices.Composite.Modularity.XapModuleTypeLoader class. This class uses another Prism class, Microsoft.Practices.Composite.Modularity.FileDownloader, to download the .xap content; but it instantiates it directly, giving you no chance to inject your own or whatever.
So - in XapModuleTypeLoader, I added a static property to set the type of the downloader:
public static Type DownloaderType { get; set; }
Then I modified the CreateDownloader() method to use the type specified above in preference to the default one:
protected virtual IFileDownloader CreateDownloader() {
if (_downloader == null) {
if (DownloaderType == null) {
_downloader = new FileDownloader();
} else {
_downloader = (IFileDownloader)Activator.CreateInstance(DownloaderType);
}
}
return _downloader;
}
When my app starts up, I set the property to my own downloader type:
XapModuleTypeLoader.DownloaderType = typeof(LocalFileDownloader);
Voila - now Prism calls your code to load its modules.
I can send you my LocalFileDownloader class as well as the class it falls back to to load the .xap from the web if you're interested... I suspect though that if you look at Prism's FileDownloader class you'll see that it's simple enough.
With regard to your other questions, the clientaccesspolicy.xml file is probably not needed if the URL the app is installed under is the same one you're talking to, or if you're in elevated trust.
The .xaps can definitely be pre-installed on the client, but it's a bit of work. What we did was write a launcher app that is a standalone .NET 2.0 desktop app. It downloads the main .xap plus certain modules* (checking for updates and downloading only as needed), then uninstalls/reinstalls the app if necessary, then launches the app. The last two are done via sllauncher.exe, which is installed as part of Silverlight. Here's a good intro to that: http://timheuer.com/blog/archive/2010/03/25/using-sllauncher-for-silent-install-silverlight-application.aspx.
Assuming you're running under elevated trust, it should also be possible to pre-fetch the module .xaps from within the SL client, but before they're actually requested due to user action. You'd just need to put them in a folder under My Documents somewhere, and then use the custom module loading approach described above to pull them from there.
*In our case, our main .xap is 2/3 of the application. The rest of our .xaps are small, so we download them on-the-fly, with the exception of some .xaps we created as containers for third-party components. We don't expect to update those very often, so we pre-install them.
Related
I have to develop a webapp for a CEF-Browser environment. There is no HTTP server available, everything will be served over file:// protocol.
When developing a Webapp nowadays one doesn't get round working with a framework like react/vue for frontend. The standard webpack build scripts of those build a bundle which only works served over HTTP.
Is it possible to configure webpacks build bundle to work on file:// or is there another way to use react or vue via file://?
I'm suggest read CEF wiki more carefully. You are especially interested in https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-request-handling
In short:
You can register custom scheme handler to serve resources over http+custom fake domain.
You can pack resources in zip for example if you like, or leave them at file system as is (but in that case you can expect that some funny users can edit your files, and then report back unexisting errors back to you).
Important helpers already done (but you can write own when need.)
You can... many other things.
Main thing that "file" scheme are more restricted, and for example you can't do XHR requests. But for custom handler you can. Even if dynamic loader for some reason use XHR instead DOM-based loading it will work again same as on http without touching network.
cefclient itself also has usage of custom schemes. Check URL of Tests->Other... in menu. :)
PS: Sorry that my answer doesnt have direct answer for your question. But, custom resource handling in CEF is so common, that i'm just should say about.
fddima is right - you don't need to configure your webpack (although it would be theoretically possible). Instead you can use custom scheme handler in CEF. I made it work with angular at work.
I wrote blog post on how to serve web application via 'file' protocol in CEF.
What you want to add is your scheme handler and its factory:
using System;
using System.IO;
using CefSharp;
namespace MyProject.CustomProtocol
{
public class CustomProtocolSchemeHandler : ResourceHandler
{
// Specifies where you bundled app resides.
// Basically path to your index.html
private string frontendFolderPath;
public CustomProtocolSchemeHandler()
{
frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
}
// Process request and craft response.
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
var requestedFilePath = frontendFolderPath + fileName;
if (File.Exists(requestedFilePath))
{
byte[] bytes = File.ReadAllBytes(requestedFilePath);
Stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
MimeType = GetMimeType(fileExtension);
callback.Continue();
return true;
}
callback.Dispose();
return false;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
}
And then register it before calling Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
We have a DNN module that uses Angular as its client side framework.
I'd like to be able to embed all the resources such as html , js ,css ,images and fonts to my module.(actually our module have more than one dll and every one of them has its own resources so that I don't want to copy all of these resource into main module folder every time I want to make a package)
So far I have tried WebResource.axd which was successful to some extent (Here's what I have done)but then I realized that It is somehow impossible to embed html,images and other stuffs rather than js and css (or it isn't?)
Then I decided to try using VirtualPathProvider and I used this open source project that implements an EmbeddedResourcesVirtualProvider.
I have registered this provider using IRouteMapper interface of DNN. Now that I start testing my project I am getting 404 for all of my resources. I tried to debug the project and put some break points over FileExists ,DirectoryExists and GetFile methods of VirtualProvider but the only virtual path that is being asked from VirtaulProvider is "~/Default.aspx" and nothing else
I would like to ask if it is possible to use VirtualParhProvider with DNN ?
We are using DNN 8.
I think you are over complicating things a bit. If you need a virtual provider for your module to work you are doing it wrong (in my opinion).
A module should be a self-contained package that could be deployed on any DNN installation without having to do anything but install the module.
Normally when you buy or download a free module, it comes in a single zip file with all the necessary files contained in that zip. That could be any type of file (.dll, .js, css, .ascx, .aspx etc) is does not matter as long as it's defined in the .dnn installation file.
You can then link to the files in the ascx of your module.
<script type="text/javascript" src="/DesktopModules/YourModulePath/js/file.js"></script>
or
<img src="/DesktopModules/YourModulePath/images/image.jpg">
With WebResource you can embed anything - images, html, fonts etc., so I would suggest continuing with the approach you've already taken.
I downloaded and installed your module in DDN 8 for testing. So the following assumes that setup.
To embed an image you can do this:
In the library MyFramework:
Add a file called image.png to a new folder \content\images\
Set Build Action to Embedded Resource for this image
Add [assembly: System.Web.UI.WebResource("MyFramework.content.images.image.png", "image/png")] to AssemblyInfo.cs
Add protected string myImageUrl { get; private set; } so we can access the URL in the inheriting class
Add myImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(MyModuleBase), "MyFramework.content.images.image.png"); to your OnInit() method
In the consuming project MyModule:
Add <img src="<%=myImageUrl%>"/> to View.ascx
For HTML and similar content type, you can do basically the same as you have already done for the scripts:
In the library MyFramework:
Add a file called myhtml.html to a new folder \content\html\
(in my file I have: <div style="font-weight: bold;font-size: x-large">Some <span style="color: orange">HTML</span></div>)
Set Build Action to Embedded Resource for the html
Add [assembly: System.Web.UI.WebResource("MyFramework.content.html.myhtml.html", "text/html")] to AssemblyInfo.cs
Add protected string MyHtmlUrl { get; private set; } so we can access the HTML in the inheriting class
Add:
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "MyFramework.content.html.myhtml.html";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (StreamReader reader = new StreamReader(stream))
{
MyHtmlUrl = reader.ReadToEnd();
}
}
In the consuming project MyModule:
Add <%=MyHtmlUrl%> to View.ascx
I have a fairly typical Nancy configuration, with views being served from locations on the file system.
Under a specific route I wish to serve views which are not stored on the file system, but will be provided in Markdown format from an external service.
I initially approached the idea by considering creating a custom concrete class for IViewLocationProvider which would deal with locating the views from the external service. By my understanding of Nancy (and I could be wrong here), you can only have one IViewLocationProvider, so adding this custom class would mean that I could not get the rest of the pages (for all my other routes) from the file system.
My approach is now to manually call the Nancy.ViewEngines.Markdown.Render method, passing it my Markdown (encapsulated in a Func<TextReader>) and a fake ViewLocationResult. As so:
public class MyModule
{
public MyModule(ExternalServiceAdapter externalService, MarkDownViewEngine viewEngine, IRenderContext renderContext) : base("/MyPath")
{
Get["/{Name}/{Version}/View"] = parameters => SpecialView(viewEngine, renderContext, externalService, (string)parameters.Name, (string)parameters.Version);
}
protected static Response SpecialView(MarkDownViewEngine viewEngine, IRenderContext renderContext, ExternalServiceAdapter externalService, string name, string version)
{
var contents = externalService.GetData(name, version);
var location = new ViewLocationResult(string.Empty, string.Empty, "md", contents);
return viewEngine.RenderView(location, null, renderContext); //FIXME fails when performing Master page substitution in Super Simple View Engine
}
}
Unfortunately the Markdown includes an SSVE #Master['master'] tag which causes the above to fall down at the indicated line with a null reference exception deep within SSVE. (I have a file on the file system which is named master and is in Nancy's default path convention for views - but I don't think it's the presence of the file which is causing the issue)
Is there a better approach to rendering a Nancy view from markdown which is not located on the file system? (yet maintain a file system provider for other routes)
I'm working on creating a clickonce install for my application.
I have ClickOnce security settings enabled with full trust in the Security tab if the project properties. I publish to a network drive and run the install. The install is successful but when I run the application I get this error:
I have the Pos for .Net code running in a separate AppDomain (due to its issues with .net 4's default security policy). It runs fine on my local system without clickonce. My application uses Prism, so I had to modify the manifest to include the dynamically loaded modules. It's somehow related to my AppDomain I create not having full trust.
This is how I create the AppDomain
AppDomainSetup currentAppDomainSetup = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup newAppDomainSetup = new AppDomainSetup()
{
ApplicationBase = currentAppDomainSetup.ApplicationBase,
LoaderOptimization = currentAppDomainSetup.LoaderOptimization,
ConfigurationFile = currentAppDomainSetup.ConfigurationFile,
PrivateBinPath = #"Modules" // need to set this so that the new AppDomain can see the prism modules
};
newAppDomainSetup.SetCompatibilitySwitches(new[] { "NetFx40_LegacySecurityPolicy" }); // required for POS for .Net to function properly
_posAppDomain = AppDomain.CreateDomain("POS Hardware AppDomain", null, newAppDomainSetup);
// Error happens on the following line. Note that type T is always in same assembly that AppDomain was created in.
T hardware = (T)PosAppDomain.CreateInstanceFromAndUnwrap(Assembly.GetAssembly(typeof(T)).Location, typeof(T).FullName);
Is there a security setting that I'm missing?
I think I'm getting closer. The AppDomain I create runs in full trust when I run without clickonce, but when I run it with clickonce it doesn't run in full trust.... so now I'm trying to figure out how to get it in full trust.
Figured it out
I had to add the Evidence and PermissionSet ...
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(SecurityZone.MyComputer));
PermissionSet ps = SecurityManager.GetStandardSandbox(evidence);
AppDomainSetup currentAppDomainSetup = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup newAppDomainSetup = new AppDomainSetup()
{
ApplicationBase = currentAppDomainSetup.ApplicationBase,
LoaderOptimization = currentAppDomainSetup.LoaderOptimization,
ConfigurationFile = currentAppDomainSetup.ConfigurationFile,
PrivateBinPath = #"Modules" // need to set this so that the new AppDomain can see the prism modules
};
newAppDomainSetup.SetCompatibilitySwitches(new[] { "NetFx40_LegacySecurityPolicy" }); // required for POS for .Net to function properly
Is there an easy way to dynamically discover all the XAMLs files within all the currently loaded modules (specifically of a Silverlight Prism application)? I am sure this is possible, but not sure where to start.
This has to occur on the Silverlight client: We could of course parse the projects on the dev machine, but that would reduce the flexibility and would include unused files in the search.
Basically we want to be able to parse all XAML files in a very large Prism project (independent of loading them) to identify all localisation strings. This will let us build up an initial localisation database that includes all our resource-binding strings and also create a lookup of which XAML files they occur in (to make editing easy for translators).
Why do this?: The worst thing for translators is to change a string in one context only to find it was used elsewhere with slightly different meaning. We are enabling in-context editing of translations from within the application itself.
Update (14 Sep):
The standard method for iterating assemblies is not available to Silverlight due to security restrictions. This means the only improvement to the solution below would be to cooperate with the Prism module management if possible. If anyone wants to provide a code solution for that last part of this problem there are points available to share with you!
Follow-up:
Iterating content of XAP files in a module-base project seems like a really handy thing to be able to do for various reasons, so putting up another 100 rep to get a real answer (preferably working example code). Cheers and good luck!
Partial solution below (working but not optimal):
Below is the code I have come up with, which is a paste together of techniques from this link on Embedded resources (as suggested by Otaku) and my own iterating of the Prism Module Catalogue.
Problem 1 - all the modules are
already loaded so this is basically
having to download them all a second
time as I can't work out how to
iterate all currently loaded Prism modules.
If anyone wants to share the bounty
on this one, you still can help make
this a complete solution!
Problem 2 - There is apparently a bug
in the ResourceManager that requires
you to get the stream of a known
resource before it will let you
iterate all resource items (see note in the code below). This means I have to have a dummy resource file in every module. It would be nice to know why that initial GetStream call is required (or how to avoid it).
private void ParseAllXamlInAllModules()
{
IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>();
foreach (var module in mm.Modules)
{
string xap = module.Ref;
WebClient wc = new WebClient();
wc.OpenReadCompleted += (s, args) =>
{
if (args.Error == null)
{
var resourceInfo = new StreamResourceInfo(args.Result, null);
var file = new Uri("AppManifest.xaml", UriKind.Relative);
var stream = System.Windows.Application.GetResourceStream(resourceInfo, file);
XmlReader reader = XmlReader.Create(stream.Stream);
var parts = new AssemblyPartCollection();
if (reader.Read())
{
reader.ReadStartElement();
if (reader.ReadToNextSibling("Deployment.Parts"))
{
while (reader.ReadToFollowing("AssemblyPart"))
{
parts.Add(new AssemblyPart() { Source = reader.GetAttribute("Source") });
}
}
}
foreach (var part in parts)
{
var info = new StreamResourceInfo(args.Result, null);
Assembly assy = part.Load(System.Windows.Application.GetResourceStream(info, new Uri(part.Source, UriKind.Relative)).Stream);
// Get embedded resource names
string[] resources = assy.GetManifestResourceNames();
foreach (var resource in resources)
{
if (!resource.Contains("DummyResource.xaml"))
{
// to get the actual values - create the table
var table = new Dictionary<string, Stream>();
// All resources have “.resources” in the name – so remove it
var rm = new ResourceManager(resource.Replace(".resources", String.Empty), assy);
// Seems like some issue here, but without getting any real stream next statement doesn't work....
var dummy = rm.GetStream("DummyResource.xaml");
var rs = rm.GetResourceSet(Thread.CurrentThread.CurrentUICulture, false, true);
IDictionaryEnumerator enumerator = rs.GetEnumerator();
while (enumerator.MoveNext())
{
if (enumerator.Key.ToString().EndsWith(".xaml"))
{
table.Add(enumerator.Key.ToString(), enumerator.Value as Stream);
}
}
foreach (var xaml in table)
{
TextReader xamlreader = new StreamReader(xaml.Value);
string content = xamlreader.ReadToEnd();
{
// This is where I do the actual work on the XAML content
}
}
}
}
}
}
};
// Do the actual read to trigger the above callback code
wc.OpenReadAsync(new Uri(xap, UriKind.RelativeOrAbsolute));
}
}
Use GetManifestResourceNames reflection and parse from there to get only those ending with .xaml. Here's an example of using GetManifestResourceNames: Enumerating embedded resources. Although the sample is showing how to do this with a seperate .xap, you can do this with the loaded one.
I've seen people complain about some pretty gross bugs in Prism
Disecting your problems:
Problem 1: I am not familiar with Prism but from an object-oriented perspective your Module Manager class should keep track of whether a Module has been loaded and if not already loaded allow you to recursively load other Modules using a map function on the List<Module> or whatever type Prism uses to represent assemblies abstractly. In short, have your Module Manager implement a hidden state that represents the List of Modules loaded. Your Map function should then take that List of Modules already loaded as a seed value, and give back the List of Modules that haven't been loaded. You can then either internalize the logic for a public LoadAllModules method or allow someone to iterate a public List<UnloadedModule> where UnloadedModule : Module and let them choose what to load. I would not recommend exposing both methods simultaneously due to concurrency concerns when the Module Manager is accessed via multiple threads.
Problem 2: The initial GetStream call is required because ResourceManager lazily evaluates the resources. Intuitively, my guess is the reason for this is that satellite assemblies can contain multiple locale-specific modules, and if all of these modules were loaded into memory at once it could exhaust the heap, and the fact these are unmanaged resources. You can look at the code using RedGate's .NET Reflector to determine the details. There might be a cheaper method you can call than GetStream. You might also be able to trigger it to load the assembly by tricking it by loading a resource that is in every Silverlight assembly. Try ResourceManager.GetObject("TOOLBAR_ICON") or maybe ResourceManager.GetStream("TOOLBAR_ICON") -- Note that I have not tried this and am typing this suggestion as I am about to leave for the day. My rationale for it being consistently faster than your SomeDummy.Xaml
approach is that I believe TOOLBAR_ICON is hardwired to be the zeroth resource in every assembly. Thus it will be read very early in the Stream. Faaaaaast. So it is not just avoiding needing SomeDummy.Xaml in every assembly of your project that I am suggesting; I am also recommending micro-optimizations.
If these tricks work, you should be able to significantly improve performance.
Additional thoughts:
I think you can clean up your code further.
IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>();
foreach (var module in mm.Modules)
{
could be refactored to remove the reference to UnityContainer. In addition, IModuleCatalog would be instantiated via a wrapper around the List<Module> I mentioned in my reply to Problem 1. In other words, the IModuleCatalog would be a dynamic view of all loaded modules. I am assuming there is still more performance that can be pulled out of this design, but at least you are no longer dependent on Unity. That will help you better refactor your code later on for more performance gains.