I would like to write a WinMerge plugin to translate SQLite databases to text, so I can use WinMerge to compare databases.
I have written code in C# to do the conversion, but I can't seem to make it appear as a WinMerge plugin. But I'm not very familiar with writing COM-visible .NET objects.
I figured I must not have put in the right COM attributes (I just put ComVisible(true) on the class). However, I think VB.Net is supposed to do all that stuff for you, so I rewrote the class in VB.Net, using Project/Add New/COM class. However, it still does not appear in WinMerge as a loaded plugin.
In desperation, I tried looking at the VB DLL using DLL Export Viewer, but it did not show any exported functions. I'm obviously doing something wrong.
Here is the code in full:
<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
I've just now reviewed the actual code of WinMerge 2.14.0, and I think it only works for real COM DLLs (and OCXs), mainly because it works without expecting the COM objects being registered. It would need to be expanded to at least refer to .TLBs or adjusted to allow no path to be specified, but a more explicit ID than "WinMergeScript".
In a working VB.NET COM object I explicitly mention <ComVisible(True)> _
and the project has "Register for COM" checked in the Compile Properties.
I just created a HelloWorld
function using your code, compiled it with vbc /t:library
and registered it with regasm /codebase
. This was successfully called using this simple vbscript (using wscript
):
Option Explicit
Dim so
Set so = CreateObject("SO13035027.WinMergeScript")
MsgBox so.HelloWorld
And the whole code (I didn't reconfigure my system to use a later vbc
so this was actually compiled to .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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With