Welcome to Technolog Sign in | Join | Help

November 2005 - Posts

Normally if you have defined a struct in C# which is for a Win32 call, you might tend to copy-paste it from C++, and then modify LPWSTR to string and add attributes (etc),
like this:

struct myStruct
{
   [MarshalAs(UnmanagedType.LPWstr)}
   public string blah; //ok
   [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.LPWStr)]   
    public string[] stringarray;
}

Let's concentrate on the array definition. The original array was defined like LPWSTR* stringarray;  this is an array of pointers to zero-terminated strings.

Now you thought, that a few attributes, would be sufficient to support your interop?

[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)]   
public string[] stringarray;

but the .NET framework, won't support you here. It is not able to create an unmanaged array of 'some type' on structs (this however, is working for parameter definitions) and to pass it to your unmanaged API.

And if you wonder what UnmanagedType.ByValArray, UnmanagedType.LPWStr  really does; it creates a pointer to a contigous block of zero terminated strings. It would not create an array of stringpointers. So now you know, why interop is an art, not a science :)

At last, I wrote an easy wrapper. It returns an IntPtr which is a just pointer to our first element to an array of string pointers :)

You should assign this class as a member variable on your own class. If you use this class inside a member function, your unmanaged memory might be cleaned when the function goes out of scope.
PS: Please send back the improved code, if you decide to use it (at your own risk of course). It might be improved, to pack managed structures as well, using C# generics.

Sample:

class myClass()

{   private packLPArray packit;      

      void myMemberfunction()
      {   // ... do your dangerous, endeavourish :) interop thing here
            packit = new packLPArray(new string[] {"element1", "element2", "blah"});
            mystruct.stringarray = packit.arrayPtr;
      }

}

 

///<summary> packs an array of strings (type = string[]) to unmanaged memory
/// also works for 64-bit environments
///</summary>
public sealed class  packLPArray
{
    private IntPtr taskAlloc;

    private readonly int _length;

    private IntPtr[] _strings;

    public packLPArray(string[] theArray)

    {   int sizeIntPtr = IntPtr.Size;

        int neededSize = 0;

        if (theArray != null)

        {

            this._length = theArray.Length;

            this._strings = new IntPtr[this._length];

           // System.Diagnostics.Debugger.Break();

            neededSize = this._length * sizeIntPtr;

            this.taskAlloc = Marshal.AllocCoTaskMem(neededSize);           

            for (int cx = this._length - 1; cx >= 0; cx--)
            {   this._strings[cx] = Marshal.StringToCoTaskMemUni(theArray[cx]);
                Marshal.WriteIntPtr(this.taskAlloc, cx * sizeIntPtr, this._strings[cx]);

            }

        }

    }

    /// <summary>

    /// retrieves array length

    /// </summary>

    public int Length

    {

        get { return _length; }

    }

    public IntPtr arrayPtr

    {

        get { return this.taskAlloc; }

 

    }

    ~packLPArray() // clean up the rub

    {

        if (taskAlloc != IntPtr.Zero)

        {

            Marshal.FreeCoTaskMem(this.taskAlloc);

            int cx = this._length;

            while(cx-- != 0)

                Marshal.FreeCoTaskMem(this._strings[cx]);

        }

    } 

}


I just had some COM interop work, and decided to use STGMEDIUM and
FORMATETC.
These structs, are necessary for many CLIPboard operations. But what draws my attention?
the MSDN says this:

typedef struct tagSTGMEDIUM

{

    DWORD tymed;

    [switch_type(DWORD), switch_is((DWORD) tymed)]

    union {

        [case(TYMED_GDI)]      HBITMAP        hBitmap;

        [case(TYMED_MFPICT)]   HMETAFILEPICT  hMetaFilePict;

        [case(TYMED_ENHMF)]    HENHMETAFILE   hEnhMetaFile;

        [case(TYMED_HGLOBAL)]  HGLOBAL        hGlobal;

        [case(TYMED_FILE)]     LPWSTR         lpszFileName;

        [case(TYMED_ISTREAM)]  IStream        *pstm;

        [case(TYMED_ISTORAGE)] IStorage       *pstg;

        [default] ;

    };

    [unique] IUnknown *pUnkForRelease;

}STGMEDIUM;

