我需要两次解组记忆吗?

时间:2014-07-14 23:32:27

标签: c# winapi marshalling

我使用Win32 API进行DHCP操作,并按照pinvoke.com的代码示例,我发现当我冒充用户时示例代码会杀死我的应用,并且当我模仿时工作正常不是。我相信它与Marshal.FreeCoTaskMem和/或Marshal.DestroyStructure有关,但我不明白为什么。

我有点害怕内存泄漏,所以我想这样做。由于我潜入了非托管的世界,我的理解是需要手动清理内存,因此调用Marshal.FreeCoTaskMem或Marshal.DestroyStructure。如果我没有在封闭的Impersonation使用块中调用它,这些方法可以正常工作。否则(通过设置断点)我看到只要GetDHCPServers()方法击中上述死亡点,就会触及Impersonation类的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)]
    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

        }

    }

0 个答案:

没有答案