0

我正在InstallShield 2015中重新實現一個大型安裝項目作爲MSI安裝程序(它以前是InstallShield的舊版InstallScript安裝程序)。我想做的許多改進之一是允許所有COM引用通過使用清單文件和以下免註冊的COM激活模式以免註冊方式解析。我認爲這在所有情況下都是不可能的,因爲我們有一些MMC管理單元,據我所知,這些管理單元不能使用無reg的COM,但我希望儘可能接近。掃描COM引用以幫助生成免註冊的COM清單

挑戰是搞清楚我需要創建的所有清單。我們提供數千個文件。我在尋找一種實用工具,可以幫助掃描文件以確定:

  1. 哪些文件表示COM服務器,什麼CLSID的做它們包含
  2. 哪些文件代表COM客戶端,並且什麼的CLSID做他們引用

希望通過這些信息,我可以使用InstallShield的免註冊COM嚮導來生成必要的清單文件。

是否有任何東西可以幫助解決這個問題?如果沒有已經爲了這個,我要去嘗試寫我自己的一個工具:

  1. 綜觀COM服務器的一些已知的CLSID,並試圖找出一些簽名身邊字節,可以幫助我確定如何一般來說,我可以在COM服務器中識別CLSID。
  2. 編寫代碼在我們所有的二進制文件中查找此簽名並獲取所有CLSID
  3. 在已知的CLSID中搜索我們所有的二進制文件,並假定來自服務器外部的代表客戶端。

如果事實證明我們使用的是ProgId,而不僅僅是CLSID,我可能不得不調整我的策略,但我希望這將覆蓋我們COM引用的很大一部分。

如果已經有任何實用工具可以幫助解決這個問題,我想使用它而不是自己寫。所以我正在尋找任何有關如何編寫這些內容的技巧尋找這方面已經完成的提示。

+0

