.NET Forum / Visual Studio.NET / Extensibility / September 2005
Managing referenced assemblies for custom compiler?
|
|
Thread rating:  |
Sporky - 02 Sep 2005 01:20 GMT Hello. I'm trying to integrate my own compiler into VS 2005. Pretty good luck so far, but one thing is driving me nuts. I need have some way of reflecting on assemblies which are referenced by my project type. I got this to work using Assembly.LoadFrom(), but after the first time a build is performed, the referenced DLLs (really the local copies) are locked, presumably because they were loaded in to the appdomain for Visual Studio.
I thought I could get around this by just loading the assemblies into another appdomain, but now I can't figure out how to get a reference to the assembly object to perform reflection. If I use a statement like this:
myAssembly = myAppDomain.Load(...);
the runtime apparently tries to load the assembly into VS's appdomain, which causes an exception because it's not located under the VS application base directory. Note that the above statement without the assignment works fine. Same problem trying to call myAppDomain.GetAssemblies(). I guess this makes sense given the isolation provided by appdomains, but I can't figure out how to work around it.
Any other compiler-writers out there know how to reflect on a project's references assemblies without locking the files, clogging up the VS appdomain, etc?
Thanks.
Dave
Carlos J. Quintero [VB MVP] - 02 Sep 2005 10:05 GMT Hi Dave,
Without another appdomain , I think to recall that someone suggested for this problem to read a stream of bytes from the assembly (via IO) and then load the assembly using the overloaded variation System.Reflection.Assembly.Load(ByVal rawAssembly() As Byte). That should not block the assembly on disk.
 Signature Best regards,
Carlos J. Quintero
MZ-Tools: Productivity add-ins for Visual Studio .NET, VB6, VB5 and VBA You can code, design and document much faster. Free resources for add-in developers: http://www.mztools.com
> Hello. I'm trying to integrate my own compiler into VS 2005. Pretty good > luck so far, but one thing is driving me nuts. I need have some way of [quoted text clipped - 29 lines] > > Dave Sporky - 02 Sep 2005 14:30 GMT Excellent solution, Carlos, thank you. That solves the immediate problem.
I'm still a little bothered about leaving the referenced assemblies loaded into the VS appdomain. Am I being too picky, or are there any potential problems related to this? For example, if a referenced project is rebuilt, a different version of the assembly will be loaded. Maybe it's not a problem, but it would be nice to clean up.
BTW, I tried running the compiler in a different appdomain, but no success. A line such as below:
CodeGenerator generator = (CodeGenerator)(myAppDomain.CreateInstanceFromAndUnwrap( typeof( CodeGenerator ).Assembly.Location, "Provisdom.ISharp.CodeGenerator" ));
throws an InvalidCastException, basically can't cast TransparentProxy to CodeGenerator. This must be something peculiar to how VS loades up my assembly, since I think this ought to work. Maybe the problem is that my assembly is not located under the VS application base directory?
Dave
> Hi Dave, > [quoted text clipped - 3 lines] > System.Reflection.Assembly.Load(ByVal rawAssembly() As Byte). That should > not block the assembly on disk. Carlos J. Quintero [VB MVP] - 02 Sep 2005 15:25 GMT Yes, loading multiples copies can be a problem and it seems that VS.NET also suffers it. See this thread:
http://groups.google.es/group/microsoft.public.dotnet.languages.vb/browse_frm/th read/f2a0b4b3e22a2243/a0b6ab64938d7324?lnk=st&q=Quintero+memory+microsoft.public &rnum=1&hl=es#a0b6ab64938d7324
I am not expert in appdomains, so I can only suggest these links:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/cs harp05162002.asp
http://blogs.msdn.com/shawnfa/
 Signature Best regards,
