Castle DynamicProxy: access is denied when saving proxies assembly - castle-dynamicproxy

Am using Castle DynamicProxy to create interceptors and persist the generated runtime proxies into assembly file. The next time I load the proxies assembly into ModuleScope and create few new proxies then try to save them back into the same file, I got the following exception:
System.UnauthorizedAccessException: "Access to the path 'D:\BuildProjects\VSProjects\DynamicProxyTest\DynamicProxyTest\bin\Debug\DynamicProxyTest.Entity.Proxies.dll' is denied."
Assembly proxies = Assembly.LoadFile(Environment.CurrentDirectory + "\\DynamicProxyTest.Entity.Proxies.dll");
var scope = new ModuleScope(
true,
true,
ModuleScope.DEFAULT_ASSEMBLY_NAME,
ModuleScope.DEFAULT_FILE_NAME,
"DynamicProxyTest.Entity.Proxies",
"DynamicProxyTest.Entity.Proxies.dll");
scope.LoadAssemblyIntoCache(proxies);
_generator = new ProxyGenerator(
new DefaultProxyBuilder(scope)
);
var c2 = _generator.CreateClassProxy<Class2>(_logging);
var c3 = _generator.CreateClassProxy<Class3>(_logging);
// System.UnauthorizedAccessException thrown here!
scope.SaveAssembly(false);
I already know that DynamicProxyTest.Entity.Proxies.dll is still referenced by Castle when I first loaded it into cache.
However, how can I implement this correctly by loading proxies assembly into cache, add any new proxies into the scope, if needed, and save/persist the proxies assembly successfully back into desk again?

Related

Issue with CompileAssemblyFromSource when called from a WCF app

