Как написать плагин WinMerge в C# (или VB.NET)

Я хотел бы написать плагин WinMerge для перевода баз данных SQLite в текст, чтобы я мог использовать WinMerge для сравнения баз данных.

Я написал код на C#, чтобы сделать преобразование, но я не могу сделать его похожим на плагин WinMerge. Но я не очень знаком с написанием COM-видимых объектов.NET.

Я подумал, что я, должно быть, не вставил правильные атрибуты COM (я просто поместил ComVisible(true) в класс). Однако я думаю, что VB.Net должен делать все это за вас, поэтому я переписал класс в VB.Net, используя Project/Add New/COM class. Тем не менее, он все еще не отображается в WinMerge как загруженный плагин.

В отчаянии я попытался просмотреть библиотеку VB с помощью DLL Export Viewer, но она не показала экспортированные функции. Я явно делаю что-то не так.

Вот код в полном объеме:

<ComClass(WinMergeScript.ClassId, WinMergeScript.InterfaceId, WinMergeScript.EventsId)> _
Public Class WinMergeScript

#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "9b9bbe1c-7b20-4826-b12e-9062fc4549a0"
    Public Const InterfaceId As String = "b0f2aa59-b9d0-454a-8148-9715c83dbb71"
    Public Const EventsId As String = "8f4f9c82-6ba3-4c22-8814-995ca1050de6"
#End Region

    Dim _connection As SQLite.SQLiteConnection
    Dim _output As IO.TextWriter
    Dim _error As Long
    Dim _errordesc As String

    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub

    Public ReadOnly Property PluginEvent() As String
        Get
            PluginEvent = "FILE_PACK_UNPACK"
        End Get
    End Property

    Public ReadOnly Property PluginDescription() As String
        Get
            PluginDescription = "Display Sqlite Databases in tab-delimited format"
        End Get
    End Property

    Public ReadOnly Property PluginFileFilters() As String
        Get
            PluginFileFilters = "\.db$"
        End Get
    End Property

    Public ReadOnly Property LastErrorNumber() As Long
        Get
            LastErrorNumber = _error
        End Get
    End Property

    Public ReadOnly Property LastErrorString() As String
        Get
            LastErrorString = _errordesc
        End Get
    End Property

    Public Function UnpackFile(ByVal fileSrc As String, ByVal fileDst As String, ByRef bChanged As Boolean, ByRef subcode As Long) As Boolean
        On Error GoTo CleanUp
        subcode = 1
        _error = 0
        _errordesc = ""
        Using connection As New SQLite.SQLiteConnection("Data Source=" + fileSrc + ";Version=3;DateTimeFormat=ISO8601;")
            _connection = connection
            Using output As New IO.StreamWriter(fileDst)
                _output = output
                For Each table As DataRow In Query("Select name from sqlite_master where type = 'table' order by name")
                    Dump(table(0).ToString())
                Next
            End Using
        End Using
        bChanged = True
        UnpackFile = True
        Exit Function
CleanUp:
        _error = Err().Number
        _errordesc = Err().Description
        bChanged = False
        UnpackFile = False
    End Function

    Sub Dump(ByVal tablename As String)
        Dim reader As IDataReader
        Using cmd As New SQLite.SQLiteCommand(_connection)
            cmd.CommandText = "Select * from """ + tablename + """"
            cmd.CommandType = CommandType.Text
            reader = cmd.ExecuteReader()
            Using reader
                _output.WriteLine("==== " + tablename + " ====")
                Dim data(reader.FieldCount) As String
                For i As Integer = 0 To reader.FieldCount - 1
                    data(i) = reader.GetName(i)
                Next
                Dump(data)
                While reader.Read()
                    For i As Integer = 0 To reader.FieldCount - 1
                        data(i) = reader.GetValue(i).ToString()
                    Next
                    Dump(data)
                End While
            End Using
        End Using
    End Sub

    Sub Dump(ByVal data() As String)
        _output.WriteLine(String.Join(vbTab, data))
    End Sub

    Function Query(ByVal sql As String) As DataRowCollection
        Dim cmd As SQLite.SQLiteCommand
        cmd = _connection.CreateCommand()
        Using cmd
            cmd.CommandText = sql
            Using da As New SQLite.SQLiteDataAdapter(cmd)
                Dim dt As New DataTable()
                da.Fill(dt)
                Query = dt.Rows
            End Using
        End Using
    End Function
End Class

1 ответ

Я только что рассмотрел реальный код WinMerge 2.14.0 и думаю, что он работает только для реальных библиотек COM (и OCX), главным образом потому, что он работает, не ожидая регистрации объектов COM. Это должно быть расширено, чтобы, по крайней мере, ссылаться на.TLBs или было изменено так, чтобы не указывать путь, а указывать более явный идентификатор, чем "WinMergeScript".


В работающем VB.NET COM-объекте я явно упоминаю <ComVisible(True)> _ и у проекта есть "Регистрация для COM", проверенная в Свойствах Компиляции.

Я только что создал HelloWorld использовать ваш код, скомпилировать его vbc /t:library и зарегистрировал его regasm /codebase, Это было успешно вызвано с помощью этого простого vbscript (используя wscript):

Option Explicit
Dim so
Set so = CreateObject("SO13035027.WinMergeScript")
MsgBox so.HelloWorld

И весь код (я не перенастроил свою систему, чтобы использовать позже vbc так что это было на самом деле скомпилировано в.NET 1.1):

Option Explicit On
Option Strict On
'Option Infer On

Imports Microsoft.VisualBasic

Namespace SO13035027
<ComClass(WinMergeScript.ClassId, WinMergeScript.InterfaceId, WinMergeScript.EventsId)> _
Public Class WinMergeScript

#Region "COM GUIDs"
' These  GUIDs provide the COM identity for this class 
' and its COM interfaces. If you change them, existing 
' clients will no longer be able to access the class.
Public Const ClassId As String =     "9b9bbe1c-7b20-4826-b12e-9062fc4549a2"
Public Const InterfaceId As String = "b0f2aa59-b9d0-454a-8148-9715c83dbb72"
Public Const EventsId As String =    "8f4f9c82-6ba3-4c22-8814-995ca1050de2"
#End Region

' A creatable COM class must have a Public Sub New() 
' with no parameters, otherwise, the class will not be 
' registered in the COM registry and cannot be created 
' via CreateObject.
Public Sub New()
    MyBase.New()
End Sub

Public Function HelloWorld() As String
 Return "Hello, world!"
End Function

End Class
End Namespace
Другие вопросы по тегам