Unfortunately that doesn't work - I should have clarified that I did try passing it an array of strings as well (that's the first thing I tried). I just mentioned the other two specifically because those are the non-standard things I tried. VB stores it's strings as unicode. When you are calling an API that expects a pointer to a string (as a LONG) you modify the call to pass a string "ByVal". When you call the function, VB actually does the conversion to an ANSI string from unicode behind the scenes to make the call. It apparently doesn't do that for you when you attempt to pass it a string array. As it turns out, the solution is the second thing I mentioned in my original question - converting the unicode to byte arrays, then placing the pointers to those arrays in an array of LONG and passing a pointer to that array to the function. The reason the call wasn't working for me was that I was trying to start a third party service and the documentation as to the required parameters was wrong. Basically I was passing the arguments successfully, but I was passing an argument that they had since deprecated (it used to be the first argument and now isn't even an argument) so all the subsequent arguments were "in the wrong place". I'm putting an example of the code below for anyone else who might need it. I've made it so it can handle up to 10 arguments. It's problematic making it truly dynamic as far as the number of arguments it can handle since you are putting pointers to the byte array variables in the array and therefore can't re-use the same byte array for the different arguments - you have to declare the byte arrays beforehand. Anyway, here's the code:
====================================
' Force declaration of all variables
Option Explicit
' Declare APIs
Private Declare Function OpenSCManager Lib "advapi32.dll" Alias "OpenSCManagerA" (ByVal lpMachineName As String, ByVal lpDatabaseName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function OpenService Lib "advapi32.dll" Alias "OpenServiceA" (ByVal lngSCManager As Long, ByVal lpServiceName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function StartService Lib "advapi32.dll" Alias "StartServiceA" (ByVal hService As Long, ByVal dwNumServiceArgs As Long, ByVal lpServiceArgVectors As Long) As Long
Private Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject As Long) As Long
' Define constants
' (some extras in here from what I'm using, but someone else might need
' them for other service stuff)
' Service Control
Private Const SERVICE_CONTROL_STOP = &H1
Private Const SERVICE_CONTROL_PAUSE = &H2
Private Const SERVICES_ACTIVE_DATABASE = "ServicesActive"
' Service State - for CurrentState
Private Const SERVICE_STOPPED = &H1
Private Const SERVICE_START_PENDING = &H2
Private Const SERVICE_STOP_PENDING = &H3
Private Const SERVICE_RUNNING = &H4
Private Const SERVICE_CONTINUE_PENDING = &H5
Private Const SERVICE_PAUSE_PENDING = &H6
Private Const SERVICE_PAUSED = &H7
Private Const SERVICE_ACTIVE = &H1
Private Const SERVICE_INACTIVE = &H2
'Service Control Manager object specific access types
Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const SC_MANAGER_CONNECT = &H1
Private Const SC_MANAGER_CREATE_SERVICE = &H2
Private Const SC_MANAGER_ENUMERATE_SERVICE = &H4
Private Const SC_MANAGER_LOCK = &H8
Private Const SC_MANAGER_QUERY_LOCK_STATUS = &H10
Private Const SC_MANAGER_MODIFY_BOOT_CONFIG = &H20
Private Const SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE Or SC_MANAGER_ENUMERATE_SERVICE Or SC_MANAGER_LOCK Or SC_MANAGER_QUERY_LOCK_STATUS Or SC_MANAGER_MODIFY_BOOT_CONFIG)
'Service object specific access types
Private Const SERVICE_QUERY_CONFIG = &H1
Private Const SERVICE_CHANGE_CONFIG = &H2
Private Const SERVICE_QUERY_STATUS = &H4
Private Const SERVICE_ENUMERATE_DEPENDENTS = &H8
Private Const SERVICE_START = &H10
Private Const SERVICE_STOP = &H20
Private Const SERVICE_PAUSE_CONTINUE = &H40
Private Const SERVICE_INTERROGATE = &H80
Private Const SERVICE_USER_DEFINED_CONTROL = &H100
Private Const SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SERVICE_QUERY_CONFIG Or SERVICE_CHANGE_CONFIG Or SERVICE_QUERY_STATUS Or SERVICE_ENUMERATE_DEPENDENTS Or SERVICE_START Or SERVICE_STOP Or SERVICE_PAUSE_CONTINUE Or SERVICE_INTERROGATE Or SERVICE_USER_DEFINED_CONTROL)
Private Const SERVICE_WIN32_OWN_PROCESS As Long = &H10
Private Const SERVICE_WIN32_SHARE_PROCESS As Long = &H20
Private Const SERVICE_WIN32 As Long = SERVICE_WIN32_OWN_PROCESS + SERVICE_WIN32_SHARE_PROCESS
Private Const SERVICE_DEMAND_START As Long = &H3
Private Const SERVICE_ERROR_NORMAL As Long = &H1
Private Const ERROR_MORE_DATA = 234
' Define UDTs
Private Type SERVICE_STATUS
dwServiceType As Long
dwCurrentState As Long
dwControlsAccepted As Long
dwWin32ExitCode As Long
dwServiceSpecificExitCode As Long
dwCheckPoint As Long
dwWaitHint As Long
End Type
Public Function startComputerService(strComputerName As String, strServiceName As String) As Long
' Declare local variable(s)
Dim udtServiceStatus As SERVICE_STATUS
Dim lngServiceManagerHandle As Long
Dim lngServiceHandle As Long
Dim lngReturnCode As Long
Dim lngAPIReturn As Long
' Initialize the return code
lngReturnCode = 0
' Open the computer Service Manager
lngServiceManagerHandle = OpenSCManager(strComputerName, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS)
' If we successfully opened the service manager, call the API
' to start the specified service
If lngServiceManagerHandle <> 0 Then
lngServiceHandle = OpenService(lngServiceManagerHandle, strServiceName, SERVICE_ALL_ACCESS)
If lngServiceHandle <> 0 Then
lngAPIReturn = StartService(lngServiceHandle, 0, 0)
If lngAPIReturn = 0 Then
lngReturnCode = Err.LastDllError
End If
CloseServiceHandle lngServiceHandle
Else
lngReturnCode = 9992 ' Service not opened
End If
CloseServiceHandle lngServiceManagerHandle
Else
lngReturnCode = 9991 ' Service Manager not opened
End If
' Return the appropriate return code
startComputerService = lngReturnCode
End Function
Public Function startComputerServiceWithArgs(strComputerName As String, strServiceName As String, ByRef strArgArray() As String) As Long
' Declare local variable(s)
Dim udtServiceStatus As SERVICE_STATUS
Dim lngServiceManagerHandle As Long
Dim lngNumberOfArguments As Long
Dim strArgumentValue As String
Dim intArgsUBound As Integer
Dim intArrayIndex As Integer
Dim lngServiceHandle As Long
Dim lngParameters() As Long
Dim bytArgument0() As Byte
Dim bytArgument1() As Byte
Dim bytArgument2() As Byte
Dim bytArgument3() As Byte
Dim bytArgument4() As Byte
Dim bytArgument5() As Byte
Dim bytArgument6() As Byte
Dim bytArgument7() As Byte
Dim bytArgument8() As Byte
Dim bytArgument9() As Byte
Dim lngReturnCode As Long
Dim lngAPIReturn As Long
' Initialize variable(s)
lngReturnCode = 0
intArgsUBound = 9
lngNumberOfArguments = 0
' Open the computer Service Manager
lngServiceManagerHandle = OpenSCManager(strComputerName, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS)
' Adjust the intArgsUBound variable if there are less than 10 entries
' This way we can make sure we don't try to pass more than 10 args
If UBound(strArgArray) < intArgsUBound Then
intArgsUBound = UBound(strArgArray)
End If
' Build the argument list array to pass to StartService
For intArrayIndex = 0 To intArgsUBound
ReDim Preserve lngParameters(lngNumberOfArguments) As Long
Select Case intArrayIndex
Case 0
unicodeToByteArray bytArgument0, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument0(0))
Case 1
unicodeToByteArray bytArgument1, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument1(0))
Case 2
unicodeToByteArray bytArgument2, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument2(0))
Case 3
unicodeToByteArray bytArgument3, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument3(0))
Case 4
unicodeToByteArray bytArgument4, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument4(0))
Case 5
unicodeToByteArray bytArgument5, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument5(0))
Case 6
unicodeToByteArray bytArgument6, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument6(0))
Case 7
unicodeToByteArray bytArgument7, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument7(0))
Case 8
unicodeToByteArray bytArgument8, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument8(0))
Case 9
unicodeToByteArray bytArgument9, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument9(0))
End Select
lngNumberOfArguments = lngNumberOfArguments + 1
Next
' If we successfully opened the service manager, call the API
' to start the specified service
If lngServiceManagerHandle <> 0 Then
lngServiceHandle = OpenService(lngServiceManagerHandle, strServiceName, SERVICE_ALL_ACCESS)
If lngServiceHandle <> 0 Then
lngAPIReturn = StartService(lngServiceHandle, lngNumberOfArguments, VarPtr(lngParameters(0)))
If lngAPIReturn = 0 Then
lngReturnCode = Err.LastDllError
End If
CloseServiceHandle lngServiceHandle
Else
lngReturnCode = 9992 ' Service not opened
End If
CloseServiceHandle lngServiceManagerHandle
Else
lngReturnCode = 9991 ' Service Manager not opened
End If
' Return the appropriate return code
startComputerServiceWithArgs = lngReturnCode
End Function
Private Sub unicodeToByteArray(ByRef arrBytes() As Byte, strStringToConvert As String)
' Declare local variable(s)
Dim lngStringLength As Long
Dim lngStringLengthB As Long
Dim lngStringPosition As Long
' Get the length of the string in characters
lngStringLength = Len(strStringToConvert)
lngStringLengthB = LenB(strStringToConvert)
' ReDim the byte array with the string length so
' we duplicate how VB converts BSTRs to ABSTRs for
' API calls
ReDim arrBytes(lngStringLengthB) As Byte
' Populate each position in the array with the
' ASCII value of the appropriate letter
For lngStringPosition = 1 To lngStringLength
arrBytes(lngStringPosition - 1) = Asc(Mid(strStringToConvert, lngStringPosition, 1))
Next
End Sub