I have a WCF service that exposes a method. When a client calls this method, the following occurs:
the method does some processing
it tries to load an assembly if its already there
if the above dll isn't there, it generates C# code, compiles it using CSharpCodeProvider's CompileAssemblyFromSource api
It then loads the just generated assembly
Now the problem. The first time the method it called, it generates the assembly at point 3), and when it tries to return the assembly reference via CompilerResults.CompiledAssembly it throws a file not found exception. However, I can clearly see that the assembly has been generated at the specified location and I can open it with other applications.
If I call the method again via a client, it is able to load the assembly (it was successfully generated as a result of the previous call) and goes ahead to do the remaining set of tasks. Only when the assembly isnt there and it generates it and goes ahead to load it immediately, I get that exception. Any ideas? I have tried playing around with the web.config, changing impersonation to true/false. I have a seperate app pool to run this web application and I tried changing the identity of the app pool from local service to local system, even gave my windows logon credentials which has admin rights but no luck.
Any help would be much appreciated.
Are you sure it's generating the assembly? I have the same problem, except I can't find the generated .dll. I first suspected it was unable to write to the folder, so it now calls CreateDirectory and drops a text file to demonstrate that the folder is writeable.
Anyway, same problem, no success. Is it really the case that nobody else has had this issue??
I'm going to remote debug the server & see if I can step through Microsoft's PDBs...
-- EDIT --
No need to step through Microsoft's code. I had a look at the Errors collection of the CompilerResults and there was 1 item in there: "Metadata file 'c:\Windows\System32\aaclient.dll' could not be opened -- 'An attempt was made to load a program with an incorrect format. '"
When I get Directory.GetCurrentDirectory() to pick up the other DLLs it's usign the Windows System32 directory...
-- EDIT --
Resolved this by adding references from the executing assembly's folder:
CompilerParameters compilerParameters = new CompilerParameters
{
OutputAssembly = Path.Combine(GeneratedAssembliesFolder, string.Format("{0}.Generated.dll", typeName))
};
string executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string[] dllFiles = Directory.GetFiles(executingDirectory, "*.dll");
compilerParameters.ReferencedAssemblies.AddRange(dllFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
IEnumerable<string> exeFiles =Directory.GetFiles(executingDirectory, "*.exe").Where(f => !f.Contains(".vshost."));
compilerParameters.ReferencedAssemblies.AddRange(exeFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
For greater robustness one aught to add checks for the binaries being valid managed code assemblies. This code could also be shortened by using a Linq .Union between the two GetFiles calls.
To find a suitable folder to write to:
private static string generatedAssembliesFolder;
private static string GeneratedAssembliesFolder
{
get
{
if (generatedAssembliesFolder == null)
{
string[] candidateFolders = new[]
{
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine),
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
foreach (string candidateFolder in candidateFolders)
{
try
{
if (!Directory.Exists(candidateFolder)) Directory.CreateDirectory(candidateFolder);
string testFileName = Path.Combine(candidateFolder, Path.GetRandomFileName());
File.WriteAllBytes(testFileName, new byte[0]);
File.Delete(testFileName);
}
catch (Exception ex)
{
continue;
}
generatedAssembliesFolder = candidateFolder;
break;
}
}
return generatedAssembliesFolder;
}
}
Thanks user1796307 for your input. I had resolved this issue, but forgot to update it. Sharing it for everyone's benefit. It was the .NET Fusion at play. It caches assembly load path and won't even try to load an assembly if a previous attempt to load from same location had failed. In other words:
if (Assembly.Load("C:\xyz.dll") == null)
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will still fail
}
The solution is to change it as:
if (!File.Exists("C:\xyz.dll")
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will now work
}

File not found in EAR packaging

Inside my .ear
-META-INF
-lib
-ejb-shared.jar
-ejb.jar
-com/ejb/... (classes)
-fileXml.xml (file I'm trying to access)
-web.war
Some description:
"ejbshared" contains some ejbs and JPA entities
"ejb" contains some ejbs and JPA entities and uses "ejb-shared" project
The problem is that I can't access fileXml.xml. Inside an EJB bean (of ejb.jar) I've done:
File f = new File("fileXml.xml");
System.out.println(f.exists()); // returns false!
I don't know why, but it seems that fileXml.xml is not in the classpath, althougth it's present in the .ear, or maybe I'm doing things in the wrong way!
Using new File("fileXml.xml") will reference a file in the current working directory of the application server JVM, not relative to your specific application. Try using:
URL url = getClass().getResource("/fileXml.xml");
boolean exists = url != null;
System.out.println(exists);
InputStream input = url.openStream();
// ... read and close input stream

How to open a SectionGroup in an ASP.NET web application?

I have a small ASP.NET web application hosted in an integration test (executing within NUnit). My product code can usually find configuration data from the web.config or app.config file, but for some reason when hosting ASP.NET I seem to get an ArgumentException when executing the first of these commands:
var configuration = ConfigurationManager.OpenExeConfiguration(null);
return configuration.GetSectionGroup(SectionGroupName);
exePath must be specified when not running inside a stand alone exe.
I don't know what to put here. There isn't a sensible exePath for my product to ever pass into this method as a parameter as it usually runs within a web server. Also, ordinary Sections (not SectionGroups) can typically be opened using:
ConfigurationManager.GetSection(SectionName)
even within unit tests this works, where an App.config file somehow magically gets read. That's what I'd like when reading SectionGroups.
Any ideas?
Inside the web application, try using WebConfigurationManager. You will need a mechanism to detect if you are in a web context or exe context and use some design pattern to switch between contexts.
A simple way to do this is check if HttpContext.Current is null (not null indicates web context and a null value indicates a exe context).
IMO, something like this should work,
Configuration configuration;
if (HttpContext.Current == null)
configuration = ConfigurationManager.OpenExeConfiguration(null); // whatever you are doing currently
else
configuration = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath); //this should do the trick
configuration.GetSectionGroup(sectionGroupName);
It will be more complex if you don't want the dependency on the System.web dll
I haven't tested it.
ConfigurationManager.GetSection("SectionGroupName/GroupName")
i.e. e.g.
<configSections>
<sectionGroup name="RFERL.Mvc" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<section name="routes" type="RFERL.Mvc.Configuration.RoutesConfigurationSection, RFERL.Mvc"/>
</sectionGroup>
</configSections>
&
var config = ConfigurationManager.GetSection("RFERL.Mvc/routes") as RoutesConfigurationSection;
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
return config.GetSectionGroup(SectionGroupName);
should work in asp.net.
Posting in case this helps anyone. I used this answer to be able to read an Umbraco configuration.
private static string CdnUrl()
{
// Unit test vs web environment
Configuration configuration;
if (HttpContext.Current == null)
{
configuration = ConfigurationManager.OpenExeConfiguration(null);
}
else
{
configuration = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);
}
// Grab Umbraco config
ConfigurationSectionGroup umbracoConfiguration = configuration.GetSectionGroup("umbracoConfiguration");
FileSystemProvidersSection fileSystemProviders = (FileSystemProvidersSection)umbracoConfiguration.Sections.Get("FileSystemProviders");
// Return the information needed
var cdnUrl = fileSystemProviders.Providers["media"].Parameters["rootUrl"].Value;
return cdnUrl;
}

ASMX Service and Lack of Crossdomain.xml file