typedef STGMEDIUM *LPSTGMEDIUM;
As we all know, a union, in C++ is just a keyword which makes it's member variables start all at the same memory location. In a .NET struct, we can have the same result by using the following atribute: [StructLayout(LayoutKind.Explicit)]
For each structure member,. you now can specify an offset.
Our dear developers, decided for us, that we should not have that luxury of difficult choices having a struct with explicit layout, but let's not complain about this for now...
There is something -more- seriously wrong.

Have a look at the built in interop defintion (in the .NET 2.0 framework!)
(namespace System.Runtime.InteropServices.ComTypes)
public struct STGMEDIUM
{
public object pUnkForRelease;
public TYMED tymed;
public IntPtr unionmember;
}
 
After some crashes, I found that the definition, contains the correct members  at the wrong location!

As you can see, this defintion, is wrong and should be
public struct STGMEDIUM
{

public TYMED tymed;
public IntPtr unionmember;
public object pUnkForRelease;
}
 
A similar problem exists with FORMATETC. I'll not bother you with the details, but let's just post the correct defition. Next time, if you need these structs, don't use the one from the .NET 2.0 interop layer...

[StructLayout(LayoutKind.Sequential)]
public struct FORMATETC
{
   public short cfFormat;
   public IntPtr ptd;
   [
MarshalAs(UnmanagedType.U4)]
   public DVASPECT dwAspect; 
   public int lindex;
   [
MarshalAs(UnmanagedType.U4)]
   public TYMED tymed;
};

Have you ever tried to pinvoke a function, that returns a pointer to an array of structs? There's you can easily marshale them using safe C# code but if the code was not written already and you can't simply copy-paste that :), I promise to you, that you'll have to do a lot more work to get it done. unsafe code might be a quicker solution.

You could use unsafe code in the following situations:

a) You are not writing code or utilities, that might or will run in restricted environments, such as with an ISP environment. And ISPs (eg.) should not allow .NET to run third-party code that needs full-trust policies.
b) You have knowledge about pointers using C++
c) Your program will not be completely 'type safe', this is what some evangelists say, however, I don't see a flawless windows or webfarm if only everything were typesafe :).

Therefore, I propagate, that the biggest part, if not all, of your code, is type-safe. If some tiny utilities, are tested well and in favor of speed and power and possibilities, need to be type-unsafe, just go on.

If conditions have been considered, make your decision. Eventually stop reading now and wait for other postings :).

Such a candidate to be marked as unsafe, would be DsCrackNames, for the COM/automation world, it would be clever to instantiate the NameTranslate class which has an IDispatch interface (good for scripting as well).
Since ít's my hobby, to avoid easy solutions :) I wrote a unique (for the moment, I did not find another on the internet) wrapper for DsCrackNames which run nearly identical its earlier automation friend.

Let's have a look at the MSDN definition of this function.:
DWORD DsCrackNames(
  HANDLE hDS
,
  DS_NAME_FLAGS flags
,
  DS_NAME_FORMAT formatOffered
,
  DS_NAME_FORMAT formatDesired
,
  DWORD cNames
,
  LPCTSTR* rpNames
,
  PDS_NAME_RESULT* ppResult

);

Wow! this promises a lot of troubles, since PDS_NAME_RESULT is a pointer to a structure which contains an array of pointers to another sequential struct.

MSDN definition:
typedef struct
{
  DWORD cItems;
  PDS_NAME_RESULT_ITEM rItems;
} DS_NAME_RESULT,
*PDS_NAME_RESULT;

And here's the struct  rItems refers to...

typedef struct {
DWORD status;
LPTSTR pDomain;
LPTSTR pName;

} DS_NAME_RESULT_ITEM,
*PDS_NAME_RESULT_ITEM;


I do challence you, to write a 'safe' equivalent to it, I tried it, and believe me, the .NET framework does its stinking best to tell you that your attributes are not valid.

