tags:

views:

819

answers:

1

Has anybody attempted to pass a VBA (or VB6) function (via AddressOf ?) to the SQLite create a UDF function (http://www.sqlite.org/c3ref/create_function.html).

How would the resulting callback arguments be handled by VBA?

The function to be called would have the following signature...

void (*xFunc)(sqlite3_context*,int,sqlite3_value**)

+3  A: 

Unfortunately, you can't use a VB6/VBA function as a callback directly as VB6 only generates stdcall functions rather than the cdecl functions SQLite expects.

You will need to write a C dll to proxy the calls back and forth or recompile SQLite to to support your own custom extension.

After recompiling your dll to export the functions as stdcall, you can register a function with the following code:

'Create Function
Public Declare Function sqlite3_create_function Lib "SQLiteVB.dll" (ByVal db As Long, ByVal zFunctionName As String, ByVal nArg As Long, ByVal eTextRep As Long, ByVal pApp As Long, ByVal xFunc As Long, ByVal xStep As Long, ByVal xFinal As Long) As Long

'Gets a value
Public Declare Function sqlite3_value_type Lib "SQLiteVB.dll" (ByVal arg As Long) As SQLiteDataTypes    'Gets the type
Public Declare Function sqlite3_value_text_bstr Lib "SQLiteVB.dll" (ByVal arg As Long) As String        'Gets as String
Public Declare Function sqlite3_value_int Lib "SQLiteVB.dll" (ByVal arg As Long) As Long                'Gets as Long

'Sets the Function Result
Public Declare Sub sqlite3_result_int Lib "SQLiteVB.dll" (ByVal context As Long, ByVal value As Long)
Public Declare Sub sqlite3_result_error_code Lib "SQLiteVB.dll" (ByVal context As Long, ByVal value As Long)

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long)

Public Property Get ArgValue(ByVal argv As Long, ByVal index As Long) As Long
    CopyMemory ArgValue, ByVal (argv + index * 4), 4
End Property

Public Sub FirstCharCallback(ByVal context As Long, ByVal argc As Long, ByVal argv As Long)
    Dim arg1 As String
    If argc >= 1 Then
        arg1 = sqlite3_value_text_bstr(ArgValue(argv, 0))
        sqlite3_result_int context, AscW(arg1)
    Else
        sqlite3_result_error_code context, 666
    End If
End Sub

Public Sub RegisterFirstChar(ByVal db As Long)
    sqlite3_create_function db, "FirstChar", 1, 0, 0, AddressOf FirstCharCallback, 0, 0
    'Example query: SELECT FirstChar(field) FROM Table
End Sub
rpetrich
I've managed to find a stdcall friendly wrapper which also converts c arraya to safe arrays etc., so calling non-callback functions works fine, its just how to handle c-like structs returned by a callback, that's my next problem.As you said VBA might very well choke before that point is reached.
gobansaor
I've managed to modify the SQLite3VB wrapper to support stdcall from certain functions. As my changes were pretty involved, I only posted the VB code. If you'd like the changes I made to the SQLite source, please post your email so I can send. Perhaps after you can post on your blog?
rpetrich
That's excellent, exactly what I was looking for! It would be great if you could send the SQLite source changes to "tom at gobansaor.com". I'm in the process of pulling together various SQLite/ExcelVBA code bits which I'll then publish on my blog, I'll make sure to give you a mention when I do.
gobansaor