The PInvoke problem


I have been talking to the CLR interop team recently about some ideas to make life easier for developers using PInvoke.  My hope is that with WinFx PInvoking out to the Win32 will be much more rare, but we have a while to go before we get there.  In the meantime, we’d like your feedback:


Several times in the past, we’ve got feedback that it’s hard to write pinvoke declarations. We were asked to either provide a tool that would take an unmanaged C header file and produce pinvoke declarations in C# and/or VB.NET or to publish pinvoke declarations for the Win32 APIs. The first ask is unfortunately impossible to do 100% correctly since unmanaged header files are too ambiguous to be automatically convertible. For example, how would such tool convert char* parameter? As String, StringBuilder, ref char or char[]? It’s hard to know unless you read docs for that win32 function. The second ask was technically possible of course but we would always run into issue of resources and benefits i.e. is it really worth doing?



Currently, we are investigating variation of the second solution, which is publishing all pinvoke declarations we have in our code base in some searchable form (both C# and VB.NET variations). We are also planning to possibly allow community to post their own pinvoke declarations and grow database of declarations over time.



We are interested to know if this would be useful. Even though we couldn’t publish all the Win32 API declarations (or any other 3rd party API declarations) we hope that a) there would be reasonable chance you could find declarations you need, b) you would be able to use the database to learn how to write your own declarations, and c) you would be able to submit missing declarations and help other developers.



Please let us know if you have any feedback about this. Is this indeed an issue for you? Would such database help or even solve your issues with writing pinvoke declarations?