Of course, we understand that it simply is not possible  unless you jump to a MC++ solution or you use the definition from www.pinvoke.net which is requires a lot tricky code (no offense to anybody)... .

You cannot use attributes on the structure, and pass the stuff in one single call to the platform invoke and have the net framework do the actual marshaling for you.  You should create some looping work using Marshal.ReadInt32 (..) if you go for the 'safe' code solution .

I want to stress the point that safe platform invokes are not necessarily safer! They can leak memory as hell sorry, as well.

I've tried to use the [MarshalAs] attribute, but a having interop fill an array of IntPtrs inside a struct is not supported by the IL environment. Yes, you can try something like

struct DS_NAME_RESULT
{
        int cItems;
        IntPtr firstItem; //will work only if you crack one item or if you use Marshal.ReadInt32 etc code seen at pinvoke.net
}

Or try this... But now you have a managed array of IntPtr and that won't work as well.

struct DS_NAME_RESULT
{
        int cItems;
        IntPtr[] pDS_NAME_RESULT_ITEM; //won't be marshaled since the framework cannot create and marshale this array on the fly
}

and DS_NAME_RESULT ITEM would be like:

struct DS_NAME_RESULT_ITEM
{
public int status;
  [MarshalAs(UnmanagedType.LPWstr)]
public string pDomain;
  [MarshalAs(UnmanagedType.LPWstr)]
public string pName;
} ;

Let's see how easy you could write the solution set the 'unsafe'  attribute in spite of how disastrous this might sound.

Unsafe only allows for easy interop with unmaged IL code, this is a facto default with MC++, where you easily can use all those .h files, (windows.h, winbase.h etc) without having to retype all your DllImport statements or without having to copy them from http://www.pinvoke.net which often has untested declares. I could have use MC++ as well, but if you want to expose the assembly for reusage, you'll start to redefine all those Win32 enums and constants anyway. We have to learn to live with the border between managed and unmanged code.

