基本的なコード生成を行う単純な VB.Net 4 WinForms アプリケーションがあります。コード生成によって DLL アセンブリが完全に正常に作成されますが、DLL が生成されるたびに、プログラムで GAC に登録する必要があります。登録する必要があるのは、デプロイ時に VB6 アプリケーションから CreateObject を介して呼び出される COM オブジェクトであるためです。ええと、私は知っています。
DLL の生成、プログラムによる登録、VB6 アプリケーションからの生成された DLL の使用など、これらすべてが正常に機能します。
問題は、コード生成を行うアプリケーションが、プロセスによって DLL がロックアウトされる前に DLL を 1 回しか生成できず、EXE を停止して再度起動しない限りロックを解除できないことです。これにより、コード生成ツールのユーザーは、アプリケーションを再起動せずに DLL を変更して再コンパイルすることができなくなります。
ロックを引き起こすコードは次のとおりです (変数 "asm" を定義する行にあります)。
Public Function RegisterAssembly() As Boolean
Dim success As Boolean = False
Try
Dim asm As [Assembly] = [Assembly].LoadFile(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "my.dll"))
Dim regasm As New RegistrationServices()
success = regasm.RegisterAssembly(asm, AssemblyRegistrationFlags.None)
Catch ex As Exception
success = False
Throw ex
End Try
Return success
End Function
Web 上の多くの記事で見たように、アセンブリ定義を別の AppDomain にスローしようとしましたが、思いついた実装はどれも機能しませんでした。実際、それらのほとんどすべてが、生成されたアセンブリになり、AppDomains の両方で定義されました。ReflectionOnly アセンブリの読み込みも試みましたが、関数を登録するには、アセンブリをリフレクション モードではなくアクティブ モードで読み込む必要があります。
それがスローする実際のエラーは、この素晴らしい宝石です:
Error Number: BC31019
Error Message: Unable to write to output file 'C:\Users\Me\AppData\Local\Temp\my.dll': The process cannot access the file because it is being used by another process.
これを修正するために私ができることについて誰かが私に答えを持っていれば、私は大いに感謝しています! 前述のように、最初の DLL コンパイルはうまく機能しますが、アプリケーション プロセスによってアセンブリがロックされているため、その後の DLL のコンパイルは失敗します。
私の完全なコンパイラ クラス定義は以下のとおりです。私が試したいくつかの追加のものを残しました。乱雑で申し訳ありません:
Imports System.CodeDom.Compiler
Imports System.Text
Imports System.IO
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Class ExitCompiler
Private _errorMessageContents As String = ""
Private _errorCount As Integer = -1
Public ReadOnly Property ErrorMessageText As String
Get
Return _errorMessageContents
End Get
End Property
Public ReadOnly Property ErrorCount As Integer
Get
Return _errorCount
End Get
End Property
Public Function Compile(ByVal codeFileInfo As FileInfo) As Boolean
Dim success As Boolean = False
Dim codeContents As String = CodeReader.ReadAllContents(codeFileInfo.FullName)
success = Compile(codeContents)
Return success
End Function
Public Function Compile(ByVal codeContents As String) As Boolean
_errorMessageContents = ""
'asmAppDomain = AppDomain.CreateDomain("asmAppDomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.RelativeSearchPath, AppDomain.CurrentDomain.ShadowCopyFiles)
LogLoadedAssemblies(AppDomain.CurrentDomain)
' LogLoadedAssemblies(asmAppDomain)
Try
' Remove output assemblies from previous compilations
'RemoveAssembly()
Catch uaEx As UnauthorizedAccessException
Throw uaEx
End Try
Dim success As Boolean = False
Dim outputFileName As String = Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "my.dll")
Dim results As CompilerResults
Dim codeProvider As New VBCodeProvider()
Dim parameters As New CompilerParameters()
parameters.TreatWarningsAsErrors = False
parameters.CompilerOptions = "/optimize"
parameters.TempFiles = New TempFileCollection(My.Computer.FileSystem.SpecialDirectories.Temp, False)
parameters.OutputAssembly = outputFileName
parameters.ReferencedAssemblies.Add("System.dll")
parameters.ReferencedAssemblies.Add("System.Data.dll")
parameters.ReferencedAssemblies.Add("System.Xml.dll")
results = codeProvider.CompileAssemblyFromSource(parameters, codeContents)
_errorCount = results.Errors.Count
If _errorCount > 0 Then
success = False
'There were compiler errors
Dim sb As New StringBuilder
For Each compileError As CompilerError In results.Errors
sb.AppendLine()
sb.AppendLine("Line number: " & compileError.Line)
sb.AppendLine("Error Number: " & compileError.ErrorNumber)
sb.AppendLine("Error Message: " & compileError.ErrorText)
sb.AppendLine()
Next
_errorMessageContents = sb.ToString()
Else
success = True
' Successful compile, now generate the TLB (Optional)
'success = GenerateTypeLib()
If success Then
' Type lib generated, now register with GAC
Try
success = RegisterAssembly()
Catch ex As Exception
success = False
End Try
End If
End If
Return success
End Function
'Private Function GenerateTypeLib() As Boolean
' Dim success As Boolean = False
' Try
' Dim asm As [Assembly] = [Assembly].ReflectionOnlyLoadFrom(My.Computer.FileSystem.SpecialDirectories.Temp & "\my.dll")
' Dim converter As New TypeLibConverter()
' Dim eventHandler As New ConversionEventHandler()
' Dim typeLib As UCOMICreateITypeLib = CType(converter.ConvertAssemblyToTypeLib(asm, My.Computer.FileSystem.SpecialDirectories.Temp & "\my.tlb", 0, eventHandler), UCOMICreateITypeLib)
' typeLib.SaveAllChanges()
' success = True
' Catch ex As Exception
' success = False
' Throw ex
' End Try
' Return success
'End Function
Public Function RegisterAssembly() As Boolean
Dim success As Boolean = False
Try
Dim asm As [Assembly] = [Assembly].LoadFile(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "my.dll"))
Dim regasm As New RegistrationServices()
success = regasm.RegisterAssembly(asm, AssemblyRegistrationFlags.None)
Catch ex As Exception
success = False
Throw ex
End Try
Return success
End Function
Public Sub RemoveAssembly()
'AppDomain.Unload(asmAppDomain)
File.Delete(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "my.dll"))
'File.Delete(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "my.tlb"))
End Sub
Private Shared Sub LogLoadedAssemblies(appDomain__1 As AppDomain)
Dim sb As New StringBuilder
sb.AppendLine("Loaded assemblies in appdomain: " & appDomain__1.FriendlyName)
For Each loadedAssembly As Assembly In AppDomain.CurrentDomain.GetAssemblies()
sb.AppendLine("- " & loadedAssembly.GetName().Name)
Next
MessageBox.Show(sb.ToString())
End Sub
'Private Shared Function CurrentDomain_ReflectionOnlyAssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
' Return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name)
'End Function
End Class