Comments (30)

  1. Seems like a good first step would be to buy the rights to reprint the appendixes from Adam Nathan’s book.

  2. Cory Smith says:

    I, for one, would definately welcome a internet updatable win32 api declaration database application 😉

  3. Steve Hall says:

    This problem is one dear to my VB6 heart! Being in a shop that is starting to convert to VB.NET and C#, every day I encounter yet more Win32 API function declarations that weren’t published as part of VB6, or were published and are downright wrong (usually erroneous conversions of string/struct/VOID pointers).

    The second option you suggest of publishing Win32 API definition files would certainly be a start, and the community additions would be great! But, you MUST also allow for community corrections to eventually replace the errant definitions. And those corrections must be refereed and merged into the master copy in a timely fashion — monthly or quarterly — not once a year or only for VS.NET service packs.

    It would also be great if this master P/Invoke Win32 API reference "database" (in whatever form it takes) were to published as part of the quarterly MSDN Library. (That’s the PERFECT vehicle for delivery of this "living/improving documentation".) It should also be published as part of the .NET Framework SDK.

    Whatever solution you choose, you need to solve the previous problem with the VB6 Win32 API definition files: they were published once and rarely (if ever) updated. Essentially, VB6 programmers felt that each service pack should have updated the file, but obviously wrong definitions were never corrected as they still exist at VB6 SP5. This non-support of a single silly file was simply inexcusable and should not be allowed to recur, else it will only serve to disenfranchise even more VB.NET programmers then those already upset by the myriad of VB language changes, as well as C# programmers!

    The idea of making a Win32 API definition database is a nice idea, but sounds like overkill. The simple approach taken with VB6, a single file and a simply API viewer tool, is all that’s needed.

    Now if a database is in the cards, there should be a tool to be able to datamine ALL the definitions into a single file for download. This would enable finding needed API definitions WITHOUT having to resort to visitng a website. (Having to resort to using websites every 5 minutes during coding can become cumbersome…esp. at home on a slow dialup. Yeah, I know: I’m DSL-challenged. Unfortunately, there’s still a lot of people who’s lot in life is to suffer at the hands of their crappy phone company!)

    Essentially, the API definitions should get installed locally as part VS.NET, MSDN Library, and the .NET Framework SDK — whatever the user has downloaded or purchased. These definitions are still KEY to windows programming until the .NET BCL has 100% Win32 coverage.

    Thanks for asking for our input Brad!

  4. Stephane Rodriguez says:

    1) It’s more than the function signature I am afraid. Not only all structs, enums and symbols have to be pre-marshalled as well and made searchable in some database, you’ll also have to provide code samples most of the time. Why? simply because often, custom marshalling is the only way to go, and this requires code.

    2) A lot of signatures will end up with IntPtr and Object. How is it supposed to tell the developer what they are supposed to give to it?

    3) As for the 2nd solution, I think an AUTOMATIC CONVERTER is the way to go. Copy your C/C++ code, click Convert, and paste the result in your managed code. It would take advantage of that existing database and would work most of the time. It would work perfectly for all wellknown uses, and would do 80% of the work otherwise.

    4) Regarding automatic converters, there are a few out there already.

    5) I don’t know if that question was really open. People like ANathan have more than their share already about what developers need in this area. And if you wondered, devsites message boards are waiting for you.

  5. Stephane Rodriguez says:

    Of course I forgot the obvious one : favor managed C++ since you need not P/Invoke. The amazing thing would be, instead of forcing them to create an assembly only to store the unmanaged stuff, it would be an order to magnitude simpler if a managed C++ code snippet could be inserted right into managed code, just like an asm block can be inserted right into regular C/C++ code.

  6. Shane King says:

    It would be easier to just copy and paste a few declarations rather than trying to figure them out for yourself.

    The main problem I have is the documentation for PInvoke is (IMO) pretty confusing, and doesn’t really tell you what you need to know. So you’re left with trial and error. Which is hard to do, since you don’t have a lot of tool support in VS to really be able to tell if what you’re doing is working right (or more to the point, to figure out why it’s not working).

  7. Pavel Lebedinsky says:

    I could never understand why .NET 1.0 shipped without prepackaged pinvoke declarations for most common APIs.

    J++ had this stuff (known as com.ms.win32 package) back in 1997 and it’s sad that .NET programmers are still struggling with manual J/Dire^H^H^H PInvoke declarations in 2004.

    Check out for example this article from 1997:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnguion/html/msdn_drguinat.asp

    [quote]

    You’re probably looking at this and saying, "Nice—but I don’t want to have to write these funky comment directives for each Windows function I want to use." Fortunately, you don’t have to declare the entire Windows API yourself—Microsoft provides handy classes that contain these declarations. All you have to do is import the correct class(es).

    [/quote]

    Well… I always thought J++ was ahead of its time.

  8. Marco Russo says:

    This is of course a good community project. An "open" readable and modifiable repository on GotDotNet would be good. Only problem: some volunteer moderator would be necessary.

  9. Corrado Cavalli says:

    Absolutely helpful, PInvoke declaration (especially on strings) is one of the area where devs usually get lost (I see many VB6’er using same VB6 declarations on .NET)

    So it will Definitely help (something like APIGuide but integrated into VS)

    It is also very hard to get messages defines even from MSDN

  10. Ken Brubaker says:

    My personal feeling is that MS will benefit from such a project To do so would 1) get ISVs moved over faster to the managed environment 2) reduce the bugs in ISV code 3) improve ISV code security because managed code is safer code. All of this makes money for Microsoft.

    I think an MS managed open source solution is a second tier solution as it will be harder for non-MS folks to ask the necessary questions to get it "right".

    However if MS is not willing to eat the whole pie, an MS managed community project is workable. Figuring that, collectively, there is a rather broad coverage of the API, we can get a rather large body of solutions.

    I see that starting with the nucleus of MS P/Invoke code as essential, as is having a MS managed process and MS quality control. A grab-bag approach is the worst possible result. Don’t let the Software Development process devolve into the hip-shooting culture of much of the open-software community.

  11. milbertus says:

    "2) A lot of signatures will end up with IntPtr and Object. How is it supposed to tell the developer what they are supposed to give to it?"

    That’s what the Platform SDK docs are for. While you can’t use only the Platform SDK or only these PInvoke declarations seperately to know what to do from managed code, but together you’ll be able to use the Win32 API correctly from managed code.

  12. Curt says:

    >>> Currently, we are investigating variation of the second solution, which is publishing all pinvoke declarations we have in our code base in some searchable form (both C# and VB.NET variations). <<<

    I think that would be a solid start. Can you be sure to include P/Invoke declarations for the CE database APIs? 🙂

  13. Brad,

    The painful part about P/Invoke is declaring the structs. Yet that’s something that can be produced from the header files automatically.

    As for the definitions of individual functions, the more you give us the better.

    Dejan

  14. Eric says:

    If Microsoft is doing it: The teams that maintain the code should provide a Pirmary Interop Assembly (PIA) that can be used to invoke the functions. There probably won’t be time to do this except for Longhorn. Of course, even better would be full managed wrappers on top of the API.

    If the community is doing it: Pick one of the existing sites and work from there. You’ll probably want code rather than an assembly from some unknown party, but that’s OK. Group the code into public files though, and inside classes, so that all you have to do is build the code into your project – after you’ve reviewed it, of course.

    Like others, I’m surprised that Microsoft didn’t do it, but I’m sure it was a schedule thing. Next best would be having MSDN document the PInvoke declaration on the smae web page that describes the Win32 function/struct. Just as the managed code pages have info for C#, VB and C++, the Win32 functions could have C and Managed code listings for each API. This means that the dev team has less work, but your documentation team will have to grow…

  15. Raymond Chen says:

    Examples of more complicated p/invoke in the documentation might be enough for some. The current examples are pretty light. I lost quite a bit of hair trying to write the p/invoke for InitializeSecurityContext; the pInout and phNewContext are strange in/out parameters, and I gave up on SecBufferDesc and just did everything manually and used a lot of IntPtr.

  16. Given the other comments here, this will probably be a controversial opinion. But I always thought that the difficulty of using Win32 functions was, on balance, a good thing… If it had been trivial to use them, those of us who were familiar with Win32 would probably have carried on using it rather than discovering the new ways offered by the .NET Framework. The barrier to entry meant you would try hard to find a .NET way of doing something before falling back to the Win32 way.

    I would be quite happy to see P/Invoke become easier *after* we move away from the current default CAS behaviour for local code. (At least I have the impression that the long term intention is to move away from the current ‘local code always gets full trust’ policy.) If local code didn’t get full trust by default, then this would put people off using P/Invoke unless there was no viable alternative. (And if there *is* no viable alternative, then it’s quite reasonable to make P/Invoke as easy to use as possible.) But if the bar to using Win32 directly is lowered before a more secure execution environment for local code becomes the norm, I fear we will see a rash of applications that won’t work without full trust because they use P/Invoke when it’s not strictly necessary..

    It’ll be like running as admin all over again: an ingrained culture of running with far more permissions than necessary, which will take years to recover from.

  17. Not only would this be helpful as a resource in its own right, but it would also give a large list of samples that people could work off when writing their own P/Invoke declarations. Providing fish to eat and bait to catch more, so to speak.

  18. James Bellinger says:

    Something like VB6’s Win32 API Viewer would be the best solution, at least for the base APIs.

    However, I think a C#/VB.Net version of API Viewer should pop up an information box if the API they are selecting already has a mananged equivalent, because many programmers use P/Invoke even when what they are trying to do can be done through managed code, and anything to avoid this would be beneficial.

  19. J. Daniel Smith says:

    I think Stephane Rodriguez has the right idea: allow a snipet of MC++ code to be placed in a C# file so that IJW can easily be used instead of P/Invoke.

    Ian Griffiths is also on-target with the "high barrier to entry" comments.

  20. +1 for a community PInvoke resource.

  21. Danny Thorpe says:

    We would definitely like to see prepackaged (pretested) Win32 API P/Invoke declarations. Actually, Borland has already had to figure out a boatload of these things to implement VCL for .NET in Delphi 8. These are all in Delphi syntax, of course. ;> But we need more, much more than we have resources to dump on this task.

    -Danny

  22. Ingrid Osinaga says:

    Hello

    I do not speak Ingles, but I read unpoco of ingles. I am using a translator, I hope that he understands my message.

    I saw your commentary in the page http://blogs.msdn.com/brada/archive/2004/02/06/69095.aspx“>http://blogs.msdn.com/brada/archive/2004/02/06/69095.aspx of problems with Pinvoke and saw that you worked in C # with InitializeSecurityContext emplendo IntPtr.

    I am working in a project in which I need to obtain the identity of the user who this acceding to the server by means of browser and causing that a process is generated in another servant for the user of browser.

    I want it to make with InitializeSecurityContext and other functions.

    Using WindowsIdentity, I obtain his LUID to command to AcquireCredentialsHandle in "NTLM" and asi to obtain his credentials, not error returns me, but when control the credential to the InitializeSecurityContext returns an error to me SEC_E_INVALID_HANDLE (-2146893055) that I am making bad, I do not hope that you can help me.

    (En Españo)

    Hola Raymond Chen, No hablo Ingles, pero leo unpoco de ingles. Estoy usando un traductor, espero que entienda mi mensaje. Vi tu comentario en la página http://blogs.msdn.com/brada/archive/2004/02/06/69095.aspx“>http://blogs.msdn.com/brada/archive/2004/02/06/69095.aspx de problemas con Pinvoke y vi que trabajaste en C# con InitializeSecurityContext emplendo IntPtr. Estoy trabajando en un proyecto en el que necesito obtener la identidad del usuario que esta accediendo al server mediante un browser y hacer que se genere un proceso en otro servidor para el usuario del browser. Lo quiero hacer con InitializeSecurityContext y otras funciones. Empleando WindowsIdentity, obtengo su LUID para mandar a AcquireCredentialsHandle en "NTLM" y asi obtener sus credenciales, no me retorna error, pero cuando mando el credencial al InitializeSecurityContext me retorna un error SEC_E_INVALID_HANDLE (-2146893055) no se que estoy haciendo mal, espero que me puedas ayudar.

    El codigo es el siguiente, (soy nueva en esto ) Help, Help

    using System;

    using System.Collections;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Web;

    using System.Web.SessionState;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.HtmlControls;

    using System.Security.Principal ;

    using System.Diagnostics ;

    using System.Runtime.InteropServices ;

    using System.Text ;

    using System.Net.Sockets ;

    using System.Runtime.Serialization.Formatters.Binary ;

    using HANDLE = System.IntPtr ;

    public struct SID_AND_ATTRIBUTES

    {

    public IntPtr Sid ;

    public int Attributes ;

    }

    public struct TOKEN_USER

    {

    public SID_AND_ATTRIBUTES User ;

    }

    public enum TOKEN_INFORMATION_CLASS

    {

    TokenUser = 1,

    TokenGroups,

    TokenPrivileges,

    TokenOwner,

    TokenPrimaryGroup,

    TokenDefaultDacl,

    TokenSource,

    TokenType,

    TokenImpersonationLevel,

    TokenStatistics,

    TokenRestrictedSids,

    TokenSessionId,

    TokenGroupsAndPrivileges,

    TokenSessionReference,

    TokenSandBoxInert,

    TokenAuditPolicy,

    TokenOrigin

    }

    public struct SecHandle //=PCtxtHandle

    {

    int dwLower ;

    int dwUpper ;

    }

    public struct SecBuffer

    {

    public int cbBuffer ;

    public int BufferType ;

    public IntPtr pvBuffer ;

    }

    public struct SecBufferDesc

    {

    public int ulVersion ;

    public int cBuffers ;

    public IntPtr pBuffers ; //Point to SecBuffer

    }

    public struct _LUID

    {

    public int LowPart ;

    public int HighPart;

    } //LUID, *PLUID;

    public struct SecPkgInfo

    {

    public int fCapabilities;

    public int wVersion;

    public int wRPCID;

    public int cbMaxToken;

    public string Name;

    public string Comment;

    }

    public struct TOKEN_STATISTICS

    {

    public _LUID TokenId;

    public _LUID AuthenticationId;

    public int ExpirationTime;

    public int TokenType; // enum ini in 1 TOKEN_TYPE

    public int ImpersonationLevel; //SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;

    public int DynamicCharged; ////DWORD

    public int DynamicAvailable; //DWORD

    public int GroupCount; //DWORD

    public int PrivilegeCount; //DWORD

    public _LUID ModifiedId;

    }// TOKEN_STATISTICS, *PTOKEN_STATISTICS;

    namespace WebAppLUIDForCred

    {

    public class WebFormMuestraUser : System.Web.UI.Page

    {

    protected System.Web.UI.WebControls.Button cmdUser;

    protected System.Web.UI.WebControls.Label lblResults2;

    protected System.Web.UI.WebControls.Label HOLA;

    protected System.Web.UI.WebControls.Label TokenInf;

    //using System.ComponentModel ;

    public const int TOKEN_QUERY = 0x00008 ;

    public const int SEC_E_OK = 0 ;

    //For AcquireCredentialsHandle in 3er Parameter "fCredentialUse"

    public const int SECPKG_CRED_OUTBOUND = 2 ;

    public const int SECBUFFER_VERSION = 0 ;

    public const int SECBUFFER_TOKEN = 2 ;

    public const int SECURITY_NATIVE_DREP = 10 ;

    public const int ISC_REQ_DELEGATE = 1 ;

    public const int ISC_REQ_REPLAY_DETECT = 4 ;

    public const int ISC_REQ_MUTUAL_AUTH = 2 ;

    public const int ISC_REQ_SEQUENCE_DETECT = 8 ;

    public const int ISC_REQ_CONFIDENTIALITY = 10 ;

    public const int ISC_REQ_USE_SESSION_KEY = 20 ;

    public const int ISC_REQ_PROMPT_FOR_CREDS = 40 ;

    public const int ISC_REQ_USE_SUPPLIED_CREDS = 80 ;

    public const int ISC_REQ_ALLOCATE_MEMORY = 100 ;

    public const int ISC_REQ_USE_DCE_STYLE = 200 ;

    public const int ISC_REQ_DATAGRAM =400 ;

    public const int ISC_REQ_CONNECTION = 800 ;

    public const int ISC_REQ_CALL_LEVEL = 1000;

    public const int ISC_REQ_EXTENDED_ERROR = 4000 ;

    public const int ISC_REQ_STREAM = 08000 ;

    public const int ISC_REQ_INTEGRITY = 10000 ;

    [ DllImport( "Kernel32" ) ]

    static extern bool CloseHandle( HANDLE handle ) ;

    [DllImport("advapi32")]

    static extern bool OpenProcessToken( HANDLE ProcessHandle , int

    DesiredAccess , ref IntPtr TokenHandle ) ;

    [DllImport("advapi32", CharSet=CharSet.Auto)]

    static extern bool GetTokenInformation( HANDLE TokenHandle ,

    TOKEN_INFORMATION_CLASS TokenInformationClass , IntPtr TokenInformation ,

    int TokenInformationLength , ref int ReturnLength ) ;

    [DllImport("advapi32", CharSet=CharSet.Auto)]

    static extern bool LookupAccountSid( string lpSystemName , IntPtr pSid ,

    [ Out ] StringBuilder Account , ref int cbName , [ Out ] StringBuilder

    DomainName ,

    ref int cbDomainName , ref int peUse ) ;

    [DllImport("secur32", CharSet=CharSet.Auto)]

    static extern int AcquireCredentialsHandle(

    string pszPrincipal, //SEC_CHAR*

    string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative"

    int fCredentialUse,

    IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID, //PLUID

    IntPtr pAuthData,//PVOID

    int pGetKeyFn, //SEC_GET_KEY_FN

    IntPtr pvGetKeyArgument, //PVOID

    out HANDLE phCredential, //SecHandle //PCtxtHandle ref

    out IntPtr ptsExpiry); //PTimeStamp //TimeStamp ref

    [DllImport("secur32", CharSet=CharSet.Auto, SetLastError=true)]

    static extern int InitializeSecurityContext(

    HANDLE phCredential,//PCredHandle

    out HANDLE phContext, //PCtxtHandle

    string pszTargetName,

    int fContextReq,

    int Reserved1,

    int TargetDataRep,

    ref IntPtr pInput, //PSecBufferDesc SecBufferDesc

    int Reserved2,

    out HANDLE phNewContext, //PCtxtHandle

    ref IntPtr pOutput, //PSecBufferDesc SecBufferDesc

    out int pfContextAttr, //

    out IntPtr ptsExpiry ); //PTimeStamp

    [DllImport( "advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

    private static extern bool ConvertSidToStringSid( IntPtr psid, ref IntPtr pstr );

    [DllImport( "secur32", CharSet=CharSet.Auto, SetLastError=true)]

    static extern int QuerySecurityPackageInfo(

    string pszPackageName,

    ref IntPtr ppPackageInfo) ; //PSecPkgInfo

    private void Page_Load(object sender, System.EventArgs e)

    {

    // Put user code to initialize the page here

    }

    #region Web Form Designer generated code

    override protected void OnInit(EventArgs e)

    {

    //

    // CODEGEN: This call is required by the ASP.NET Web Form Designer.

    //

    InitializeComponent();

    base.OnInit(e);

    }

    /// <summary>

    /// Required method for Designer support – do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

    this.cmdUser.Click += new System.EventHandler(this.cmdUser_Click);

    this.Load += new System.EventHandler(this.Page_Load);

    }

    #endregion

    private string DataUser()

    {

    TOKEN_STATISTICS TokenUserLUID ;

    _LUID TokenUserLUID2 ;

    IntPtr PAuthenticationID ;

    HANDLE hCred = new HANDLE( ) ;

    string pszPrincipal = null;

    IntPtr DLifetime = new IntPtr( ) ;

    //sksks

    SecBufferDesc OutBuffDesc = new SecBufferDesc( ) ;

    SecBufferDesc InBuffDesc = new SecBufferDesc( ) ;

    SecBuffer OutSecBuff = new SecBuffer( ) ;

    SecBuffer InSecBuff = new SecBuffer( ) ;

    PAuthenticationID = new IntPtr( ) ;

    IntPtr hcText = new IntPtr( ) ;

    string pszTarget = null;

    int ContextAttributes = 0 ;

    IntPtr IntPtrToken ;

    string HostName1 = "localhost" ; //"flox" ;

    int portNum = 65000 ;

    int errr = 0 ;

    int auxcbMaxToken ;

    bool Result ;

    WindowsIdentity MyIdentity = WindowsIdentity.GetCurrent( ) ;

    //IIdentity MyIdentity = IIdentity.

    //Put the previous identity into a principal object.

    WindowsPrincipal MyPrincipal = new WindowsPrincipal( MyIdentity ) ;

    TcpClient tcpSocket = new TcpClient( HostName1 , portNum ) ;

    NetworkStream streamToServer = tcpSocket.GetStream();

    System.IO.StreamWriter writer = new System.IO.StreamWriter( streamToServer ) ;

    try

    {

    //Principal values.

    string Name = MyPrincipal.Identity.Name ;

    string Type = MyPrincipal.Identity.AuthenticationType ;

    string Auth = MyPrincipal.Identity.IsAuthenticated.ToString( ) ;

    TOKEN_USER TokenUser ;

    int TokenInfLength = 0 ;

    Result = GetTokenInformation( MyIdentity.Token , TOKEN_INFORMATION_CLASS.TokenUser ,

    IntPtr.Zero , TokenInfLength , ref TokenInfLength ) ;

    IntPtr TokenInformation = Marshal.AllocHGlobal( TokenInfLength ) ;

    Result = GetTokenInformation( MyIdentity.Token , TOKEN_INFORMATION_CLASS.TokenUser , TokenInformation , TokenInfLength ,

    ref TokenInfLength ) ;

    if( Result )

    {

    TokenUser = ( TOKEN_USER )Marshal.PtrToStructure( TokenInformation ,

    typeof( TOKEN_USER ) ) ;

    StringBuilder UserName = null ;

    StringBuilder DomainName = null ;

    int cchName = 0 ;

    int cchDomain = 0 ;

    int peUse = 0 ;

    IntPtr pstr = IntPtr.Zero;

    Boolean ok = ConvertSidToStringSid( TokenUser.User.Sid , ref pstr );

    string sidstr = Marshal.PtrToStringAuto( pstr );

    Result = LookupAccountSid( null , TokenUser.User.Sid , UserName , ref

    cchName , DomainName , ref cchDomain , ref peUse ) ;

    UserName = new StringBuilder( cchName ) ;

    DomainName = new StringBuilder( cchDomain ) ;

    Result = LookupAccountSid( null , TokenUser.User.Sid , UserName , ref

    cchName , DomainName , ref cchDomain , ref peUse ) ;

    //Cred

    TokenInfLength = 0 ;

    Result = GetTokenInformation( MyIdentity.Token , TOKEN_INFORMATION_CLASS.TokenStatistics , IntPtr.Zero , TokenInfLength , ref TokenInfLength ) ;

    TokenInformation = Marshal.AllocHGlobal( TokenInfLength ) ;

    Result = GetTokenInformation( MyIdentity.Token , TOKEN_INFORMATION_CLASS.TokenStatistics , TokenInformation , TokenInfLength , ref TokenInfLength ) ;

    if( Result )

    {

    TokenUserLUID = ( TOKEN_STATISTICS )Marshal.PtrToStructure( TokenInformation , typeof( TOKEN_STATISTICS ) ) ;

    PAuthenticationID = Marshal.AllocCoTaskMem( Marshal.SizeOf( TokenUserLUID.AuthenticationId ) ) ;

    /*TokenUserLUID2 = (_LUID)Marshal.PtrToStructure( PAuthenticationID ,typeof(_LUID));

    TokenUserLUID2.HighPart = TokenUserLUID.AuthenticationId.HighPart ;

    TokenUserLUID2.LowPart = TokenUserLUID.AuthenticationId.LowPart ;*/

    //Marshal.StructureToPtr(TokenUserLUID2,PAuthenticationID,false);

    Marshal.StructureToPtr(TokenUserLUID.AuthenticationId,PAuthenticationID,false);

    string pszPackage = "Kerberos" ; //"Kerberos","NTLM","Negotiative"

    IntPtr pSPI = new IntPtr( ) ;

    int ss = QuerySecurityPackageInfo( pszPackage,ref pSPI) ; //"Kerberos","NTLM","Negotiative"

    if (pszPackage == "Kerberos")

    {

    pszTarget = "JAKE" ;

    auxcbMaxToken = 1904 ;

    }

    else

    {

    SecPkgInfo EstSecPkgInfo = (SecPkgInfo)Marshal.PtrToStructure( pSPI , typeof( SecPkgInfo ) ) ;

    auxcbMaxToken = EstSecPkgInfo.cbMaxToken ;

    }

    ss = AcquireCredentialsHandle(pszPrincipal, pszPackage, SECPKG_CRED_OUTBOUND,

    IntPtr.Zero, IntPtr.Zero , 0 , IntPtr.Zero,out hCred ,out DLifetime);

    //PAuthenticationID

    if (ss >= SEC_E_OK)

    {

    OutBuffDesc.ulVersion = SECBUFFER_VERSION ;

    OutBuffDesc.cBuffers = 1 ;

    OutSecBuff.BufferType = SECBUFFER_TOKEN;

    OutSecBuff.cbBuffer = auxcbMaxToken; //1904 ; //

    OutSecBuff.pvBuffer = Marshal.AllocHGlobal(1904);//EstSecPkgInfo.cbMaxToken);

    IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( OutSecBuff ));

    Marshal.StructureToPtr( OutSecBuff , buffer, false );

    OutBuffDesc.pBuffers = buffer;

    IntPtr POutBuffDesc =Marshal.AllocCoTaskMem( Marshal.SizeOf( OutBuffDesc ));

    Marshal.StructureToPtr( OutBuffDesc, POutBuffDesc, false );

    InBuffDesc.ulVersion = SECBUFFER_VERSION ;

    InBuffDesc.cBuffers = 1 ;

    InSecBuff.BufferType = SECBUFFER_TOKEN;

    InSecBuff.cbBuffer = 1904 ;//EstSecPkgInfo.cbMaxToken ;// //

    InSecBuff.pvBuffer = Marshal.AllocHGlobal(1904);//EstSecPkgInfo.cbMaxToken);

    IntPtr bufferIn = Marshal.AllocCoTaskMem( Marshal.SizeOf( InSecBuff ));

    Marshal.StructureToPtr( OutSecBuff , bufferIn, false );

    IntPtr PInBuffDesc = new IntPtr( ) ;

    InBuffDesc.pBuffers = bufferIn;

    PInBuffDesc = Marshal.AllocCoTaskMem( Marshal.SizeOf( InBuffDesc ));

    Marshal.StructureToPtr( InBuffDesc, PInBuffDesc, false );

    DLifetime = IntPtr.Zero ;

    // DLifetime= IntPtr.Zero ;

    ss = InitializeSecurityContext (

    hCred,

    out hcText,

    pszTarget,// null string pszTargetName,

    ISC_REQ_REPLAY_DETECT|ISC_REQ_SEQUENCE_DETECT|ISC_REQ_CONFIDENTIALITY|ISC_REQ_SEQUENCE_DETECT|ISC_REQ_DELEGATE,//ISC_REQ_DELEGATE, //,int fContextReq, //29,2077,

    0,//int Reserved1,

    SECURITY_NATIVE_DREP,//int TargetDataRep

    ref PInBuffDesc, // ,ref SecBufferDesc pInput,

    0, //int Reserved2,

    out hcText, //pHandle CtxtHandle = SecHandle

    ref POutBuffDesc,//ref SecBufferDesc pOutput, //PSecBufferDesc

    out ContextAttributes,//ref int pfContextAttr,

    out DLifetime); //ref IntPtr ptsExpiry ); //PTimeStamp

    if ( ss == SEC_E_OK )

    {

    // Message for the server

    //string mensaje = "Hola" ;

    OutBuffDesc = (SecBufferDesc)Marshal.PtrToStructure( POutBuffDesc ,typeof(SecBufferDesc));

    OutSecBuff = (SecBuffer)Marshal.PtrToStructure(OutBuffDesc.pBuffers,typeof(SecBuffer)); //auxP

    if ( streamToServer.CanWrite )

    {

    int num =OutSecBuff.cbBuffer;

    string cad = num.ToString( ) ;

    Byte[] sendBytes = Encoding.ASCII.GetBytes(cad);

    streamToServer.Write(sendBytes, 0, sendBytes.Length);

    if ( streamToServer.CanWrite )

    {

    int num1 =OutSecBuff.cbBuffer;

    string cad1 = num.ToString( ) ;

    Byte[] sendBytes1 = Encoding.ASCII.GetBytes(cad);

    streamToServer.Write(sendBytes1, 0, sendBytes.Length);

    writer.Close( ) ;

    errr = 1 ;

    }

    }

    }

    }

    }

    if (errr == 0 )

    {

    writer.Close( ) ;

    }

    return " UserName = " + Name.ToString( ) + " Domain : " + DomainName.ToString( ) ;

    }

    else

    {

    writer.Close( ) ;

    return " error " ;

    }

    }

    catch

    {

    writer.Close( ) ;

    return " Error " ;

    }

    }

    private void cmdUser_Click(object sender, System.EventArgs e)

    {

    TokenInf.Text = DataUser() ;

    }

    }

    }

    Ingrid M. Osinaga O.

    ingrid@softwareandina.com

    http://www.softwareandina.com

  23. Brad Abrams says:
  24. Ken Brubaker says:

    pinvoke.net, a PInvoke community Wiki, pinvoke.net, maintained by the PInvoke deity Adam Nathan.

  25. Mark Traudt says:

    SWIG is an open source wrapper generator with support for C# as well as many other languages. We have used it extensively over the past several months to generate C# wrappers for our proprierary C++ API.

    I have not used it to wrap Win32 API functions, although I think it should be a good tool for this purpose.