Our 'unsafe' CSharp code, looks very much like a C++ implementation. In addition, it offers some improvements over the IADsNameTranslate interface (that you derive from NameTranslate, with Guid("b1b272a3-3625-11d1-a3a4-00c04fb950dc").

Some remarks about my style of programming: I really don't like ansi or Win9x compatible code. Screw it! :), as you can see, my declares favor Windows 2000 and higher. Also take in account, that this is not full proof & tested code.

Have fun using this code.

/* Copyright, Nierop Webconsultancy 2005 www.nieropwebconsult.nl

 * Use of this code, in your projects, is for your own risk.

 * If you modify the code, you send improvements back

 * If you copy the code, you won't remove the credits for the code

 */

using System;

using System.DirectoryServices;

using System.Runtime.InteropServices;

using System.DirectoryServices.ActiveDirectory;

using System.Data;

using System.ComponentModel;

 

 

namespace NameTranslate

{

    public enum ADS_NAME_INITTYPE_ENUM

    {

        /// <summary>

        /// Initializes a NameTranslate object by setting the domain that the object binds to.

        /// </summary>

        ADS_NAME_INITTYPE_DOMAIN = 1,

        /// <summary>

        /// Initializes a NameTranslate object by setting the server that the object binds to.

        /// </summary>

        ADS_NAME_INITTYPE_SERVER = 2,

        /// <summary>

        /// Initializes a NameTranslate object by locating the global catalog that the object binds to

        /// </summary>

        ADS_NAME_INITTYPE_GC = 3

    } ;

   

    public enum DS_NAME_FORMAT

    {

        DS_UNKNOWN_NAME = 0,

        DS_FQDN_1779_NAME = 1,

        DS_NT4_ACCOUNT_NAME = 2,

        DS_DISPLAY_NAME = 3,

        DS_UNIQUE_ID_NAME = 6,

        DS_CANONICAL_NAME = 7,

        DS_USER_PRINCIPAL_NAME = 8,

        DS_CANONICAL_NAME_EX = 9,

        DS_SERVICE_PRINCIPAL_NAME = 10,

        DS_SID_OR_SID_HISTORY_NAME = 11,

        DS_DNS_DOMAIN_NAME = 12

    } ;

 

    enum DS_NAME_FLAGS

    {

        /// <summary>

        /// Indicates that there are no associated flags

        /// </summary>

        DS_NAME_NO_FLAGS = 0x0,

        /// <summary>

        /// Performs a syntactical mapping at the client without transferring over the network.

        /// The only syntactic mapping supported is from DS_FQDN_1779_NAME to DS_CANONICAL_NAME or DS_CANONICAL_NAME_EX.

        /// DsCrackNames returns the DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING flag if a syntactical mapping is not possible.

        /// </summary>

        DS_NAME_FLAG_SYNTACTICAL = 0x1,

        /// <summary>

        /// Forces a trip to the domain controller for evaluation, even if the syntax could be cracked locally

        /// </summary>

        DS_NAME_FLAG_EVAL_AT_DC = 0x2,

        /// <summary>

        /// The call fails if the domain controller is not a global catalog server.

        /// </summary>

        DS_NAME_FLAG_GCVERIFY = 0x4,

        /// <summary>

        /// Enables cross forest trust referral.

        /// </summary>

        DS_NAME_FLAG_TRUST_REFERRAL = 0x8

    } ;

    public enum DS_NAME_ERROR

    {

        /// <summary>

        /// The conversion was successful.

        /// </summary>

        DS_NAME_NO_ERROR = 0,

        /// <summary>

        /// A generic processing error occurred.

        /// </summary>

        DS_NAME_ERROR_RESOLVING = 1,

        /// <summary>

        /// The name cannot be found or the caller does not have permission to access the name.

        /// </summary>

        DS_NAME_ERROR_NOT_FOUND = 2,

        /// <summary>

        /// The input name is mapped to more than one output name or the desired format did not have a single,

        /// unique value for the object found.

        /// </summary>

        DS_NAME_ERROR_NOT_UNIQUE = 3,

        /// <summary>

        /// The input name was found, but the associated output format cannot be found.

        /// This can occur if the object does not have all the required attributes.

        /// </summary>

        DS_NAME_ERROR_NO_MAPPING = 4,

        /// <summary>

        /// Unable to resolve entire name, but was able to determine in which domain object resides. The caller is expected to retry the call at a domain controller for the specified domain. The entire name cannot be resolved, but the domain that the object resides in could be determined. The pDomain member of the DS_NAME_RESULT_ITEM contains valid data when this error is specified.

        /// </summary>

        DS_NAME_ERROR_DOMAIN_ONLY = 5,

        /// <summary>

        /// A syntactical mapping cannot be performed on the client without transmitting over the network

        /// </summary>

        DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6,

        /// <summary>

        /// The name is from an external trusted forest

        /// </summary>

        DS_NAME_ERROR_TRUST_REFERRALDS = 7

    };

 

    public class NWCNameTranslate

    {

        [DllImport("NtDsapi.dll", EntryPoint = "DsBindW", CharSet = CharSet.Unicode, SetLastError = false)]

        static extern int DsBind(

            [MarshalAs(UnmanagedType.LPWStr)]string DomainControllerName,

            [MarshalAs(UnmanagedType.LPWStr)]string DnsDomainName,

            out IntPtr phDS);

 

        [DllImport("NtDsapi.dll", EntryPoint = "DsBindWithCredW", CharSet = CharSet.Unicode, SetLastError = false)]

        static extern int DsBindWithCred(

            [MarshalAs(UnmanagedType.LPWStr)]string DomainControllerName,

            [MarshalAs(UnmanagedType.LPWStr)]string DnsDomainName,

            IntPtr authHandle,

            out IntPtr phds);

 

 

        unsafe struct DS_NAME_RESULT_ITEM

        {

            public DS_NAME_ERROR status;           

            public char* pDomain;           

            public char* pName;

        } ;

        public struct NameResultItem

        {

            public DS_NAME_ERROR status;

            public string domain;

            public string name;

        }

       

        unsafe struct DS_NAME_RESULT

        {

            public int cItems;

            public DS_NAME_RESULT_ITEM* pDS_NAME_RESULT_ITEM;

        } ;

 

       

    

 

        [DllImport("Ntdsapi.dll", EntryPoint = "DsMakePasswordCredentialsW", ExactSpelling=true, CharSet = CharSet.Unicode, SetLastError = false)]

        static extern int DsMakePasswordCredentials(

            [MarshalAs(UnmanagedType.LPWStr)]string User,

           [MarshalAs(UnmanagedType.LPWStr)]string Domain,

           [MarshalAs(UnmanagedType.LPWStr)]string Password,

            out IntPtr pAuthIdentity

            );

 

        [DllImport("Ntdsapi.dll", ExactSpelling=true)]

        static extern void DsFreePasswordCredentials(

              IntPtr AuthIdentity

            );

        [DllImport("Ntdsapi.dll", EntryPoint = "DsUnBindW", SetLastError = false, ExactSpelling=true)]

        static extern int DsUnBind(IntPtr phDS);

 

 

        [DllImport("NtDsapi.dll", EntryPoint = "DsFreeNameResultW", CharSet = CharSet.Unicode, SetLastError = false, ExactSpelling=true)]

        unsafe private static extern int

            DsFreeNameResult(DS_NAME_RESULT* pResult);

 

        [DllImport("Ntdsapi.dll", EntryPoint = "DsCrackNamesW", SetLastError = false, CharSet = CharSet.Unicode,  ExactSpelling=true)]

        unsafe static extern int DsCrackNames(IntPtr hDS,

                DS_NAME_FLAGS flags,

                DS_NAME_FORMAT formatOffered,

                DS_NAME_FORMAT formatDesired,

                int cNames,

                [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 4)]

                string[] rpNames,           

                DS_NAME_RESULT** ds_name_result

                );

        [DllImport("Activeds.dll", EntryPoint = "ADsBuildVarArrayStr", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Unicode)]

        static extern int ADsBuildVarArrayStr(

            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)]          

            string[] lppPathNames,

            int arrayLen,

            [MarshalAs(UnmanagedType.Struct)]

            out object varArray);

 

        private IntPtr hDs;

        private IntPtr ident;

        private string[] crackme;

        private NameResultItem[] cracked;

        private DS_NAME_FORMAT inType;

        private DS_NAME_FORMAT outType;

        public NWCNameTranslate()

        {

           

        }

        ~NWCNameTranslate()

        {

            DsUnBind(this.hDs);
            
if (this.ident != IntPtr.Zero)
               DsFreePasswordCredentials(
this.ident);

        }

        public void ChaseReferral(int lnChaseReferral)

        {

            //this.nt.ChaseReferral(lnChaseReferral);

            //if (res != 0) throw new COMException("NameTranslate Failed", res);

        }

        public NameResultItem Get(DS_NAME_FORMAT lnFormatType)

        {           

            this.outType = lnFormatType;

            this.Crack();

            if (this.cracked.Length == 1)

                return this.cracked[0];

            else

                return new NameResultItem();

        }

        unsafe private void Crack()

        {

            DS_NAME_RESULT * ppresult=null;

            int lenArray = this.crackme.Length;

           

            int result = DsCrackNames(this.hDs,

                    DS_NAME_FLAGS.DS_NAME_NO_FLAGS,

                    this.inType,

                    this.outType,

                    lenArray,

                    this.crackme,

                    &ppresult);

 

            if (result != 0)

                throw new Win32Exception(result);

 

            lenArray = ppresult->cItems;

            this.cracked = new NameResultItem[lenArray];

           

            // Well, is this code giving you the creeps being 'unsafe', I think not! :)

 

            for (int cx = lenArray - 1; cx >= 0; cx--)           

            {

                DS_NAME_RESULT_ITEM nameRes = ppresult->pDS_NAME_RESULT_ITEM[cx];

                this.cracked[cx].status = nameRes.status;

                this.cracked[cx].domain = new string(nameRes.pDomain);

                this.cracked[cx].name = new string(nameRes.pName);

            }

            DsFreeNameResult(ppresult);

 

        }

        public NameResultItem[] GetEx(DS_NAME_FORMAT lnFormatType)

        {

 

            this.outType = lnFormatType;

            this.Crack();

 

            return this.cracked;

        }

        public void Init(ADS_NAME_INITTYPE_ENUM lnSetType)

        {

            int result = 0;

 

            DsGetDcNameWrap.DOMAIN_CONTROLLER_INFO domInfo = DsGetDcNameWrap.GetDomainInfo();

           

            if (this.hDs != IntPtr.Zero)

            {   

                result = DsUnBind(this.hDs);

                if (this.ident != IntPtr.Zero) 
                  DsFreePasswordCredentials(this.ident);

            }

            if (lnSetType == ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_DOMAIN)

                result = DsBind(null, domInfo.DomainName, out this.hDs);

            else if (lnSetType == ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_SERVER)

                result = DsBind(domInfo.DomainControllerName, null, out this.hDs);

 

            if (result != 0)

                throw new Win32Exception(result);

 

        }

 

        public void Init(ADS_NAME_INITTYPE_ENUM lnSetType, string bstrADsPath)

        {

            int result =0;

 

            if (this.hDs != IntPtr.Zero)
            {
               DsUnBind(
this.hDs);
               
if (this.ident != IntPtr.Zero) 
                  DsFreePasswordCredentials(this.ident);
            }

            if (lnSetType == ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_DOMAIN)

                result = DsBind(null, bstrADsPath, out this.hDs);

            else if (lnSetType == ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_SERVER)

                result = DsBind(bstrADsPath, null, out this.hDs);

          

            if (result != 0)

                throw new Win32Exception(result);

          

        }

 

        public void InitEx(ADS_NAME_INITTYPE_ENUM lnSetType, string bstrADsPath, string bstrUserID, string bstrDomain, string bstrPassword)

        {

 

         if (this.hDs != IntPtr.Zero)
         {
            DsUnBind(
this.hDs);
            
if (this.ident != IntPtr.Zero) 
               
//fix leak thanks to Jimmy!
               
DsFreePasswordCredentials(this.ident);
         }

           int result = DsMakePasswordCredentials(bstrUserID, bstrDomain, bstrPassword, out this.ident);

            if (result != 0)               

                result = DsBindWithCred(null, bstrADsPath, this.ident, out this.hDs);

 

            if (result != 0)

                throw new Win32Exception(result);

                

        }

 

        public void Set(DS_NAME_FORMAT lnSetType, string bstrADsPath)

        {

            this.crackme = new string[] { bstrADsPath };

            this.inType = lnSetType;

        }

 

        public void SetEx(DS_NAME_FORMAT lnFormatType, string[] pVar)

        {

           

            this.inType = lnFormatType;

            this.crackme = pVar;                   

 

        }

    }

 

 

    struct DsGetDcNameWrap

    {

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

        internal struct DOMAIN_CONTROLLER_INFO

        {

            [MarshalAs(UnmanagedType.LPWStr)]

            public string DomainControllerName;

            [MarshalAs(UnmanagedType.LPTStr)]

            public string DomainControllerAddress;

            public uint DomainControllerAddressType;

            public Guid DomainGuid;

            [MarshalAs(UnmanagedType.LPWStr)]

            public string DomainName;

            [MarshalAs(UnmanagedType.LPWStr)]

            public string DnsForestName;

            public int Flags;

            [MarshalAs(UnmanagedType.LPWStr)]

            public string DcSiteName;

            [MarshalAs(UnmanagedType.LPWStr)]

            public string ClientSiteName;

        }

 

 

        [DllImport("Netapi32.dll", SetLastError = true, ExactSpelling = true)]

        static extern int NetApiBufferFree(IntPtr Buffer);

 

        [DllImport("Netapi32.dll", EntryPoint = "DsGetDcNameW", ExactSpelling = true,

            CharSet = CharSet.Unicode, SetLastError = false)]

        static extern int DsGetDcName

        (

            [MarshalAs(UnmanagedType.LPWStr)]

            string ComputerName,

            [MarshalAs(UnmanagedType.LPWStr)]

            string DomainName,

            IntPtr DomainGuid,

            [MarshalAs(UnmanagedType.LPWStr)]

            string SiteName,

            int Flags,

            out IntPtr pDOMAIN_CONTROLLER_INFO

        );

    

 

        static internal DOMAIN_CONTROLLER_INFO GetDomainInfo()

        {

            DOMAIN_CONTROLLER_INFO domainInfo;

            IntPtr pDCI;

            int val = DsGetDcName(null, null, IntPtr.Zero, null, 0, out pDCI);

            //check return value for error

            if (val == 0)

            {

                if (pDCI != IntPtr.Zero)

                {

                    domainInfo = (DOMAIN_CONTROLLER_INFO)Marshal.PtrToStructure(pDCI, typeof(DOMAIN_CONTROLLER_INFO));

                    NetApiBufferFree(pDCI);

                    Marshal.DestroyStructure(pDCI, typeof(DOMAIN_CONTROLLER_INFO));

                    return domainInfo;

                }

                else

                    throw new NullReferenceException("DsGetDcName returned null");

            }

            else

            {

                throw new Win32Exception(val);

            }

        }

    }

}

 

