Registering a User Defined Function with Excel

Pages in this article

  1. Introduction
  2. How does it work?
  3. Setting things up
  4. Class Module
  5. VBA code
  6. Put to Use
  7. Wrap Up

Class module

The main code has been written in a class module called CUdfHelper.

This class contains almost all code you need to set things up. Internally, there is a number of tricks involved in getting things working reliably:

  • Write all argument data for the REGISTER function into Excel's hidden name space
  • Execute the REGISTER function using ExecuteExcel4Macro
  • This creates a new hidden name, with the name of the function and a pointer to the dll that has been used. To ensure nothing goes wrong, this pointer is set to zero
  • By declaring the UDF functions as type private, you avoid them from showing  up twice in the function wizard.

Relevant code:

Function RegisterFunction() As Boolean
' Purpose   : This procedure registers the user defined function
' Arguments : none
' Results   : True is the function was registered successfully
'
' Notes     : You must FIRST assign the properties
' ---------------------------------------------------------------------
' Date        Developer                   Action
' 2006-02-23  Jurgen Volkerink            Created

    Dim i%, sCmd$, vRes


    If VERBOSE Then
        'Check we've got enough data
        Select Case True
        Case Len(m_uArgs.sDllName) = 0
            MsgBox "DLLname not specified", vbExclamation
            Exit Function
        Case Len(m_uArgs.sDllProc) = 0
            MsgBox "DLLproc not specified", vbExclamation
        Case ProcInUse
            Select Case MsgBox(DllName & " " & DllProc & " already registered." & _
                    vbLf & "Delete existing registration?", vbOKCancel)
            Case vbOK
                UnregisterFunction
            Case vbCancel
                Exit Function
            End Select
        Case Len(m_uArgs.sFunText) = 0
            MsgBox "FunText not specified", vbExclamation
            Exit Function
        Case Len(m_uArgs.vCatName) = 0
            MsgBox "CatName not specified", vbExclamation
            Exit Function
        End Select
    Else
        'errors will be raised by the property procedures
    End If

    'Clear existing names
    DelArgumentNames
    'Define names for each argument needed by the Register function
    SetArgumentNames

    'Create the command string
    For i = 1 To 10 + NumArgs: sCmd = sCmd & "," & NAMEID & i: Next
    sCmd = "REGISTER(" & Mid(sCmd, 2) & ")"

    'Execute the command
    vRes = Application.ExecuteExcel4Macro(sCmd)

    'Block access to the DLL memory address
    'Assign 0 to the Function namespace!!!!
    SetGlobalName Me.FunText, 0

    'Unload arguments from namespace
    DelArgumentNames

    If IsError(vRes) Then
        vRes = "Failed to Register:" & DllName & " " & DllProc & " " & FunText
        If VERBOSE Then
            MsgBox vRes
        Else
            Err.Raise 5, , vRes
        End If
    End If


End Function

And the routines that handle creating and deleting of the hidden names:

Private Function SetGlobalName(sName As String, Optional ByVal vValue As Variant) As Variant
'defines a global name to refer to a value.
'if vValue is omitted the name 'sName' is deleted.

    Dim sCmd$
    Select Case True
    Case IsArray(vValue)
        Err.Raise 16, , "SetGlobalName: Arrays s/b assigned as string like '{1,2,3}'"
    Case IsMissing(vValue)
        sCmd = "SET.NAME(" & Q & sName & Q & ")"
    Case IsEmpty(vValue)
        sCmd = "SET.NAME(" & Q & sName & QCQ & Q & ")"
    Case TypeName(vValue) = "String"
        sCmd = "SET.NAME(" & Q & sName & QCQ & vValue & Q & ")"
    Case Else
        'Int'l: The value must use a . as decimal separator
        vValue = Application.Substitute(CStr(vValue), Application.International(xlDecimalSeparator), ".")
        sCmd = "SET.NAME(" & Q & sName & QC & vValue & ")"
    End Select
    SetGlobalName = Application.ExecuteExcel4Macro(sCmd)
End Function

Private Sub DelArgumentNames()
'Deletes the argument "names" from Excel's (hidden) global namespace
    Dim i%
    For i = 1 To 30
        SetGlobalName NAMEID & i
    Next
End Sub