以编程方式注册生成的VB.Net DLL程序集而不锁定DLL

时间:2011-11-08 00:10:18

标签: .net vb.net process locking regasm

我有一个简单的VB.Net 4 WinForms应用程序,可以执行基本代码生成。代码生成完全可以创建DLL程序集,但每次生成DLL时,都需要以编程方式向GAC注册。它必须注册的原因是它是一个COM对象,当部署时,从VB6应用程序通过CreateObject调用。 Eww,我知道。

所有这一切都运行正常:DLL生成,以编程方式注册并使用VB6应用程序生成的DLL。

问题是我的代码生成应用程序只能在DLL被进程锁定之前生成一次DLL,而无法在不停止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

我已经尝试将程序集定义抛出到不同的AppDomain中,正如我在Web上的一些文章中看到的那样,但我提出的所有实现都没有奏效。实际上,几乎所有这些都以生成的程序集结束,然后在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

3 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

  

需要以编程方式向GAC注册。它的原因   必须注册的是它是一个COM对象

您无需将基于.NET的COM对象放入GAC。您只需要使用标准.NET regasm实用程序注册它们,并且可以使用System.Diagnostics.Proces对象将regasm实用程序作为外部进程调用。

您是否有其他原因将GAC用于COM对象?

答案 2 :(得分:0)

使用CompileAssemblyFromSource时,您不仅要进行编译,而是将Assembly加载到当前进程中,这样就可以正常运行,直到appdomain被卸载为止。

您是否考虑过使用命令行编译器vbc?它会独立于app域执行编译,然后DLL只会在磁盘上创建,没有对它的引用会锁定它吗?