1 //------------------------------------------------------------------------------
2 // <copyright file="ClientBuildManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /************************************************************************************************************/
8
9
10 namespace System.Web.Compilation {
11
12 using System;
13 using System.CodeDom;
14 using System.CodeDom.Compiler;
15 using System.Collections;
16 using System.Collections.Generic;
17 using System.ComponentModel;
18 using System.Reflection;
19 using System.Runtime.InteropServices;
20 using System.Runtime.Remoting;
21 using System.Security.Permissions;
22 using System.Threading;
23 using System.Web;
24 using System.Web.Hosting;
25 using System.Web.Util;
26 using Debug = System.Web.Util.Debug;
27
28
29 // Flags that drive the behavior of precompilation
30 [Flags]
31 public enum PrecompilationFlags {
32
33 Default = 0x00000000,
34
35 // determines whether the deployed app will be updatable
36 Updatable = 0x00000001,
37
38 // determines whether the target directory can be overwritten
39 OverwriteTarget = 0x00000002,
40
41 // determines whether the compiler will emit debug information
42 ForceDebug = 0x00000004,
43
44 // determines whether the application is built clean
45 Clean = 0x00000008,
46
47 // determines whether the /define:CodeAnalysis flag needs to be added
48 // as compilation symbol
49 CodeAnalysis = 0x00000010,
50
51 // determines whether to generate APTCA attribute.
52 AllowPartiallyTrustedCallers = 0x00000020,
53
54 // determines whether to delaySign the generate assemblies.
55 DelaySign = 0x00000040,
56
57 // determines whether to use fixed assembly names
58 FixedNames = 0x00000080,
59
60 // determines whether to skip BadImageFormatException
61 IgnoreBadImageFormatException = 0x00000100,
62 }
63
64 [Serializable]
65 public class ClientBuildManagerParameter {
66 private string _strongNameKeyFile;
67 private string _strongNameKeyContainer;
68 private PrecompilationFlags _precompilationFlags = PrecompilationFlags.Default;
69 private List<string> _excludedVirtualPaths;
70
71 public List<string> ExcludedVirtualPaths {
72 get {
73 if (_excludedVirtualPaths == null) {
74 _excludedVirtualPaths = new List<string>();
75 }
76 return _excludedVirtualPaths;
77 }
78 }
79
80 // Determines the behavior of the precompilation
81 public PrecompilationFlags PrecompilationFlags {
82 get { return _precompilationFlags; }
83 set { _precompilationFlags = value; }
84 }
85
86 public string StrongNameKeyFile {
87 get { return _strongNameKeyFile; }
88 set { _strongNameKeyFile = value; }
89 }
90
91 public string StrongNameKeyContainer {
92 get { return _strongNameKeyContainer; }
93 set { _strongNameKeyContainer = value; }
94 }
95 }
96
97 //
98 // This class provide access to the BuildManager outside of an IIS environment
99 // Instances of this class are created in the caller's App Domain.
100 //
101 // It creates and configures the new App Domain for handling BuildManager calls
102 // using System.Web.Hosting.ApplicationHost.CreateApplicationHost()
103 //
104
105 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
106 [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
107 public sealed class ClientBuildManager : MarshalByRefObject, IDisposable {
108
109 private VirtualPath _virtualPath;
110 private string _physicalPath;
111 private string _installPath;
112 private string _appId;
113 private IApplicationHost _appHost;
114 private string _codeGenDir;
115
116 private HostingEnvironmentParameters _hostingParameters;
117 private ClientBuildManagerTypeDescriptionProviderBridge _cbmTdpBridge;
118
119 private WaitCallback _onAppDomainUnloadedCallback;
120 private WaitCallback _onAppDomainShutdown;
121 private ApplicationShutdownReason _reason;
122
123 private BuildManagerHost _host;
124 private Exception _hostCreationException;
125 private bool _hostCreationPending;
126
127 public event BuildManagerHostUnloadEventHandler AppDomainUnloaded;
128
129 public event EventHandler AppDomainStarted;
130
131 public event BuildManagerHostUnloadEventHandler AppDomainShutdown;
132 // internal lock used for host creation.
133 private object _lock = new object();
134
135 // Whether to wait for the call back from the previous host unloading before creating a new one
136 private bool _waitForCallBack;
137
138 private const string IISExpressPrefix = "/IISExpress/";
139
140 /*
141 * Creates an instance of the ClientBuildManager.
142 * appPhysicalSourceDir points to the physical root of the application (e.g "c:\myapp")
143 * virtualPath is the virtual path to the app root. It can be anything (e.g. "/dummy"),
144 * but ideally it should match the path later given to Cassini, in order for
145 * compilation that happens here to be reused there.
146 */
147
ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir)148 public ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir) :
149 this(appVirtualDir, appPhysicalSourceDir,
150 appPhysicalTargetDir: null, parameter: null) {
151 }
152
153 /*
154 * Creates an instance of the PrecompilationManager.
155 * appPhysicalSourceDir points to the physical root of the application (e.g "c:\myapp")
156 * appVirtualDir is the virtual path to the app root. It can be anything (e.g. "/dummy"),
157 * but ideally it should match the path later given to Cassini, in order for
158 * compilation that happens here to be reused there.
159 * appPhysicalTargetDir is the directory where the precompiled site is placed
160 */
ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir, string appPhysicalTargetDir)161 public ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir,
162 string appPhysicalTargetDir) : this(appVirtualDir, appPhysicalSourceDir,
163 appPhysicalTargetDir, parameter: null) {
164 }
165
166 /*
167 * Creates an instance of the PrecompilationManager.
168 * appPhysicalSourceDir points to the physical root of the application (e.g "c:\myapp")
169 * appVirtualDir is the virtual path to the app root. It can be anything (e.g. "/dummy"),
170 * but ideally it should match the path later given to Cassini, in order for
171 * compilation that happens here to be reused there.
172 * appPhysicalTargetDir is the directory where the precompiled site is placed
173 * flags determines the behavior of the precompilation
174 */
ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir, string appPhysicalTargetDir, ClientBuildManagerParameter parameter)175 public ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir,
176 string appPhysicalTargetDir, ClientBuildManagerParameter parameter) :
177 this(appVirtualDir, appPhysicalSourceDir,
178 appPhysicalTargetDir, parameter, typeDescriptionProvider: null) {
179 }
180
181 /*
182 * Creates an instance of the PrecompilationManager.
183 * appPhysicalSourceDir points to the physical root of the application (e.g "c:\myapp")
184 * appVirtualDir is the virtual path to the app root. It can be anything (e.g. "/dummy"),
185 * but ideally it should match the path later given to Cassini, in order for
186 * compilation that happens here to be reused there.
187 * appPhysicalTargetDir is the directory where the precompiled site is placed
188 * typeDescriptionProvider is the provider used for retrieving type
189 * information for multi-targeting
190 */
ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir, string appPhysicalTargetDir, ClientBuildManagerParameter parameter, TypeDescriptionProvider typeDescriptionProvider)191 public ClientBuildManager(string appVirtualDir, string appPhysicalSourceDir,
192 string appPhysicalTargetDir, ClientBuildManagerParameter parameter,
193 TypeDescriptionProvider typeDescriptionProvider) {
194
195 if (parameter == null) {
196 parameter = new ClientBuildManagerParameter();
197 }
198
199 InitializeCBMTDPBridge(typeDescriptionProvider);
200
201 // Always build clean in precompilation for deployment mode,
202 // since building incrementally raises all kind of issues (VSWhidbey 382954).
203 if (!String.IsNullOrEmpty(appPhysicalTargetDir)) {
204 parameter.PrecompilationFlags |= PrecompilationFlags.Clean;
205 }
206
207 _hostingParameters = new HostingEnvironmentParameters();
208 _hostingParameters.HostingFlags = HostingEnvironmentFlags.DontCallAppInitialize |
209 HostingEnvironmentFlags.ClientBuildManager;
210 _hostingParameters.ClientBuildManagerParameter = parameter;
211 _hostingParameters.PrecompilationTargetPhysicalDirectory = appPhysicalTargetDir;
212 if (typeDescriptionProvider != null) {
213 _hostingParameters.HostingFlags |= HostingEnvironmentFlags.SupportsMultiTargeting;
214 }
215
216 // Make sure the app virtual dir starts with /
217 if (appVirtualDir[0] != '/')
218 appVirtualDir = "/" + appVirtualDir;
219
220 if (appPhysicalSourceDir == null
221 && appVirtualDir.StartsWith(IISExpressPrefix, StringComparison.OrdinalIgnoreCase)
222 && appVirtualDir.Length > IISExpressPrefix.Length) {
223 // appVirtualDir should have the form "/IISExpress/<version>/LM/W3SVC/",
224 // and we will try to extract the version. The version will be validated
225 // when it is passed to IISVersionHelper..ctor.
226 int endSlash = appVirtualDir.IndexOf('/', IISExpressPrefix.Length);
227 if (endSlash > 0) {
228 _hostingParameters.IISExpressVersion = appVirtualDir.Substring(IISExpressPrefix.Length, endSlash - IISExpressPrefix.Length);
229 appVirtualDir = appVirtualDir.Substring(endSlash);
230 }
231 }
232
233 Initialize(VirtualPath.CreateNonRelative(appVirtualDir), appPhysicalSourceDir);
234 }
235
236 /*
237 * returns the codegendir used by runtime appdomain
238 */
239 public string CodeGenDir {
240 get {
241 if (_codeGenDir == null) {
242 EnsureHostCreated();
243 _codeGenDir = _host.CodeGenDir;
244 }
245
246 return _codeGenDir;
247 }
248 }
249
250 /*
251 * Indicates whether the host is created.
252 */
253
254 public bool IsHostCreated {
255 get {
256 return _host != null;
257 }
258 }
259
260 /*
261 * Create an object in the runtime appdomain
262 */
263
CreateObject(Type type, bool failIfExists)264 public IRegisteredObject CreateObject(Type type, bool failIfExists) {
265 if (type == null) {
266 throw new ArgumentNullException("type");
267 }
268
269 EnsureHostCreated();
270 Debug.Assert(_appId != null);
271 Debug.Assert(_appHost != null);
272
273 _host.RegisterAssembly(type.Assembly.FullName, type.Assembly.Location);
274
275 ApplicationManager appManager = ApplicationManager.GetApplicationManager();
276 return appManager.CreateObjectInternal(_appId, type, _appHost, failIfExists, _hostingParameters);
277 }
278
279 /*
280 * Return the list of directories that would cause appdomain shutdown.
281 */
GetAppDomainShutdownDirectories()282 public string[] GetAppDomainShutdownDirectories() {
283 Debug.Trace("CBM", "GetAppDomainShutdownDirectories");
284
285 return FileChangesMonitor.s_dirsToMonitor;
286 }
287
288 /*
289 * Makes sure that all the top level files are compiled (code, global.asax, ...)
290 */
291
CompileApplicationDependencies()292 public void CompileApplicationDependencies() {
293 Debug.Trace("CBM", "CompileApplicationDependencies");
294
295 EnsureHostCreated();
296
297 _host.CompileApplicationDependencies();
298 }
299
300
GetBrowserDefinitions()301 public IDictionary GetBrowserDefinitions() {
302 Debug.Trace("CBM", "GetBrowserDefinitions");
303
304 EnsureHostCreated();
305
306 return _host.GetBrowserDefinitions();
307 }
308
309 /*
310 * Returns the physical path of the generated file corresponding to the virtual directory.
311 * Note the virtualPath needs to use this format:
312 * "/[appname]/App_WebReferences/{[subDir]/}"
313 */
GetGeneratedSourceFile(string virtualPath)314 public string GetGeneratedSourceFile(string virtualPath) {
315 Debug.Trace("CBM", "GetGeneratedSourceFile " + virtualPath);
316
317 if (virtualPath == null) {
318 throw new ArgumentNullException("virtualPath");
319 }
320
321 EnsureHostCreated();
322
323 return _host.GetGeneratedSourceFile(VirtualPath.CreateTrailingSlash(virtualPath));
324 }
325
326 /*
327 * Returns the virtual path of the corresponding generated file.
328 * Note the filepath needs to be a full path.
329 */
GetGeneratedFileVirtualPath(string filePath)330 public string GetGeneratedFileVirtualPath(string filePath) {
331 Debug.Trace("CBM", "GetGeneratedFileVirtualPath " + filePath);
332
333 if (filePath == null) {
334 throw new ArgumentNullException("filePath");
335 }
336
337 EnsureHostCreated();
338
339 return _host.GetGeneratedFileVirtualPath(filePath);
340 }
341 /*
342 * Returns an array of the virtual paths to all the code directories in the app thru the hosted appdomain
343 */
344
GetVirtualCodeDirectories()345 public string[] GetVirtualCodeDirectories() {
346 Debug.Trace("CBM", "GetHostedVirtualCodeDirectories");
347
348 EnsureHostCreated();
349
350 return _host.GetVirtualCodeDirectories();
351 }
352
353 /*
354 * Returns an array of the assemblies defined in the bin and assembly reference config section
355 */
356
GetTopLevelAssemblyReferences(string virtualPath)357 public String[] GetTopLevelAssemblyReferences(string virtualPath) {
358 Debug.Trace("CBM", "GetHostedVirtualCodeDirectories");
359
360 if (virtualPath == null) {
361 throw new ArgumentNullException("virtualPath");
362 }
363
364 EnsureHostCreated();
365
366 return _host.GetTopLevelAssemblyReferences(VirtualPath.Create(virtualPath));
367 }
368
369 /*
370 * Returns the compiler type and parameters that need to be used to build
371 * a given code directory. Also, returns the directory containing all the code
372 * files generated from non-code files in the code directory (e.g. wsdl files)
373 */
374
GetCodeDirectoryInformation(string virtualCodeDir, out Type codeDomProviderType, out CompilerParameters compilerParameters, out string generatedFilesDir)375 public void GetCodeDirectoryInformation(string virtualCodeDir,
376 out Type codeDomProviderType, out CompilerParameters compilerParameters,
377 out string generatedFilesDir) {
378 Debug.Trace("CBM", "GetCodeDirectoryInformation " + virtualCodeDir);
379
380 if (virtualCodeDir == null) {
381 throw new ArgumentNullException("virtualCodeDir");
382 }
383
384 EnsureHostCreated();
385
386 _host.GetCodeDirectoryInformation(VirtualPath.CreateTrailingSlash(virtualCodeDir),
387 out codeDomProviderType, out compilerParameters, out generatedFilesDir);
388
389 Debug.Trace("CBM", "GetCodeDirectoryInformation " + virtualCodeDir + " end");
390 }
391
392 /*
393 * Returns the compiler type and parameters that need to be used to build
394 * a given file.
395 */
396
GetCompilerParameters(string virtualPath, out Type codeDomProviderType, out CompilerParameters compilerParameters)397 public void GetCompilerParameters(string virtualPath,
398 out Type codeDomProviderType, out CompilerParameters compilerParameters) {
399 Debug.Trace("CBM", "GetCompilerParameters " + virtualPath);
400
401 if (virtualPath == null) {
402 throw new ArgumentNullException("virtualPath");
403 }
404
405 EnsureHostCreated();
406
407 _host.GetCompilerParams(VirtualPath.Create(virtualPath), out codeDomProviderType, out compilerParameters);
408 }
409
410 /*
411 * Returns the codedom tree and the compiler type/param for a given file.
412 */
413
GenerateCodeCompileUnit( string virtualPath, out Type codeDomProviderType, out CompilerParameters compilerParameters, out IDictionary linePragmasTable)414 public CodeCompileUnit GenerateCodeCompileUnit(
415 string virtualPath, out Type codeDomProviderType,
416 out CompilerParameters compilerParameters, out IDictionary linePragmasTable) {
417 Debug.Trace("CBM", "GenerateCodeCompileUnit " + virtualPath);
418
419 return GenerateCodeCompileUnit(virtualPath, null,
420 out codeDomProviderType, out compilerParameters, out linePragmasTable);
421 }
422
423
GenerateCodeCompileUnit( string virtualPath, String virtualFileString, out Type codeDomProviderType, out CompilerParameters compilerParameters, out IDictionary linePragmasTable)424 public CodeCompileUnit GenerateCodeCompileUnit(
425 string virtualPath, String virtualFileString, out Type codeDomProviderType,
426 out CompilerParameters compilerParameters, out IDictionary linePragmasTable) {
427 Debug.Trace("CBM", "GenerateCodeCompileUnit " + virtualPath);
428
429 if (virtualPath == null) {
430 throw new ArgumentNullException("virtualPath");
431 }
432
433 EnsureHostCreated();
434
435 return _host.GenerateCodeCompileUnit(VirtualPath.Create(virtualPath), virtualFileString,
436 out codeDomProviderType, out compilerParameters, out linePragmasTable);
437 }
438
GenerateCode( string virtualPath, String virtualFileString, out IDictionary linePragmasTable)439 public string GenerateCode(
440 string virtualPath, String virtualFileString, out IDictionary linePragmasTable) {
441 Debug.Trace("CBM", "GenerateCode " + virtualPath);
442
443 if (virtualPath == null) {
444 throw new ArgumentNullException("virtualPath");
445 }
446
447 EnsureHostCreated();
448
449 return _host.GenerateCode(VirtualPath.Create(virtualPath), virtualFileString, out linePragmasTable);
450 }
451
452 /*
453 * Returns the compiled type for an input file
454 */
455
GetCompiledType(string virtualPath)456 public Type GetCompiledType(string virtualPath) {
457 Debug.Trace("CBM", "GetCompiledType " + virtualPath);
458
459 if (virtualPath == null) {
460 throw new ArgumentNullException("virtualPath");
461 }
462
463 EnsureHostCreated();
464
465 string[] typeAndAsemblyName = _host.GetCompiledTypeAndAssemblyName(VirtualPath.Create(virtualPath), null);
466 if (typeAndAsemblyName == null)
467 return null;
468
469 Assembly a = Assembly.LoadFrom(typeAndAsemblyName[1]);
470 Type t = a.GetType(typeAndAsemblyName[0]);
471 return t;
472 }
473
474 /*
475 * Compile a file
476 */
CompileFile(string virtualPath)477 public void CompileFile(string virtualPath) {
478 CompileFile(virtualPath, null);
479 }
480
CompileFile(string virtualPath, ClientBuildManagerCallback callback)481 public void CompileFile(string virtualPath, ClientBuildManagerCallback callback) {
482 Debug.Trace("CBM", "CompileFile " + virtualPath);
483
484 if (virtualPath == null) {
485 throw new ArgumentNullException("virtualPath");
486 }
487
488 try {
489 EnsureHostCreated();
490 _host.GetCompiledTypeAndAssemblyName(VirtualPath.Create(virtualPath), callback);
491 }
492 finally {
493 // DevDiv 180798. We are returning null in ClientBuildManagerCallback.InitializeLifetimeService,
494 // so we need to manually disconnect the instance so that it will be released.
495 if (callback != null) {
496 RemotingServices.Disconnect(callback);
497 }
498 }
499 }
500
501 /*
502 * Indicates whether an assembly is a code assembly.
503 */
IsCodeAssembly(string assemblyName)504 public bool IsCodeAssembly(string assemblyName) {
505 Debug.Trace("CBM", "IsCodeAssembly " + assemblyName);
506
507 if (assemblyName == null) {
508 throw new ArgumentNullException("assemblyName");
509 }
510
511 //
512
513 EnsureHostCreated();
514 bool result = _host.IsCodeAssembly(assemblyName);
515
516 Debug.Trace("CBM", "IsCodeAssembly " + result.ToString());
517 return result;
518 }
519
520
Unload()521 public bool Unload() {
522 Debug.Trace("CBM", "Unload");
523
524 BuildManagerHost host = _host;
525 if (host != null) {
526 _host = null;
527 return host.UnloadAppDomain();
528 }
529
530 return false;
531 }
532
533 /*
534 * Precompile an application
535 */
PrecompileApplication()536 public void PrecompileApplication() {
537 PrecompileApplication(null);
538 }
539
540 /*
541 * Precompile an application with callback support
542 */
PrecompileApplication(ClientBuildManagerCallback callback)543 public void PrecompileApplication(ClientBuildManagerCallback callback) {
544 PrecompileApplication(callback, false);
545 }
546
PrecompileApplication(ClientBuildManagerCallback callback, bool forceCleanBuild)547 public void PrecompileApplication(ClientBuildManagerCallback callback, bool forceCleanBuild) {
548 Debug.Trace("CBM", "PrecompileApplication");
549
550 PrecompilationFlags savedFlags = _hostingParameters.ClientBuildManagerParameter.PrecompilationFlags;
551
552 if (forceCleanBuild) {
553
554 // If there was a previous host, it will be unloaded by CBM and we will wait for the callback.
555 // If there was no previous host, we don't do any waiting.
556 // DevDiv 46290
557 _waitForCallBack = _host != null;
558
559 Debug.Trace("CBM", "Started Unload");
560 // Unload the existing appdomain so the new one will be created with the clean flag
561 Unload();
562
563 _hostingParameters.ClientBuildManagerParameter.PrecompilationFlags =
564 savedFlags | PrecompilationFlags.Clean;
565
566 WaitForCallBack();
567 }
568
569 try {
570 EnsureHostCreated();
571 _host.PrecompileApp(callback, _hostingParameters.ClientBuildManagerParameter.ExcludedVirtualPaths);
572 }
573 finally {
574 if (forceCleanBuild) {
575 // Revert precompilationFlags
576 _hostingParameters.ClientBuildManagerParameter.PrecompilationFlags = savedFlags;
577 }
578 // DevDiv 180798. We are returning null in ClientBuildManagerCallback.InitializeLifetimeService,
579 // so we need to manually disconnect the instance so that it will be released.
580 if (callback != null) {
581 RemotingServices.Disconnect(callback);
582 }
583 }
584 }
585
586 // _waitForCallBack is set to false in OnAppDomainUnloaded.
587 // This method waits until it is set to false before continuing, so that
588 // we do not run into a concurrency issue where _host could be set to null.
589 // DevDiv 46290
WaitForCallBack()590 private void WaitForCallBack() {
591 Debug.Trace("CBM", "WaitForCallBack");
592 int waited = 0;
593 while (_waitForCallBack && waited <= 50) {
594 Thread.Sleep(200);
595 waited++;
596 }
597 if (_waitForCallBack) {
598 Debug.Trace("CBM", "timeout while waiting for callback");
599 }
600 else {
601 Debug.Trace("CBM", "callback received before timeout");
602 }
603 }
604
InitializeLifetimeService()605 public override Object InitializeLifetimeService() {
606 return null; // never expire lease
607 }
608
Initialize(VirtualPath virtualPath, string physicalPath)609 internal void Initialize(VirtualPath virtualPath, string physicalPath) {
610 Debug.Trace("CBM", "Initialize");
611
612 _virtualPath = virtualPath;
613
614 _physicalPath = FileUtil.FixUpPhysicalDirectory(physicalPath);
615
616 _onAppDomainUnloadedCallback = new WaitCallback(OnAppDomainUnloadedCallback);
617 _onAppDomainShutdown = new WaitCallback(OnAppDomainShutdownCallback);
618
619 _installPath = RuntimeEnvironment.GetRuntimeDirectory();
620
621 // Do not create host during intialization. It will be done on demand.
622 //CreateHost();
623 }
624
EnsureHostCreated()625 private void EnsureHostCreated() {
626
627 if (_host == null) {
628 lock (_lock) {
629 // Create the host if necessary
630 if (_host == null) {
631 CreateHost();
632 Debug.Trace("CBM", "EnsureHostCreated: after CreateHost()");
633 }
634 }
635 }
636
637 // If an exception happened during host creation, rethrow it
638 if (_hostCreationException != null) {
639 Debug.Trace("CBM", "EnsureHostCreated: failed. " + _hostCreationException);
640
641 // We need to wrap it in a new exception, otherwise we lose the original stack.
642 throw new HttpException(_hostCreationException.Message,
643 _hostCreationException);
644 }
645 }
646
CreateHost()647 private void CreateHost() {
648 Debug.Trace("CBM", "CreateHost");
649 Debug.Assert(_host == null);
650
651 Debug.Assert(!_hostCreationPending, "CreateHost: creation already pending");
652
653 _hostCreationPending = true;
654
655 // Use a local to avoid having a partially created _host
656 BuildManagerHost host = null;
657
658 try {
659 string appId;
660 IApplicationHost appHost;
661
662 ApplicationManager appManager = ApplicationManager.GetApplicationManager();
663
664 host = (BuildManagerHost) appManager.CreateObjectWithDefaultAppHostAndAppId(
665 _physicalPath, _virtualPath,
666 typeof(BuildManagerHost), false /*failIfExists*/,
667 _hostingParameters, out appId, out appHost);
668
669 // host appdomain cannot be unloaded during creation.
670 host.AddPendingCall();
671
672 host.Configure(this);
673
674 _host = host;
675 _appId = appId;
676 _appHost = appHost;
677
678 _hostCreationException = _host.InitializationException;
679 }
680 catch (Exception e) {
681 // If an exception happens, keep track of it
682 _hostCreationException = e;
683
684 // Even though the host initialization failed, keep track of it so subsequent
685 // request will see the error
686 _host = host;
687 }
688 finally {
689 _hostCreationPending = false;
690
691 if (host != null) {
692 // Notify the client that the host is ready
693 if (AppDomainStarted != null) {
694 AppDomainStarted(this, EventArgs.Empty);
695 }
696
697 // The host can be unloaded safely now.
698 host.RemovePendingCall();
699 }
700 }
701
702 Debug.Trace("CBM", "CreateHost LEAVE");
703 }
704
705 // Called by BuildManagerHost when the ASP appdomain is unloaded
OnAppDomainUnloaded(ApplicationShutdownReason reason)706 internal void OnAppDomainUnloaded(ApplicationShutdownReason reason) {
707 Debug.Trace("CBM", "OnAppDomainUnloaded " + reason.ToString());
708
709 _reason = reason;
710 _waitForCallBack = false;
711
712 // Don't do anything that can be slow here. Instead queue in a worker thread
713 ThreadPool.QueueUserWorkItem(_onAppDomainUnloadedCallback);
714 }
715
ResetHost()716 internal void ResetHost() {
717 lock (_lock) {
718 // Though _appId and _appHost are created along with _host,
719 // we need not reset those here as they always correspond to
720 // default app id and app host.
721 _host = null;
722 _hostCreationException = null;
723 }
724 }
725
726 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
OnAppDomainUnloadedCallback(Object unused)727 private void OnAppDomainUnloadedCallback(Object unused) {
728 Debug.Trace("CBM", "OnAppDomainUnloadedCallback");
729
730 // Notify the client that the appdomain is unloaded
731 if (AppDomainUnloaded != null) {
732 AppDomainUnloaded(this, new BuildManagerHostUnloadEventArgs(_reason));
733 }
734 }
735
736 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
OnAppDomainShutdownCallback(Object o)737 private void OnAppDomainShutdownCallback(Object o) {
738 if (AppDomainShutdown != null) {
739 AppDomainShutdown(this, new BuildManagerHostUnloadEventArgs((ApplicationShutdownReason)o));
740 }
741 }
742
OnAppDomainShutdown(ApplicationShutdownReason reason)743 internal void OnAppDomainShutdown(ApplicationShutdownReason reason) {
744 // Don't do anything that can be slow here. Instead queue in a worker thread
745 ThreadPool.QueueUserWorkItem(_onAppDomainShutdown, reason);
746 }
747
InitializeCBMTDPBridge(TypeDescriptionProvider typeDescriptionProvider)748 private void InitializeCBMTDPBridge(TypeDescriptionProvider typeDescriptionProvider) {
749 if (typeDescriptionProvider == null){
750 return;
751 }
752 _cbmTdpBridge = new ClientBuildManagerTypeDescriptionProviderBridge(typeDescriptionProvider);
753 }
754
755 internal ClientBuildManagerTypeDescriptionProviderBridge CBMTypeDescriptionProviderBridge {
756 get {
757 return _cbmTdpBridge;
758 }
759 }
760
761 #region IDisposable
762 //Dispose the runtime appdomain properly when CBM is disposed
IDisposable.Dispose()763 void IDisposable.Dispose() {
764 Unload();
765 }
766 #endregion
767
768 }
769
770
771 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
772 [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
773 public class BuildManagerHostUnloadEventArgs : EventArgs {
774 ApplicationShutdownReason _reason;
775
BuildManagerHostUnloadEventArgs(ApplicationShutdownReason reason)776 public BuildManagerHostUnloadEventArgs(ApplicationShutdownReason reason) {
777 _reason = reason;
778 }
779
780 // Get the reason for the hosted appdomain shutdown
781
782 public ApplicationShutdownReason Reason { get { return _reason; } }
783 }
784
785
BuildManagerHostUnloadEventHandler(object sender, BuildManagerHostUnloadEventArgs e)786 public delegate void BuildManagerHostUnloadEventHandler(object sender, BuildManagerHostUnloadEventArgs e);
787
788 /*
789 * Type of the entries in the table returned by GenerateCodeCompileUnit
790 */
791
792 [Serializable]
793 public sealed class LinePragmaCodeInfo {
794
LinePragmaCodeInfo()795 public LinePragmaCodeInfo() {
796 }
797
LinePragmaCodeInfo(int startLine, int startColumn, int startGeneratedColumn, int codeLength, bool isCodeNugget)798 public LinePragmaCodeInfo(int startLine, int startColumn, int startGeneratedColumn, int codeLength, bool isCodeNugget) {
799 this._startLine = startLine;
800 this._startColumn = startColumn;
801 this._startGeneratedColumn = startGeneratedColumn;
802 this._codeLength = codeLength;
803 this._isCodeNugget = isCodeNugget;
804 }
805
806 // Starting line in ASPX file
807 internal int _startLine;
808
809 public int StartLine { get { return _startLine; } }
810
811 // Starting column in the ASPX file
812 internal int _startColumn;
813
814 public int StartColumn { get { return _startColumn; } }
815
816 // Starting column in the generated source file (assuming no indentations are used)
817 internal int _startGeneratedColumn;
818
819 public int StartGeneratedColumn { get { return _startGeneratedColumn; } }
820
821 // Length of the code snippet
822 internal int _codeLength;
823
824 public int CodeLength { get { return _codeLength; } }
825
826 // Whether the script block is a nugget.
827 internal bool _isCodeNugget;
828
829 public bool IsCodeNugget { get { return _isCodeNugget; } }
830 }
831
832 }
833
834
835