1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.DynamicProxy
18 using System
.Collections
;
20 using System
.Reflection
;
21 using System
.Reflection
.Emit
;
22 using System
.Resources
;
23 using System
.Threading
;
24 using Castle
.DynamicProxy
.Generators
;
27 /// Summary description for ModuleScope.
29 public class ModuleScope
32 /// The default file name used when the assembly is saved using <see cref="DEFAULT_FILE_NAME"/>.
34 public static readonly String DEFAULT_FILE_NAME
= "CastleDynProxy2.dll";
37 /// The default assembly (simple) name used for the assemblies generated by a <see cref="ModuleScope"/> instance.
39 public static readonly String DEFAULT_ASSEMBLY_NAME
= "DynamicProxyGenAssembly2";
41 // Avoid leaks caused by non disposal of generated types.
42 private ModuleBuilder moduleBuilderWithStrongName
= null;
43 private ModuleBuilder moduleBuilder
= null;
45 // The names to use for the generated assemblies and the paths (including the names) of their manifest modules
46 private string strongAssemblyName
;
47 private string weakAssemblyName
;
48 private string strongModulePath
;
49 private string weakModulePath
;
51 // Keeps track of generated types
52 private Hashtable typeCache
= Hashtable
.Synchronized(new Hashtable());
54 // Users of ModuleScope should use this lock when accessing the cache
55 private ReaderWriterLock readerWriterLock
= new ReaderWriterLock ();
57 // Used to lock the module builder creation
58 private object _lockobj
= new object();
60 // Specified whether the generated assemblies are intended to be saved
61 private bool savePhysicalAssembly
;
64 /// Initializes a new instance of the <see cref="ModuleScope"/> class; assemblies created by this instance will not be saved.
66 public ModuleScope() : this (false)
71 /// Initializes a new instance of the <see cref="ModuleScope"/> class, allowing to specify whether the assemblies generated by this instance
74 /// <param name="savePhysicalAssembly">If set to <c>true</c> saves the generated module.</param>
75 public ModuleScope (bool savePhysicalAssembly
)
76 : this (savePhysicalAssembly
, DEFAULT_ASSEMBLY_NAME
, DEFAULT_FILE_NAME
, DEFAULT_ASSEMBLY_NAME
, DEFAULT_FILE_NAME
)
81 /// Initializes a new instance of the <see cref="ModuleScope"/> class, allowing to specify whether the assemblies generated by this instance
82 /// should be saved and what simple names are to be assigned to them.
84 /// <param name="savePhysicalAssembly">If set to <c>true</c> saves the generated module.</param>
85 /// <param name="strongAssemblyName">The simple name of the strong-named assembly generated by this <see cref="ModuleScope"/>.</param>
86 /// <param name="strongModulePath">The path and file name of the manifest module of the strong-named assembly generated by this <see cref="ModuleScope"/>.</param>
87 /// <param name="weakAssemblyName">The simple name of the weak-named assembly generated by this <see cref="ModuleScope"/>.</param>
88 /// <param name="weakModulePath">The path and file name of the manifest module of the weak-named assembly generated by this <see cref="ModuleScope"/>.</param>
89 public ModuleScope (bool savePhysicalAssembly
, string strongAssemblyName
, string strongModulePath
, string weakAssemblyName
, string weakModulePath
)
91 this.savePhysicalAssembly
= savePhysicalAssembly
;
92 this.strongAssemblyName
= strongAssemblyName
;
93 this.strongModulePath
= strongModulePath
;
94 this.weakAssemblyName
= weakAssemblyName
;
95 this.weakModulePath
= weakModulePath
;
99 /// Users of this <see cref="ModuleScope"/> should use this lock when accessing the cache.
101 public ReaderWriterLock RWLock
103 get { return readerWriterLock; }
107 /// Returns a type from this scope's type cache, or null if the key cannot be found.
109 /// <param name="key">The key to be looked up in the cache.</param>
110 /// <returns>The type from this scope's type cache matching the key, or null if the key cannot be found</returns>
111 public Type
GetFromCache (CacheKey key
)
113 // no lock needed, typeCache is synchronized
114 return (Type
) typeCache
[key
];
118 /// Registers a type in this scope's type cache.
120 /// <param name="key">The key to be associated with the type.</param>
121 /// <param name="type">The type to be stored in the cache.</param>
122 public void RegisterInCache (CacheKey key
, Type type
)
124 // no lock needed, typeCache is synchronized
125 typeCache
[key
] = type
;
129 /// Gets the key pair used to sign the strong-named assembly generated by this <see cref="ModuleScope"/>.
131 /// <returns></returns>
132 public static byte[] GetKeyPair ()
136 using (Stream stream
= Assembly
.GetExecutingAssembly ().GetManifestResourceStream ("Castle.DynamicProxy.DynProxy.snk"))
139 throw new MissingManifestResourceException (
140 "Should have a Castle.DynamicProxy.DynProxy.snk as an embedded resource, so Dynamic Proxy could sign generated assembly");
142 int length
= (int) stream
.Length
;
143 keyPair
= new byte[length
];
144 stream
.Read (keyPair
, 0, length
);
151 /// Gets the strong-named module generated by this scope, or <see langword="null"/> if none has yet been generated.
153 /// <value>The strong-named module generated by this scope, or <see langword="null"/> if none has yet been generated.</value>
154 public ModuleBuilder StrongNamedModule
160 return moduleBuilderWithStrongName
;
166 /// Gets the file name of the strongly named module generated by this scope.
168 /// <value>The file name of the strongly named module generated by this scope.</value>
169 public string StrongNamedModuleName
173 return Path
.GetFileName (strongModulePath
);
178 /// Gets the directory where the strongly named module generated by this scope will be saved, or <see langword="null"/> if the current directory
181 /// <value>The directory where the strongly named module generated by this scope will be saved when <see cref="SaveAssembly()"/> is called
182 /// (if this scope was created to save modules).</value>
183 public string StrongNamedModuleDirectory
187 string directory
= Path
.GetDirectoryName (strongModulePath
);
196 /// Gets the weak-named module generated by this scope, or <see langword="null"/> if none has yet been generated.
198 /// <value>The weak-named module generated by this scope, or <see langword="null"/> if none has yet been generated.</value>
199 public ModuleBuilder WeakNamedModule
205 return moduleBuilder
;
211 /// Gets the file name of the weakly named module generated by this scope.
213 /// <value>The file name of the weakly named module generated by this scope.</value>
214 public string WeakNamedModuleName
218 return Path
.GetFileName (weakModulePath
);
223 /// Gets the directory where the weakly named module generated by this scope will be saved, or <see langword="null"/> if the current directory
226 /// <value>The directory where the weakly named module generated by this scope will be saved when <see cref="SaveAssembly()"/> is called
227 /// (if this scope was created to save modules).</value>
228 public string WeakNamedModuleDirectory
232 string directory
= Path
.GetDirectoryName (weakModulePath
);
241 /// Gets the specified module generated by this scope, creating a new one if none has yet been generated.
243 /// <param name="isStrongNamed">If set to true, a strong-named module is returned; otherwise, a weak-named module is returned.</param>
244 /// <returns>A strong-named or weak-named module generated by this scope, as specified by the <paramref name="isStrongNamed"/> parameter.</returns>
245 public ModuleBuilder
ObtainDynamicModule (bool isStrongNamed
)
250 return ObtainDynamicModuleWithStrongName ();
252 return ObtainDynamicModuleWithWeakName ();
257 /// Gets the strong-named module generated by this scope, creating a new one if none has yet been generated.
259 /// <returns>A strong-named module generated by this scope.</returns>
260 public ModuleBuilder
ObtainDynamicModuleWithStrongName()
264 if (moduleBuilderWithStrongName
== null)
266 moduleBuilderWithStrongName
= CreateModule (true);
268 return moduleBuilderWithStrongName
;
273 /// Gets the weak-named module generated by this scope, creating a new one if none has yet been generated.
275 /// <returns>A weak-named module generated by this scope.</returns>
276 public ModuleBuilder
ObtainDynamicModuleWithWeakName ()
280 if (moduleBuilder
== null)
282 moduleBuilder
= CreateModule (false);
284 return moduleBuilder
;
288 private ModuleBuilder
CreateModule(bool signStrongName
)
290 AssemblyName assemblyName
= GetAssemblyName(signStrongName
);
292 string moduleName
= signStrongName
? StrongNamedModuleName
: WeakNamedModuleName
;
293 string moduleDirectory
= signStrongName
? StrongNamedModuleDirectory
: WeakNamedModuleDirectory
;
295 if (savePhysicalAssembly
)
297 AssemblyBuilder assemblyBuilder
= AppDomain
.CurrentDomain
.DefineDynamicAssembly(
298 assemblyName
, AssemblyBuilderAccess
.RunAndSave
, moduleDirectory
);
300 return assemblyBuilder
.DefineDynamicModule(moduleName
, moduleName
, true);
304 AssemblyBuilder assemblyBuilder
= AppDomain
.CurrentDomain
.DefineDynamicAssembly(
306 AssemblyBuilderAccess
.Run
);
308 return assemblyBuilder
.DefineDynamicModule(moduleName
, true);
312 private AssemblyName
GetAssemblyName (bool signStrongName
)
314 AssemblyName assemblyName
= new AssemblyName();
315 assemblyName
.Name
= signStrongName
? strongAssemblyName
: weakAssemblyName
;
319 byte[] keyPairStream
= GetKeyPair();
321 if (keyPairStream
!= null)
323 assemblyName
.KeyPair
= new StrongNameKeyPair(keyPairStream
);
330 /// Saves the generated assembly with the name and directory information given when this <see cref="ModuleScope"/> instance was created (or with
331 /// the <see cref="DEFAULT_FILE_NAME"/> and current directory if none was given).
335 /// This method stores the generated assembly in the directory passed as part of the module information specified when this instance was
336 /// constructed (if any, else the current directory is used). If both a strong-named and a weak-named assembly
337 /// have been generated, it will throw an exception; in this case, use the <see cref="SaveAssembly (bool)"/> overload.
340 /// If this <see cref="ModuleScope"/> was created without indicating that the assembly should be saved, this method does nothing.
341 /// </para></remarks>
342 /// <exception cref="InvalidOperationException">Both a strong-named and a weak-named assembly have been generated or no assembly has been
343 /// generated.</exception>
344 public void SaveAssembly ()
346 if (!savePhysicalAssembly
)
349 if (StrongNamedModule
!= null && WeakNamedModule
!= null)
350 throw new InvalidOperationException ("Both a strong-named and a weak-named assembly have been generated.");
351 else if (StrongNamedModule
!= null)
353 else if (WeakNamedModule
!= null)
354 SaveAssembly (false);
356 throw new InvalidOperationException ("No assembly has been generated.");
360 /// Saves the specified generated assembly with the name and directory information given when this <see cref="ModuleScope"/> instance was created
361 /// (or with the <see cref="DEFAULT_FILE_NAME"/> and current directory if none was given).
363 /// <param name="strongNamed">True if the generated assembly with a strong name should be saved (see <see cref="StrongNamedModule"/>);
364 /// false if the generated assembly without a strong name should be saved (see <see cref="WeakNamedModule"/>.</param>
367 /// This method stores the specified generated assembly in the directory passed as part of the module information specified when this instance was
368 /// constructed (if any, else the current directory is used).
371 /// If this <see cref="ModuleScope"/> was created without indicating that the assembly should be saved, this method does nothing.
374 /// <exception cref="InvalidOperationException">No assembly has been generated that matches the <paramref name="strongNamed"/> parameter.
376 public void SaveAssembly (bool strongNamed
)
378 if (!savePhysicalAssembly
)
381 AssemblyBuilder assemblyBuilder
;
382 string assemblyFileName
;
383 string assemblyFilePath
;
387 if (StrongNamedModule
== null)
388 throw new InvalidOperationException ("No strong-named assembly has been generated.");
391 assemblyBuilder
= (AssemblyBuilder
) StrongNamedModule
.Assembly
;
392 assemblyFileName
= StrongNamedModuleName
;
393 assemblyFilePath
= StrongNamedModule
.FullyQualifiedName
;
398 if (WeakNamedModule
== null)
399 throw new InvalidOperationException ("No weak-named assembly has been generated.");
402 assemblyBuilder
= (AssemblyBuilder
) WeakNamedModule
.Assembly
;
403 assemblyFileName
= WeakNamedModuleName
;
404 assemblyFilePath
= WeakNamedModule
.FullyQualifiedName
;
408 if (File
.Exists (assemblyFilePath
))
410 File
.Delete (assemblyFilePath
);
413 assemblyBuilder
.Save (assemblyFileName
);