C# 我需要解组内存吗。。。两次

C# 我需要解组内存吗。。。两次,c#,winapi,marshalling,C#,Winapi,Marshalling,我正在使用Win32 API来处理DHCP内容,下面是pinvoke.com上的一个代码示例,我发现示例代码在我模拟用户时会杀死我的应用程序,在我不模拟用户时工作正常。我相信这与Marshal.FreeCoTaskMem和/或Marshal.destrostructure有关,但我不明白为什么 我有点害怕内存泄漏,所以我想把它做好。由于我正在潜入非托管世界,我的理解是内存需要手动清理,因此需要调用Marshal.FreeCoTaskMem或Marshal.destrostructure。如果我不

我正在使用Win32 API来处理DHCP内容,下面是pinvoke.com上的一个代码示例,我发现示例代码在我模拟用户时会杀死我的应用程序,在我不模拟用户时工作正常。我相信这与Marshal.FreeCoTaskMem和/或Marshal.destrostructure有关,但我不明白为什么

我有点害怕内存泄漏,所以我想把它做好。由于我正在潜入非托管世界,我的理解是内存需要手动清理,因此需要调用Marshal.FreeCoTaskMem或Marshal.destrostructure。如果我不是从封闭的模拟using块中调用它,那么这些方法工作得很好。否则(通过设置断点),每当GetDHCPServers()方法命中提到的死点时,我就会看到模拟类的Dispose方法被命中

我的问题是——考虑到我是如何模仿的,我是否需要担心使用Marshal来清理我自己,或者它是多余的?如果我确实需要做一些事情,那么在不破坏封闭的模拟类的情况下,什么是安全的方法呢

以下是我模仿的方式:

 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
 public class Impersonation : IDisposable
 {
    private readonly SafeTokenHandle _handle;
    private readonly WindowsImpersonationContext _context;

    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

    // WIN32 API 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

    public Impersonation(string domain, string username, string password)
    {
        var ok = LogonUser(username, domain, password,LOGON32_LOGON_NEW_CREDENTIALS, 0, out this._handle);

        if (!ok)
        {
            var errorCode = Marshal.GetLastWin32Error();
            throw new ApplicationException(string.Format("Could not impersonate the elevated user.  LogonUser returned error code {0}.", errorCode));
        }

        this._context = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());
    }

    public void Dispose()
    {
        this._context.Dispose();
        this._handle.Dispose();
    }
}
然后,我在启动DHCP服务器查找程序时使用此模拟上下文,如下所示:

        using (new Impersonation(_domain, _user, _password))
        {
            DHCPScanner.GetDHCPServers();
        }
这里是问题发生的地方,单元测试失败了(没有异常,它只是停止运行):

[DllImport(“dhcpsapi.dll”,SetLastError=true,CharSet=CharSet.Unicode)]
私有静态外部单元DHCPenumSubnet(
字符串ServerIpAddress,
参考uint恢复句柄,
uint首选最大值,
输出IntPtr枚举信息,
参考uint元素READ,
参考uint ElementsTotal
);
公共静态void GetDHCPServers()
{
IntPtr-svrs;
uint dhcpResult=dhcpenumserver(0,IntPtr.Zero,out svrs,IntPtr.Zero,IntPtr.Zero);
如果(dhcpResult==0)
{
DHCP_服务器_信息_数组dsArray=(DHCP_服务器_信息_数组)Marshal.PtrToStructure(svrs,typeof(DHCP_服务器_信息_数组));
int size=(int)dsArray.NumElements;
IntPtr outArray=dsArray.Servers;
DHCPDS_服务器[]服务器列表=新的DHCPDS_服务器[大小];
DHCP_服务器[]outlist=新的DHCP_服务器[大小];
IntPtr电流=输出阵列;
对于(int i=0;iFreeCoTaskMem元帅(outArray);//模拟不应影响内存管理,尽管它可能会在出现问题时改变症状。文档中说,您需要使用它来释放DHCP服务器管理API返回的缓冲区;此函数似乎没有被封送处理类公开,因此您可能需要直接P/调用它。您好,Harry,这对k很好现在,我一直在关注pPcKe.NET上的DHCP相关项目,而我从来没有提到过。我今晚回家时会调用这个函数,看看它是如何影响事情的。参考一下,这里是我发现我的函数的地方,因为C++是可怕的:如果这不能修复它,那么DHCP服务器管理API中只会出现一个bug。在启用模拟时暴露;这样的错误很容易被忽略。您可能会发现需要在其他用户的上下文中启动子进程来代表您完成工作。
    [DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern uint DhcpEnumSubnets(
        string ServerIpAddress,
        ref uint ResumeHandle,
        uint PreferredMaximum,
        out IntPtr EnumInfo,
        ref uint ElementsRead,
        ref uint ElementsTotal
    );

    public static void GetDHCPServers()
    {
        IntPtr svrs;
        uint dhcpResult = DhcpEnumServers(0, IntPtr.Zero, out svrs, IntPtr.Zero, IntPtr.Zero);

        if (dhcpResult == 0)
        {
            DHCP_SERVER_INFO_ARRAY dsArray = (DHCP_SERVER_INFO_ARRAY)Marshal.PtrToStructure(svrs, typeof(DHCP_SERVER_INFO_ARRAY));
            int size = (int)dsArray.NumElements;
            IntPtr outArray = dsArray.Servers;
            DHCPDS_SERVER[] serverList = new DHCPDS_SERVER[size];
            DHCP_SERVERS[] outlist = new DHCP_SERVERS[size];
            IntPtr current = outArray;
            for (int i = 0; i < size; i++)
            {
                serverList[i] = new DHCPDS_SERVER();
                Marshal.PtrToStructure(current, serverList[i]);
                // the below line kills the process if I uncomment it
                //Marshal.DestroyStructure(current, typeof(DHCPDS_SERVER)); 
                current = (IntPtr)((int)current + Marshal.SizeOf(serverList[i]));
                outlist[i].ServerName = serverList[i].ServerName;
                outlist[i].ServerAddress = UInt32IPAddressToString(serverList[i].ServerAddress);
            }
            Marshal.FreeCoTaskMem(outArray); // <-- dies here too

        }

    }