C# 在使用PInvoke访问ldap功能的回调期间调用CertFreeCertificateContext时发生异常

C# 在使用PInvoke访问ldap功能的回调期间调用CertFreeCertificateContext时发生异常,c#,interop,ldap,C#,Interop,Ldap,当调用CertFreeCertificateContext方法时,我在运行下面的代码时遇到访问冲突异常 我想这是因为pServerCert参数在LdapServerCertDelegate上被mashall,但却找不到解决方案 using System; using System.Runtime.InteropServices; namespace ldaptest { class Program { static void Main(string[] args

当调用CertFreeCertificateContext方法时,我在运行下面的代码时遇到访问冲突异常

我想这是因为pServerCert参数在LdapServerCertDelegate上被mashall,但却找不到解决方案

using System;
using System.Runtime.InteropServices;

namespace ldaptest
{
    class Program
    {
        static void Main(string[] args)
        {
            new LdapAuthenticationProvider().AuthenticateUser("a.qas", "a", "administrator", "test123");
        }
    }

    public class LdapAuthenticationProvider
    {
        public void AuthenticateUser(string server, string domain, string username, string password)
        {
            IntPtr ld = ldap_sslinit(server, LDAP_SSL_PORT, 1);
            if (IntPtr.Zero == ld) throw new Exception("ldap_sslinit");

            var version = new IntPtr(LDAP_VERSION3);
            var ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, version);
            if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_PROTOCOL_VERSION 0x{0:X}", ret));

            var ldapOn = new IntPtr(LDAP_OPT_ON);
            ret = ldap_set_option(ld, LDAP_OPT_SSL, ldapOn);
            if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SSL 0x{0:X}", ret));

            // note the necessity to convert the delegate to a function pointer
            var callback = new LdapServerCertDelegate(AcceptAnySslCertificate);
            IntPtr pFn = Marshal.GetFunctionPointerForDelegate(callback);
            ret = ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, pFn);
            if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SERVER_CERTIFICATE 0x{0:X}", ret));

            var tv = new l_timeval();
            ret = ldap_connect(ld, ref tv);
            if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_connect 0x{0:X}", ret));

            string login = string.Format(@"{0}\{1}", domain, username);
            ret = ldap_bind_s(ld, login, password, LDAP_AUTH_SIMPLE); // triggers the callback
            if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_bind_s 0x{0:X}", ret));

            ldap_unbind_s(ld);
            Console.WriteLine("Success");
            Console.Read();
        }

        private delegate bool LdapServerCertDelegate(IntPtr connection, IntPtr pServerCert);

        private bool AcceptAnySslCertificate(IntPtr connection, IntPtr pServerCert)
        {
            CertFreeCertificateContext(pServerCert); // << System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
            return true;
        }

        #region crypt32.dll functions

        [DllImport("crypt32.dll", CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CertFreeCertificateContext(IntPtr pCertContext);

        #endregion

        #region Winldap.h definitions

        private const int LDAP_PORT = 389;
        private const uint LDAP_SSL_PORT = 636;
        private const int LDAP_VERSION3 = 3;
        private const int LDAP_OPT_PROTOCOL_VERSION = 17;
        private const int LDAP_OPT_SSL = 10;
        private const int LDAP_OPT_ON = 1;
        private const int LDAP_AUTH_SIMPLE = 128;
        private const int LDAP_OPT_SERVER_CERTIFICATE = 129;
        private const uint LDAP_SUCCESS = 0;

        [StructLayoutAttribute(LayoutKind.Sequential)]
        private struct l_timeval
        {
            private int tv_sec;
            private int tv_usec;
        }

        #endregion

        #region wldap32.dll functions

        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_sslinit.asp?frame=true
        [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_sslinitW",
            SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr ldap_sslinit(string hostName, uint portNumber, int secure);

        [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW",
            SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern uint ldap_set_option([In] IntPtr ldapHandle, int option, IntPtr invalue);

        [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_unbind_s",
            SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern uint ldap_unbind_s([In] IntPtr ldapHandle);

        [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_connect",
            SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern uint ldap_connect([In] IntPtr ld, [In] ref l_timeval timeout);

        [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_bind_sW",
            SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern uint ldap_bind_s([In] IntPtr ld, string dn, string cred, uint method);

        #endregion
    }
}
使用系统;
使用System.Runtime.InteropServices;
名称空间ldaptest
{
班级计划
{
静态void Main(字符串[]参数)
{
新的ldaAuthenticationProvider().AuthenticateUser(“a.qas”、“a”、“管理员”、“test123”);
}
}
公共类LdapAuthenticationProvider
{
public void AuthenticateUser(字符串服务器、字符串域、字符串用户名、字符串密码)
{
IntPtr ld=ldap\u sslinit(服务器,ldap\u SSL\u端口,1);
if(IntPtr.Zero==ld)抛出新异常(“LDAPsslinit”);
var版本=新的IntPtr(LDAP_版本3);
var ret=ldap\u set\u选项(ld,ldap\u OPT\u协议版本,版本);
如果(ret!=LDAP_SUCCESS)抛出新异常(string.Format(“LDAP_OPT_协议_版本0x{0:X}”,ret));
var ldapOn=新的IntPtr(LDAP选择打开);
ret=ldap\u set\u选项(ld、ldap\u OPT\u SSL、ldapOn);
如果(ret!=LDAP_SUCCESS)抛出新异常(string.Format(“LDAP_OPT_ssl0x{0:X}”,ret));
//请注意将委托转换为函数指针的必要性
var callback=新的LdapServerCertDelegate(证书);
IntPtr pFn=Marshal.GetFunctionPointerForDelegate(回调);
ret=ldap_设置_选项(ld,ldap_选项_服务器_证书,pFn);
如果(ret!=LDAP_SUCCESS)抛出新异常(string.Format(“LDAP_OPT_SERVER_证书0x{0:X}”,ret));
var tv=新的l_timeval();
ret=ldap_连接(ld,ref tv);
如果(ret!=LDAP_SUCCESS)抛出新异常(string.Format(“LDAP_connect 0x{0:X}”,ret));
string login=string.Format(@“{0}\{1}”,域,用户名);
ret=ldap_bind_s(ld,login,password,ldap_AUTH_SIMPLE);//触发回调
如果(ret!=LDAP_SUCCESS)抛出新异常(string.Format(“LDAP_bind_s 0x{0:X}”,ret));
ldap解除绑定(ld);
Console.WriteLine(“成功”);
Console.Read();
}
私有委托bool LdapServerCertDelegate(IntPtr连接,IntPtr psservercert);
专用布尔证书(IntPtr连接、IntPtr pServerCert)
{

CertFreeCertificateContext(pServerCert);//最好的选择是避免PInvoke并使用C++/CLI。

最好的选择是避免PInvoke并使用C++/CLI。

如果您想更新您的答案,我希望看到您是如何p/调用它的。上面的代码使用了p/调用,但我从未解决访问冲突,而是实现了LdapAuthenticationProvider::AC++/CLI中的AuthenticateUser,然后从C#汇编中调用它,结果证明这种方式更简单。如果您愿意更新答案,我将非常感谢您如何P/调用它。上面的代码使用P/调用,但我从未解决访问冲突,而是在C++/CLI中实现了LdapAuthenticationProvider::AuthenticateUser,这是一个然后从C#assembly中调用,结果证明这样做容易得多。