Jedi
asked on
Getting Information about a WIN NT Service from VB ?
Hi !
Has anyone succceeded with retrieving Service Information
using : the API call QueryServiceConfig from VB.
In that case I want to know how !
Like this:
The API requires a pointer to a structure, containing both numeric and string data. This I have made with API text viewer. Nothing strange with this!
But, the result in the stucture after a successfull call is a mess within the strings. The Numerics works fine, but the separate strings are all placed in the first string and this one starts with a lot of strange caracters in the beginning !
Anyone ?
Has anyone succceeded with retrieving Service Information
using : the API call QueryServiceConfig from VB.
In that case I want to know how !
Like this:
The API requires a pointer to a structure, containing both numeric and string data. This I have made with API text viewer. Nothing strange with this!
But, the result in the stucture after a successfull call is a mess within the strings. The Numerics works fine, but the separate strings are all placed in the first string and this one starts with a lot of strange caracters in the beginning !
Anyone ?
Can you post your code?
What service are you trying to get information on?
ASKER
I'm trying to get information about a lot of services, in fact
every registered service
every registered service
ASKER
Ok som e code I'm using:
It's a lot of code to post if it's going to compile, the API calls and error handeling and the declarations takes a lot of space.
I'm mostly interested in code that will retrieve cdata using the:
QueryServiceConfig call:
If there is some need for initializing my VB strings before the call or something.
Some code :
'Declarations:
'-------------
Public Declare Function QueryServiceConfig Lib "advapi32.dll" Alias "QueryServiceConfigA" (ByVal hService As Long, lpServiceConfig As QUERY_SERVICE_CONFIG, ByVal cbBufSize As Long, pcbBytesNeeded As Long) As Long
Type QUERY_SERVICE_CONFIG
dwServiceType As Long
dwStartType As Long
dwErrorControl As Long
lpBinaryPathName As String '* 255
lpLoadOrderGroup As String '* 255
dwTagId As Long
lpDependencies As String '* 255
lpServiceStartName As String '* 255
lpDisplayName As String '* 255
End Type
'------------------------- --
'Running
'------------------------- --
Dim lresult As Long
Dim pcbBytesNeeded As Long
Dim lpServiceConfig As QUERY_SERVICE_CONFIG
Dim cbBufSize As Long
lpServiceConfig.dwServiceT ype = 0
lpServiceConfig.dwStartTyp e = 0
lpServiceConfig.dwErrorCon trol=0
lpServiceConfig.lpBinaryPa thName = String(255, vbNullChar)lpServiceConfig .lpLoadOrd erGroup = String(255, vbNullChar)lpServiceConfig .dwTagId = 0
lpServiceConfig.lpDependen cies = String(255, vbNullChar)lpServiceConfig .lpService StartName = String(255, vbNullChar) lpServiceConfig.lpDisplayN ame = String(255, vbNullChar)
cbBufSize = LenB(lpServiceConfig)
'Stop Service it first
' SvcsStop (lpServiceName)
'Open the DB
' hSCManager = SvcsOpenDB("")
' If hSCManager = Null Then
' SvcsDisable = False
' Exit Function
' Exit Sub
' End If
'Open the service in question
' hService = SvcsOpen(hSCManager, lpServiceName)
'Get the right values
lresult = QueryServiceConfig(hServic e, lpServiceConfig, cbBufSize, pcbBytesNeeded)
'****The strings within the lpServiceConfig structure are a mess.
Hope it gives some clues
It's a lot of code to post if it's going to compile, the API calls and error handeling and the declarations takes a lot of space.
I'm mostly interested in code that will retrieve cdata using the:
QueryServiceConfig call:
If there is some need for initializing my VB strings before the call or something.
Some code :
'Declarations:
'-------------
Public Declare Function QueryServiceConfig Lib "advapi32.dll" Alias "QueryServiceConfigA" (ByVal hService As Long, lpServiceConfig As QUERY_SERVICE_CONFIG, ByVal cbBufSize As Long, pcbBytesNeeded As Long) As Long
Type QUERY_SERVICE_CONFIG
dwServiceType As Long
dwStartType As Long
dwErrorControl As Long
lpBinaryPathName As String '* 255
lpLoadOrderGroup As String '* 255
dwTagId As Long
lpDependencies As String '* 255
lpServiceStartName As String '* 255
lpDisplayName As String '* 255
End Type
'-------------------------
'Running
'-------------------------
Dim lresult As Long
Dim pcbBytesNeeded As Long
Dim lpServiceConfig As QUERY_SERVICE_CONFIG
Dim cbBufSize As Long
lpServiceConfig.dwServiceT
lpServiceConfig.dwStartTyp
lpServiceConfig.dwErrorCon
lpServiceConfig.lpBinaryPa
lpServiceConfig.lpDependen
cbBufSize = LenB(lpServiceConfig)
'Stop Service it first
' SvcsStop (lpServiceName)
'Open the DB
' hSCManager = SvcsOpenDB("")
' If hSCManager = Null Then
' SvcsDisable = False
' Exit Function
' Exit Sub
' End If
'Open the service in question
' hService = SvcsOpen(hSCManager, lpServiceName)
'Get the right values
lresult = QueryServiceConfig(hServic
'****The strings within the lpServiceConfig structure are a mess.
Hope it gives some clues
The problem arises when you try to access null-terminated strings through pointers embedded in a structure. In order to do this properly, you need to retrieve the string pointers as integer values (define them as "Long" in your VB type, not "String") and then convert them into VB strings.
The following code is a sample of how to do this. The C++ code is for a DLL which will convert a pointer stored as an integer value into a VB string, and the VB application is a simple demonstration of how to use the library.
========================== ========
GetBSTR.cpp
========================== ========
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#pragma warning( disable : 4201 )
#include <windows.h>
#include <ole2.h>
char const * const gc_p = "TestString";
/*
* test function for returning a pointer to a string as a long
* integer value...
*/
extern "C" long __declspec( dllexport ) __stdcall GetString( )
{
return long( gc_p );
}
/*
* take the given string pointer and convert it to a Visual Basic
* string...
*/
extern "C" BSTR __declspec( dllexport ) __stdcall GetStringFromPointer( char const * const p )
{
return ::SysAllocStringByteLen( p
, ::strlen( p )
);
}
========================== ========
VB Sample Program
========================== ========
Option Explicit
Declare Function GetString Lib "GetBSTR.dll" Alias "_GetString@0" () As Long
Declare Function GetStringFromPointer Lib "GetBSTR.dll" Alias "_GetStringFromPointer@4" (ByVal l As Long) As String
Sub main()
Dim l As Long
l = GetString()
Dim s As String
s = GetStringFromPointer(l)
End Sub
The following code is a sample of how to do this. The C++ code is for a DLL which will convert a pointer stored as an integer value into a VB string, and the VB application is a simple demonstration of how to use the library.
==========================
GetBSTR.cpp
==========================
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#pragma warning( disable : 4201 )
#include <windows.h>
#include <ole2.h>
char const * const gc_p = "TestString";
/*
* test function for returning a pointer to a string as a long
* integer value...
*/
extern "C" long __declspec( dllexport ) __stdcall GetString( )
{
return long( gc_p );
}
/*
* take the given string pointer and convert it to a Visual Basic
* string...
*/
extern "C" BSTR __declspec( dllexport ) __stdcall GetStringFromPointer( char const * const p )
{
return ::SysAllocStringByteLen( p
, ::strlen( p )
);
}
==========================
VB Sample Program
==========================
Option Explicit
Declare Function GetString Lib "GetBSTR.dll" Alias "_GetString@0" () As Long
Declare Function GetStringFromPointer Lib "GetBSTR.dll" Alias "_GetStringFromPointer@4" (ByVal l As Long) As String
Sub main()
Dim l As Long
l = GetString()
Dim s As String
s = GetStringFromPointer(l)
End Sub
Jedi,
The main problem here is the size of QUERY_SERVICE_CONFIG is not enough for the API function QueryServiceConfig to retrieve the service information, so you must allocate enough space for QUERY_SERVICE_CONFIG structure before calling QueryServiceConfig.
The following steps must be done:
1- Change the variables with string data type in QUERY_SERVICE_CONFIG type to Long data type.
2- Declare a variable as an array with 10 dimensions of this type and pass it to QueryServiceConfig function with the size (10 * length of the first dimension of variable).
3- To get the string use lstrcpy and lstrlen API functions (you don't have to write a C code to do that).
The following example illustrates how to get the information for Schadule service:
Example
=======
Private Type QUERY_SERVICE_CONFIG
dwServiceType As Long
dwStartType As Long
dwErrorControl As Long
lpBinaryPathName As Long
lpLoadOrderGroup As Long
dwTagId As Long
lpDependencies As Long
lpServiceStartName As Long
lpDisplayName As Long
End Type
Private Const GENERIC_READ = &H80000000
Private Const SERVICE_QUERY_CONFIG = &H1
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 hSCManager As Long, ByVal lpServiceName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject As Long) As Long
Private Declare Function QueryServiceConfig Lib "advapi32.dll" Alias "QueryServiceConfigA" (ByVal hService As Long, lpServiceConfig As QUERY_SERVICE_CONFIG, ByVal cbBufSize As Long, pcbBytesNeeded As Long) As Long
Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, ByVal lpString2 As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long
Private Sub Command1_Click()
Dim hSCM As Long
Dim hService As Long
Dim sMachineName As String
Dim sDatabaseName As String
Dim lResult As Long
Dim pcbBytesNeeded As Long
Dim lpServiceConfig() As QUERY_SERVICE_CONFIG
Dim sBinaryPathName As String
Dim sLoadOrderGroup As String
Dim sDependencies As String
Dim sServiceStartName As String
Dim sDisplayName As String
hSCM = OpenSCManager(sMachineName , sDatabaseName, GENERIC_READ)
hService = OpenService(hSCM, "Schedule", SERVICE_QUERY_CONFIG)
ReDim lpServiceConfig(100) As QUERY_SERVICE_CONFIG
lResult = QueryServiceConfig(hServic e, lpServiceConfig(0), 100 * Len(lpServiceConfig(0)), pcbBytesNeeded)
sBinaryPathName = Space(255)
lResult = lstrcpy(sBinaryPathName, lpServiceConfig(0).lpBinar yPathName)
sBinaryPathName = Mid(sBinaryPathName, 1, lstrlen(sBinaryPathName))
sLoadOrderGroup = Space(255)
lResult = lstrcpy(sLoadOrderGroup, lpServiceConfig(0).lpLoadO rderGroup)
sLoadOrderGroup = Mid(sLoadOrderGroup, 1, lstrlen(sLoadOrderGroup))
sDependencies = Space(255)
lResult = lstrcpy(sDependencies, lpServiceConfig(0).lpDepen dencies)
sDependencies = Mid(sDependencies, 1, lstrlen(sDependencies))
sServiceStartName = Space(255)
lResult = lstrcpy(sServiceStartName, lpServiceConfig(0).lpServi ceStartNam e)
sServiceStartName = Mid(sServiceStartName, 1, lstrlen(sServiceStartName) )
sDisplayName = Space(255)
lResult = lstrcpy(sDisplayName, lpServiceConfig(0).lpDispl ayName)
sDisplayName = Mid(sDisplayName, 1, lstrlen(sDisplayName))
MsgBox "Service Type: " & lpServiceConfig(0).dwServi ceType & vbCrLf & _
"Start Type: " & lpServiceConfig(0).dwStart Type & vbCrLf & _
"Error Control: " & lpServiceConfig(0).dwError Control & vbCrLf & _
"Binary Path Name: " & sBinaryPathName & vbCrLf & _
"Load Order Group: " & sLoadOrderGroup & vbCrLf & _
"Tag ID: " & lpServiceConfig(0).dwTagId & vbCrLf & _
"Dependencies: " & sDependencies & vbCrLf & _
"Service Start Name: " & sServiceStartName & vbCrLf & _
"Display Name: " & sDisplayName
Call CloseServiceHandle(hServic e)
Call CloseServiceHandle(hSCM)
End Sub
Best regards
Bin Huwairib
The main problem here is the size of QUERY_SERVICE_CONFIG is not enough for the API function QueryServiceConfig to retrieve the service information, so you must allocate enough space for QUERY_SERVICE_CONFIG structure before calling QueryServiceConfig.
The following steps must be done:
1- Change the variables with string data type in QUERY_SERVICE_CONFIG type to Long data type.
2- Declare a variable as an array with 10 dimensions of this type and pass it to QueryServiceConfig function with the size (10 * length of the first dimension of variable).
3- To get the string use lstrcpy and lstrlen API functions (you don't have to write a C code to do that).
The following example illustrates how to get the information for Schadule service:
Example
=======
Private Type QUERY_SERVICE_CONFIG
dwServiceType As Long
dwStartType As Long
dwErrorControl As Long
lpBinaryPathName As Long
lpLoadOrderGroup As Long
dwTagId As Long
lpDependencies As Long
lpServiceStartName As Long
lpDisplayName As Long
End Type
Private Const GENERIC_READ = &H80000000
Private Const SERVICE_QUERY_CONFIG = &H1
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 hSCManager As Long, ByVal lpServiceName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject As Long) As Long
Private Declare Function QueryServiceConfig Lib "advapi32.dll" Alias "QueryServiceConfigA" (ByVal hService As Long, lpServiceConfig As QUERY_SERVICE_CONFIG, ByVal cbBufSize As Long, pcbBytesNeeded As Long) As Long
Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, ByVal lpString2 As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long
Private Sub Command1_Click()
Dim hSCM As Long
Dim hService As Long
Dim sMachineName As String
Dim sDatabaseName As String
Dim lResult As Long
Dim pcbBytesNeeded As Long
Dim lpServiceConfig() As QUERY_SERVICE_CONFIG
Dim sBinaryPathName As String
Dim sLoadOrderGroup As String
Dim sDependencies As String
Dim sServiceStartName As String
Dim sDisplayName As String
hSCM = OpenSCManager(sMachineName
hService = OpenService(hSCM, "Schedule", SERVICE_QUERY_CONFIG)
ReDim lpServiceConfig(100) As QUERY_SERVICE_CONFIG
lResult = QueryServiceConfig(hServic
sBinaryPathName = Space(255)
lResult = lstrcpy(sBinaryPathName, lpServiceConfig(0).lpBinar
sBinaryPathName = Mid(sBinaryPathName, 1, lstrlen(sBinaryPathName))
sLoadOrderGroup = Space(255)
lResult = lstrcpy(sLoadOrderGroup, lpServiceConfig(0).lpLoadO
sLoadOrderGroup = Mid(sLoadOrderGroup, 1, lstrlen(sLoadOrderGroup))
sDependencies = Space(255)
lResult = lstrcpy(sDependencies, lpServiceConfig(0).lpDepen
sDependencies = Mid(sDependencies, 1, lstrlen(sDependencies))
sServiceStartName = Space(255)
lResult = lstrcpy(sServiceStartName,
sServiceStartName = Mid(sServiceStartName, 1, lstrlen(sServiceStartName)
sDisplayName = Space(255)
lResult = lstrcpy(sDisplayName, lpServiceConfig(0).lpDispl
sDisplayName = Mid(sDisplayName, 1, lstrlen(sDisplayName))
MsgBox "Service Type: " & lpServiceConfig(0).dwServi
"Start Type: " & lpServiceConfig(0).dwStart
"Error Control: " & lpServiceConfig(0).dwError
"Binary Path Name: " & sBinaryPathName & vbCrLf & _
"Load Order Group: " & sLoadOrderGroup & vbCrLf & _
"Tag ID: " & lpServiceConfig(0).dwTagId
"Dependencies: " & sDependencies & vbCrLf & _
"Service Start Name: " & sServiceStartName & vbCrLf & _
"Display Name: " & sDisplayName
Call CloseServiceHandle(hServic
Call CloseServiceHandle(hSCM)
End Sub
Best regards
Bin Huwairib
ASKER
Thanks Mwalsh, but the commented answer from Bin is even better.
Exelent answer from Bin Huwairib ,if you post a answer I'll give you the points !
//Jedi
Exelent answer from Bin Huwairib ,if you post a answer I'll give you the points !
//Jedi
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Excellent , That was the kind of answerI was looking for,
An explanation and a solution.
Thanks again
//Jedi
An explanation and a solution.
Thanks again
//Jedi