Calling the EnumServices Win32 API from your .Net Compact Framework Application

I was helping a customer use PInvoke to call EnumServices today and got stuck a few times so I thought it may be helpful to post the solution in case anyone else runs into this someday.

EnumServices returns a buffer containing a number of structures of type ServiceEnumInfo that describe basic information about the services on a device.

Each ServiceEnumInfo structure contains an embedded character array that represents the service's prefix and a pointer to a string that represents the name of the dll that implements the service.  The dll names corresponding to the structures are laid out in memory just after the structures themselves.  So the contents of the buffer you get back from calling EnumServices looks like this (this example is from a device with 3 services):

Here's some sample code that calls EnumServices and loops the buffer pulling out both the prefix name and the dll name for each service:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace EnumServices
{
    public partial class Form1 : Form
    {
        // Managed definition of the native ServiceEnumInfo structure.  Here's the corresponding native definition:
        //
        // typedef struct_ServiceEnumInfo {
        //    WCHAR szPrefix[6];
        //    WCHAR szDllName;
        //    HANDLE hServiceHandle;
        //    DWORD dwServiceState;
        // } ServiceEnumInfo;
        //
        struct ServiceEnumInfo
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)] public String prefixName;
            public IntPtr pDllName; // this value is a pointer to the dll name - not the dll name itself.
            public IntPtr hServiceHandle;
            public int dwServiceState;
        }

        [DllImport("coredll.dll")]
        private static extern int EnumServices(IntPtr pBuffer, ref int numEntries, ref int cbBuf);

        public Form1()
        {
            InitializeComponent();
        }

        private void btnServices_Click(object sender, EventArgs e)
        {
            int numEntries = 0;
            int cbSize = 0;
            int structSize = Marshal.SizeOf(typeof(ServiceEnumInfo));

            // call once to get required buffer size
            int result = EnumServices(IntPtr.Zero, ref numEntries, ref cbSize);

            // alloc a buffer of the correct size
            IntPtr pBuffer = Marshal.AllocHGlobal(cbSize);

            // call again to get the real stuff
            result = EnumServices(pBuffer, ref numEntries, ref cbSize);

            // loop through the structure pulling out the prefix and the dll name
            for (int i = 0; i < numEntries; i++)
            {
                // move a pointer along to point to the "current" structure each time through the loop
                IntPtr pStruct = new IntPtr(pBuffer.ToInt32()+ (i * structSize));

                // "translate" the pointer into an actual structure
                ServiceEnumInfo sei = (ServiceEnumInfo)Marshal.PtrToStructure(pStruct,

                                                            typeof(ServiceEnumInfo));

                string prefix = sei.prefixName;
                string dllName = Marshal.PtrToStringUni(sei.pDllName);

                // use the prefix and dllName as needed....
                Debug.WriteLine(prefix);
                Debug.WriteLine(dllName);
            }

            // remember to free the buffer that we allocated
            Marshal.FreeHGlobal(pBuffer);

        }
    }
}

Thanks,

Steven

This posting is provided "AS IS" with no warranties, and confers no rights.