Now our tester code. I'm sure you know how to deal with it

static void Main()

{

    NameTranslate.NWCNameTranslate nt = new NameTranslate.NWCNameTranslate();

   

    nt.Init(NameTranslate.ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_DOMAIN);

    string you = Environment.UserDomainName + "\\" + Environment.UserName;

    nt.Set(NameTranslate.DS_NAME_FORMAT.DS_NT4_ACCOUNT_NAME, you);

    NWCNameTranslate.NameResultItem test = nt.Get(NameTranslate.DS_NAME_FORMAT.DS_FQDN_1779_NAME);

   

    // twice the same entry, just to illustrate :)           

    nt.SetEx(NameTranslate.DS_NAME_FORMAT.DS_NT4_ACCOUNT_NAME, new string[] { you, you });

 

    NWCNameTranslate.NameResultItem[] test2 = nt.GetEx(NameTranslate.DS_NAME_FORMAT.DS_FQDN_1779_NAME);

}

 

 

ps: You may not publish this article without permission.

You can download the source here: nametranslate.zip

Hi,

Let's just get to business!

Eprogrammer is MVP, a Microsoft Valued Professional. And what for? Just for helping people on the internet, you can find him often at news://news.microsoft.com/microsoft.public.inetserver.iis . He runs a business, is self-employed and always looks for the edge of technology on C#, .NET C++ and ASP.NET.