Carlos J. Quintero
MZ-Tools: Productivity add-ins for Visual Studio .NET, VB6, VB5 and VBA You can code, design and document much faster. Free resources for add-in developers: http://www.mztools.com
> Excellent solution, Carlos, thank you. That solves the immediate problem. > [quoted text clipped - 20 lines] > > Dave Dustin Campbell - 02 Sep 2005 17:41 GMT >BTW, I tried running the compiler in a different appdomain, but no success. >A line such as below: [quoted text clipped - 7 lines] >assembly, since I think this ought to work. Maybe the problem is that my >assembly is not located under the VS application base directory? The first parameter of AppDomain.CreateInstanceAndUnwrap asks for an assembly name and not its location. Try using code like this:
CodeGenerator generator = (CodeGenerator)(myAppDomain.CreateInstanceFromAndUnwrap(typeof(CodeGenerator).Assembly.GetName().Name, "Provisdom.ISharp.CodeGenerator" ));
You'll need to make sure that CodeGenerator descends from MarshalByRefObject and you need to ensure that it is visible to the CLR assembly resolution logic. That means placing it in the GAC (best solution IMO), in the VS app directory or one of the sub directories that are specified in the private bin path (PrivateAssemblies and PublicAssemblies). -------- Best Regards, Dustin Campbell Developer Express, Inc
Sporky - 02 Sep 2005 19:15 GMT I just discovered that this is only a partial solution. Say assembly A contains a type with a method whose signature references a type in assembly B. When the assemblies are loaded indirectly (disk->byte[]->AppDomain), then reflecting on the method in assembly A causes a FileNotFound exception, since it needs to load B to describe the method signature, and B isn't under the app base directory. Using Assembly.LoadFrom() avoids that problem, but then we're right back where we started, because the DLLs get locked.
I feel like I'm missing something fundamental here. Any compiler that integrates with VS must deal with this issue of managing assemblies referenced by the associated project.
Dave Dave
> Hi Dave, > [quoted text clipped - 3 lines] > System.Reflection.Assembly.Load(ByVal rawAssembly() As Byte). That should > not block the assembly on disk. Dustin Campbell - 02 Sep 2005 19:27 GMT It's sounds like you're definitely in need of the unmanaged metadata API that I mentioned that is included in the .NET Framework SDK. This is designed for compilers to read and write metadata *without* using the CLR. -------- Best Regards, Dustin Campbell Developer Express, Inc
Dustin Campbell - 02 Sep 2005 19:39 GMT My apologies, I mentioned the unmanaged metadata APIs in a different thread. Check out the "Assembly References information" thread. -------- Best Regards, Dustin Campbell Developer Express, Inc
Sporky - 03 Sep 2005 01:10 GMT Thanks to everyone for their help and input. I did come up with a solution, via a combination of the info obtained here and some detective work. I managed to avoid (barely) the use of the unmanaged metadata APIs. Let me go over the solution, just in case somebody else runs into this issue in the future.
First thing is to have your build task Execute() method make copies of the non-GAC assemblies referenced by your project instance. These should all be under a common directory. Then create an AppDomain which sets that directory as its application base:
AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = fullPathToDirectoryContainingReferencedDllCopies; AppDomain myAppDomain = AppDomain.CreateDomain("Compiler AppDomain", null, setup);
The compiler itself needs to run in that AppDomain, so that reflection can properly resolve all type references for type resolution (note that the set of referenced DLLs needs to be closed). Thus, when A.dll references a type in B.dll, when you reflect on A.dll the runtime can find B.dll. Running the compiler in the separate AppDomain requires that you use one of the AppDomain.CreateInstance methods to make an instance of the compiler. I used CreateInstanceFromAndUnwrap, providing the full path to the compiler's assembly, because that's the only way I could get it to work. .
Note that in order to be able to reference your remoted (cross AppDomain) compiler instance from inside the VS process, the compiler DLLs and any dependencies need to be somewhere under the VS app base directory. I put mine in <VS Install Dir>\Common7\IDE\PublicAssemblies. This required a post-build step to copy the VSPackage and its dependencies to that directory, as well as redirect the RegPkg step to the appropriate dir.
The final piece of the puzzle was loading the project reference assemblies into the compiler's AppDomain. Recall that my original question had to do with avoiding the lockup of those DLLs after the first build (they presumably got locked because the VS AppDomain held a reference to them). I tried just loading the assemblies via Assembly.Load(), and then unloading the compiler AppDomain when the build was done. No luck - the files were still locked, which seems weird (in fact VS showed the relevant modules as being unloaded after tear-down of the AppDomain, but the files were still locked). I next tried Carlos' idea of first reading the assemblies to a byte array, and then loading into the AppDomain. That almost worked, except that when A.dll referenced B.dll, B.dll get reloaded by the runtime, and then was locked anyway. Bleah.
The solution turned out to be using Assembly.ReflectionLoadOnly(). Apparently this has some memory, so dependencies are also loaded as ReflectionOnly, and the whole locking thing is avoided.
Thanks again to everyone for their help.
Dave
> Hello. I'm trying to integrate my own compiler into VS 2005. Pretty good > luck so far, but one thing is driving me nuts. I need have some way of [quoted text clipped - 23 lines] > > Dave
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|