如何用C#(或VB.NET)编写WinMerge插件

Nik*_*cke 5 c# vb.net plugins winmerge

我想编写一个WinMerge插件来将SQLite数据库转换为文本,因此我可以使用WinMerge来比较数据库.

我已用C#编写代码来进行转换,但我似乎无法将其显示为WinMerge插件.但是我对编写COM可见的.NET对象并不是很熟悉.

我想我不能放入正确的COM属性(我只是把ComVisible(true)放在类上).但是,我认为VB.Net应该为你做所有的事情,所以我在VB.Net中重写了类,使用Project/Add New/COM类.但是,它仍然不会作为加载的插件出现在WinMerge中.

无奈之下,我尝试使用DLL导出查看器查看VB DLL,但它没有显示任何导出的函数.我显然做错了什么.

这是完整的代码:

<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
Run Code Online (Sandbox Code Playgroud)

Mar*_*urd 1

我刚刚回顾了 WinMerge 2.14.0 的实际代码,我认为它只适用于真正的 COM DLL(和 OCX),主要是因为它的工作不需要注册 COM 对象。需要将其扩展为至少引用 .TLB 或进行调整以允许不指定路径,但允许指定比“WinMergeScript”更明确的 ID。


在工作的 VB.NET COM 对象中,我明确提到<ComVisible(True)> _,并且该项目在编译属性中选中了“Register for COM”。

我刚刚HelloWorld使用您的代码创建了一个函数,将其编译vbc /t:library并注册为regasm /codebase. 使用这个简单的 vbscript 成功调用了此函数(使用wscript):

Option Explicit
Dim so
Set so = CreateObject("SO13035027.WinMergeScript")
MsgBox so.HelloWorld
Run Code Online (Sandbox Code Playgroud)

整个代码(我没有重新配置我的系统以使用更高版本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
Run Code Online (Sandbox Code Playgroud)