看看[this](http://stackoverflow.com/a/23073183/1768303)有幫助。您應該可以在'DllRegisterServer'中使用'RegOverridePredefKey'來監視COM註冊。 – Noseratio

回答

0

與已刪除的答案How to read TLB (type libraries) of unmanaged code from C#?,害得我一個類型庫閱讀課的C#源代碼在http://clrinterop.codeplex.com/releases/view/17579的幫助下,我能讀類型庫拿起CLSID值(跳過文件如果嘗試加載類型庫失敗 - 試圖從每個可執行文件中加載一個庫類型)。然後,我對所有可執行文件進行了簡單搜索,以查找這些CLSID值。我不確定結果有多完整,但至少看起來有些有效。

GetDirCLSIDRefs函數是頂級函數,它返回可執行文件名列表,併爲每個函數返回一個關聯文件列表,其中的類型庫包含文件引用的CLSID。

Private Function GetDirCLSIDRefs(Directory As String, CLSIDs As KeyValuePair(Of Guid, String)()) As KeyValuePair(Of String, String())() 
    Dim result As New List(Of KeyValuePair(Of String, String())) 
    Dim entries = New System.IO.DirectoryInfo(Directory).GetFileSystemInfos() 
    For Each entry In entries 
     If TypeOf entry Is System.IO.FileInfo AndAlso _ 
     (entry.Name.EndsWith(".exe") OrElse _ 
      entry.Name.EndsWith(".dll") OrElse _ 
      entry.Name.EndsWith(".ocx")) Then 
     Dim refs = ScanForCLSIDs(entry.FullName, CLSIDs) 
     If refs IsNot Nothing AndAlso refs.Length > 0 Then result.Add(New KeyValuePair(Of String, String())(entry.FullName, refs)) 
     ElseIf TypeOf entry Is System.IO.DirectoryInfo Then 
     result.AddRange(GetDirCLSIDRefs(entry.FullName, CLSIDs)) 
     End If 
    Next 
    Return result.ToArray() 
End Function 

Private Function GetDirCLSIDs(Directory As String) As KeyValuePair(Of Guid, String)() 
    Dim result As New List(Of KeyValuePair(Of Guid, String)) 
    Dim entries = New System.IO.DirectoryInfo(Directory).GetFileSystemInfos() 
    For Each entry In entries 
     If TypeOf entry Is System.IO.FileInfo AndAlso _ 
     (entry.Name.EndsWith(".exe") OrElse _ 
      entry.Name.EndsWith(".dll") OrElse _ 
      entry.Name.EndsWith(".ocx")) Then 
     For Each clsid In GetCLSIDs(entry.FullName) 
      result.Add(New KeyValuePair(Of Guid, String)(clsid, entry.FullName)) 
     Next 
     ElseIf TypeOf entry Is System.IO.DirectoryInfo Then 
     result.AddRange(GetDirCLSIDs(entry.FullName)) 
     End If 
    Next 
    Return result.ToArray() 
End Function 

Private Function GetCLSIDs(FileName As String) As Guid() 
    Dim tlb = TypeLibTypes.Interop.TypeLib.Load(FileName) 
    If tlb.GetTypeLib() Is Nothing Then Return {} 
    Dim result As New List(Of Guid) 
    Try 
     For typeIndex As Integer = 0 To tlb.GetTypeInfoCount() - 1 
     Using attr = tlb.GetTypeInfo(typeIndex).GetTypeAttr() 
      If (attr.wTypeFlags And TypeLibTypes.Interop.TYPEFLAGS.TYPEFLAG_FHIDDEN) = 0 _ 
       AndAlso attr.typekind = TypeLibTypes.Interop.TYPEKIND.TKIND_COCLASS Then 
       result.Add(attr.Guid) 
      End If 
     End Using 
     Next 
    Catch ex As System.Runtime.InteropServices.COMException When ex.ErrorCode = &H80029C4A 
     Return {} 
    End Try 
    Return result.ToArray() 
End Function 

Private Function ScanForCLSIDs(FileName As String, CLSIDFiles As KeyValuePair(Of Guid, String)()) As String() 
    Dim content As Byte() 
    Using file As New System.IO.FileStream(FileName, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite) 
     content = DirectCast(Array.CreateInstance(GetType(Byte), CInt(file.Length)), Byte()) 
     file.Read(content, 0, CInt(file.Length)) 
    End Using 
    Dim result As New List(Of String) 
    For Each CLSIDFile In CLSIDFiles 
     If FindBytes(content, CLSIDFile.Key.ToByteArray()) >= 0 Then 
     If Not result.Contains(CLSIDFile.Value) Then result.Add(CLSIDFile.Value) 
     End If 
    Next 
    Return result.ToArray() 
End Function 

Private Function FindBytes(data As Byte(), pattern As Byte(), Optional start As Integer = 0) As Int32 
    If pattern.Length = 0 Then Return -1 
    If data.Length = 0 Then Return 0 
    Dim search = ProcessSearchPattern(pattern) 
    Dim i As Integer = start + pattern.Length - 1 
    Do While i < data.Length 
     Dim j As Integer = pattern.Length - 1 
     Do While j >= 0 AndAlso data(i) = pattern(j) 
     i -= 1 
     j -= 1 
     Loop 
     If j < 0 Then Return i + 1 
     i += Math.Max(search.Delta1(data(i)), search.Delta2(j)) 
    Loop 
    Return -1 
End Function 

Private Class SearchPattern 
    Public Delta1 As Integer() 
    Public Delta2 As Integer() 
End Class 

Private Class PatternBytes 
    Private bytes As Byte() 
    Public Sub New(value As Byte()) 
     bytes = value 
    End Sub 
    Public Overrides Function GetHashCode() As Integer 
     Dim i As Integer = 0 
     GetHashCode = 0 
     Do While i <= bytes.Length - 4 
     GetHashCode = GetHashCode Xor BitConverter.ToInt32(bytes, i) 
     i += 4 
     Loop 
     If i <= bytes.Length - 2 Then 
     GetHashCode = GetHashCode Xor BitConverter.ToInt16(bytes, i) 
     i += 2 
     End If 
     If i < bytes.Length Then 
     GetHashCode = GetHashCode Xor bytes(i) 
     End If 
    End Function 

    Public Overrides Function Equals(obj As Object) As Boolean 
     Dim compare As Byte() 
     If TypeOf obj Is PatternBytes Then 
     compare = DirectCast(obj, PatternBytes).bytes 
     ElseIf TypeOf obj Is Byte() Then 
     compare = DirectCast(obj, Byte()) 
     Else 
     Return False 
     End If 
     If compare.Length <> bytes.Length Then Return False 
     For i As Integer = 0 To bytes.Length - 1 
     If bytes(i) <> compare(i) Then Return False 
     Next 
     Return True 
    End Function 
End Class 

Private Function ProcessSearchPattern(pattern As Byte()) As SearchPattern 
    Static cache As New Dictionary(Of PatternBytes, SearchPattern) 
    ProcessSearchPattern = Nothing 
    Dim pb As New PatternBytes(pattern) 
    If cache.TryGetValue(pb, ProcessSearchPattern) Then Exit Function 
    ProcessSearchPattern = New SearchPattern() 
    Dim patLen = pattern.Length 
    ProcessSearchPattern.Delta1 = DirectCast(Array.CreateInstance(GetType(Integer), Byte.MaxValue + 1), Integer()) 
    ProcessSearchPattern.Delta2 = DirectCast(Array.CreateInstance(GetType(Integer), patLen), Integer()) 
    Dim c As Integer ' Cannot run a For loop to Byte.MaxValue in a Byte! 
    Dim i As Integer 
    For c = 0 To Byte.MaxValue 
     ProcessSearchPattern.Delta1(c) = patLen 
    Next 
    For i = 0 To patLen - 2 
     ProcessSearchPattern.Delta1(pattern(i)) = patLen - 1 - i 
    Next 

    Dim m As Integer 
    Dim lastPrefixIndex = patLen - 1 
    Dim isPrefix As Boolean = False 
    For i = patLen - 1 To 0 Step -1 
     isPrefix = True 
     If patLen - i - 2 > 0 Then 
     For m = 0 To patLen - i - 2 
      If pattern(m) <> pattern(m + i + 1) Then 
       isPrefix = False 
       Exit For 
      End If 
     Next 
     End If 
     If isPrefix Then lastPrefixIndex = i + 1 
     ProcessSearchPattern.Delta2(i) = lastPrefixIndex + (patLen - 1 - i) 
    Next 

    For i = 0 To patLen - 2 
     For m = 0 To i - 1 
     If pattern(i - m) = pattern(patLen - 1 - m) Then 
      Exit For 
     End If 
     If pattern(i - m) <> pattern(patLen - 1 - m) Then 
      ProcessSearchPattern.Delta2(patLen - 1 - m) = patLen - 1 - i + m 
     End If 
     Next 
    Next 

    cache.Add(pb, ProcessSearchPattern) 
End Function