Ok, who is Eprogrammer really? Are you really interested? In my free-time, I love to play and hunting rabbits with my falcon 'a gray parrot' and when I'm not free, I love to speak with Russian people about the bible. To be honest now, I did not say everything according to truth, if you look for 'chippy'  in my private photocollection, you'll discover, that grey parrots do not really love to eat rabbits :)

What can you expect in my web logs? I would like to write like Kenny Kerr, my favorite weblogger. He's doing a lot of new and verstatile things that are really usefull to us all. http://weblogs.asp.net/kennykerr/

Currently, I run two projects, one for Leones, a MMC 3.0 add-in project. I never expected to write a full MMC application, but really believe me, in just a day or two, you have your own Active Directory Users and Computers snapin! (Now I'm exaggerating again, sorry!)

This MMC experience, is a very usefull, but suddenly, when you think you got it all, you'll find, it won't work as an extension snap-in for the Active Directory Users and Computers snapin. Why? Because they use a slightly other interface which derives for instance from another interface (for instance) IShellExtInit. MS wanted us, third party developers, to create our own extensions for the mentioned MMC app. The MSDN documentation really tells you all you should know, I repeat: they tell you, they write about it, but the samples lack recent code using ATL or WTL and using .NET, you'll find yourself writing interop declares for the biggest part of the programming time, so I decided to stick to C++ for the moment. I do not complain :), this makes my job a unique one, for the moment.

That's why I use C++ for my active directory extension snapins, for adccure.nl and .NET 2.0 framework, for Leones, the MMC SDK 3.0 should do it.

I'll post back later to let you know my findings.

Oh yes, I forgot to mention, reading blogs are for free :<, I share my secrets with you, but only if my sponsor pays enough :)