I have an issue with an asmx service I am trying to access. No crossdomain file. I read there is a way around this using HTTPService instead of a webservice. Still cannot load the wsdl. See code below. Any help would be greatly appreciated:
var dataService:HTTPService = new HTTPService();
dataService.url =
"http://flexmappers.com/proxy.php?url=http://www.server.net/carbon.asmx";
dataService.method = "POST";
dataService.addEventListener("result", onCarbonCalcResult);
dataService.addEventListener("fault", onCarbonCalcFault);
//dataService.resultFormat = "xml"
var params:Object = new Object();
params["call"] = "getCarbon";
params.area = carbonarea;
params.geojson = geojson;
dataService.send(params);
No crossdomain file. I read there is a
way around this using HTTPService
instead of a webservice
It sounds like you were misinformed.
In browser based applications, neither HTTPService, WebService, and RemoteObject tags are not allowed to access content on a remote server unless a crossdomain.xml file exists allowing such access. They can all access content on the same domain as the SWF without a crossdomain.xml file in place.
To get around this, you can use an HTTP Proxy on the same server that serves your SWF. You could also use an AIR app which does not run in a browser, and therefore exists in a different security sandbox.
You can create your own proxy with BlazeDS or Apache HTTP.

ASP.NET: WebResource.axd call 404 error: how to know which assembly/resource is missing or responsible?

I get a 404 HTTP status error (not found) on a specific WebResource.axd call inside an ASP.NET 3.5 (AJAX) web application. I guess the error is thrown because a specific referenced assembly is missing in the bin folder/GAC. But I don't know which, since the page which requests the resource is very complex (I'm using third-party controls and ASP.NET Ajax.)
Is it possible to know from the encrypted "d" querystring parameter of the query, like:
.../WebResource.axd?d=...
which assembly should create the content and is possibly missing?
Note: There are other WebRequest.axd calls which execute with success.
One of the reasons for this issue is that the registered embedded resource path is incorrect or the resource is not there. Make sure the resource file is added as a Embedded Resource.
Asp.net uses the WebResourceAttribute which you must give the path to the resource.
The resource file needs to be added as a Embedded Resource to the project and the path to it would be the full namespace plus the file name.
So you have the following project resource "my.js" in the project "MyAssembly" the resource path would be "MyAssembly.my.js".
To check what file the web resource handler is not finding you can decrypt the hash code provided on the WebResource.axd url. Please see the example below an how to do that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
byte[] encryptedData = HttpServerUtility.UrlTokenDecode("encoded hash value");
Type machineKeySection = typeof(System.Web.Configuration.MachineKeySection);
Type[] paramTypes = new Type[] { typeof(bool), typeof(byte[]), typeof(byte[]), typeof(int), typeof(int) };
MethodInfo encryptOrDecryptData = machineKeySection.GetMethod("EncryptOrDecryptData", BindingFlags.Static | BindingFlags.NonPublic, null, paramTypes, null);
try
{
byte[] decryptedData = (byte[])encryptOrDecryptData.Invoke(null, new object[] { false, encryptedData, null, 0, encryptedData.Length });
string decrypted = System.Text.Encoding.UTF8.GetString(decryptedData);
decryptedLabel.Text = decrypted;
}
catch (TargetInvocationException)
{
decryptedLabel.Text = "Error decrypting data. Are you running your page on the same server and inside the same application as the web resource URL that was generated?";
}
}
}
}
Original code example by Telerik UI for ASP.NET AJAX Team Link: http://blogs.telerik.com/aspnet-ajax/posts/07-03-27/debugging-asp-net-2-0-web-resources-decrypting-the-url-and-getting-the-resource-name.aspx
This should return the URL path that aspt.net believes the embedded resource is at.
I just spent hours on a similar issue. Due to the great article pointed out by Diadistis I was able to decrypt the WebResource url and find out that my WebResource was translated into a wrong assembly pointer, recognizable by the garbage in front of your resource name. After many struggles I found out that this was because I was using the Page.ClientScript.GetWebResourceUrl in a class deriving from another class which resided outside of the assembly my resource was in. Confusing thing was that my class WAS in the same assembly, though the class deriving from was NOT. The this.GetType() parameter many articles state is a must, turned out not to be so much of a must at all in my situation. Actually, it needed to be replaced with a typeof() and it worked! Hope this may prevent others from getting the same headache as I got from this bugger.
In my case, the source of the 404 error was that the date and time of the machine running IIS were wrong (from the past).
Is your project missing any references?
Are there any references set to CopyLocal=False (common with Infragistics or GAC'ed refs) that dont make it to the destination?
A utility like reflector or dependency walker will tell you if your main assembly is missing any dependencies that are not immediately obvious.
Does the Application_Error handler in global.asax have a catch that is producing any error info (FileNotFoundExceptions)?
Did you set custom errors to 'remote only' and browse the site from the local machine?
This same issue occurs if there is a request filtering rule that detects the specified string in the query string. In my case, the query string for the AXD file was generated with a double dash that the rule detected and caused a 404 not found error for the AXD file request.

Resources