What would be the proper use of DsMapSchemaGuidsA in C#.NET 8 for retrieving Active Directory GUIDs to DisplayName

muttBunch 120 Reputation points
2025-05-18T06:29:34.5733333+00:00

I may be digging in the crates here. But quick use case, I am trying to retrieve the DisplayName of Directory Service Access (Event ID: 4662) of the properties. However, they only appear as GUIDs, which I guess would be Microsoft's way of hiding them, instead of being in plain text. I respect that.

I've been researching this and I keep coming across this DsMapSchemaGuids structure (https://learn.microsoft.com/en-us/windows/win32/api/ntdsapi/ns-ntdsapi-ds_schema_guid_mapa)

I have the below code as a test console app, that I am running directly on a domain controller:

using System.Runtime.InteropServices;
namespace DS_SCHEMA_GUID_MAPA_v2
{
    internal class Program
    {
        [DllImport("ntdsapi.dll", CharSet = CharSet.Auto)]
        private static extern int DsBind(string domainController, string dnsDomain, out IntPtr phDs);
        [DllImport("ntdsapi.dll")]
        private static extern int DsUnBind(ref IntPtr phDs);
        [DllImport("ntdsapi.dll")]
        private static extern int DsMapSchemaGuids(IntPtr hDs, uint cGuids, Guid[] rGuids, out IntPtr ppGuidMap);
        [DllImport("ntdsapi.dll")]
        private static extern void DsFreeSchemaGuidMap(IntPtr pGuidMap);
        static void Main(string[] args)
        {
            IntPtr hDs;
            Guid schemaGuid = new Guid("42a75fc6-783f-11d2-9916-0000f87a57d4");
            IntPtr pGuidMap;
            // Bind to the directory service
            if (DsBind("dc00.mydomain.local", "mydomain.local", out hDs) != 0)
            {
                Console.WriteLine("Failed to bind to directory service.");
                return;
            }
            // Map the schema GUID to its display name
            if (DsMapSchemaGuids(hDs, 1, new Guid[] { schemaGuid }, out pGuidMap) == 0)
            {
                Console.WriteLine("Display Name: " + Marshal.PtrToStringAuto(pGuidMap));
                DsFreeSchemaGuidMap(pGuidMap);
            }
            else
            {
                Console.WriteLine("Failed to map schema GUID.");
            }
            // Unbind from the directory service
            DsUnBind(ref hDs);
        }
    }
}

The direct output from the result is:

Display Name: ?????

The bottom half of the event log is as follows:

Operation:
	Operation Type:		Object Access
	Accesses:		Read Property
				
	Access Mask:		0x10
	Properties:		Read Property
	{f30e3bc2-9ff0-11d1-b603-0000f80367c1}
		{e48d0154-bcf8-11d1-8702-00c04fb96050}
			{bf9679e5-0de6-11d0-a285-00aa003049e2}
			{bf96793f-0de6-11d0-a285-00aa003049e2}
		{59ba2f42-79a2-11d0-9020-00c04fc2d3cf}
			{bf967953-0de6-11d0-a285-00aa003049e2}
		{771727b1-31b8-4cdf-ae62-4fe39fadf89e}
			{bf967976-0de6-11d0-a285-00aa003049e2}
			{bf967a76-0de6-11d0-a285-00aa003049e2}
			{f30e3bc0-9ff0-11d1-b603-0000f80367c1}
			{f30e3bc1-9ff0-11d1-b603-0000f80367c1}
			{32ff8ecc-783f-11d2-9916-0000f87a57d4}
			{42a75fc6-783f-11d2-9916-0000f87a57d4}
			{7bd4c7a6-1add-4436-8c04-3999a880154c}

Basically, I am just trying any one of the above GUIDs randomly. But no matter what, same result of "??????" every time. I am not quite sure if the sample excerpt is the problem or not.

It definitely is doing the "DsBind". If I intentionally change the DNS name, it craps out with the "Failed to bind to directory service". So I know at least that part is communicating with the DC.

Any help would be appreciated if anyone has ever used this before.

Thanks

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,539 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. RLWA32 49,301 Reputation points
    2025-05-18T14:20:11.2066667+00:00

    I would use the following for p/invoke interop -

    [DllImport("Ntdsapi32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private static extern uint DsMapSchemaGuidsW(IntPtr h,
                                        uint cGuids,
                                        Guid[] rGuids,
                                        out IntPtr ppGuidMaps);
    
    [DllImport("NtdsApi32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private static extern void DsFreeSchemaGuidMapW(IntPtr p);
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct DS_SCHEMA_GUID_MAPW
    {
        public Guid guid;
        public uint guidType;
        public string name;
    };
    

    Since DSMapSchemaGuidsW returns an array of DS_SCHEMA_GUID_MAPW structures the code needs to iterate through the array. For example, assuming that the function call succeeds and returns a valid IntPtr containing the array in unmanaged memory -

    if (pGuidmaps != IntPtr.Zero)
    {
        var sz = Marshal.SizeOf<DS_SCHEMA_GUID_MAPW>();
        for (int i = 0; i < guids.Length; i++)
        {
            int offset = i * sz;
            IntPtr pentry = pGuidmaps + offset;
            DS_SCHEMA_GUID_MAPW mapped = Marshal.PtrToStructure<DS_SCHEMA_GUID_MAPW>(pentry);
            Console.WriteLine($"Guid: {mapped.guid}, Type: {mapped.guidType}, Name: {mapped.name}");
        }
        DsFreeSchemaGuidMapW(pGuidmaps);
    }
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.