当ini文件是远程文件时,在模拟块中运行的GetPrivateProfileSectionNames返回0

本文关键字:文件 运行 模拟 GetPrivateProfileSectionNames 返回 ini 程文件 | 更新日期: 2023-09-27 18:21:38

我有一段代码,它创建了一个模拟块,允许对远程ini文件进行读取访问,并允许对远程目录进行写入访问。当要写入的"远程"目录确实是远程计算机UNC路径时,系统会正常写入,但如果"远程"ini文件确实是远程UNC路径,GetPrivateProfileSectionNames会返回0。但是,如果"远程"ini文件实际上只是一个本地UNC路径,则此函数可以正常工作。如果ini文件真的在远程计算机上,有没有办法让这个函数按预期工作?

我的模拟块是使用以下一次性类完成的:

[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonation : IDisposable
{
    private WindowsImpersonationContext _impersonatedUserContext;
    // Declare signatures for Win32 LogonUser and CloseHandle APIs
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool LogonUser(
      string principal,
      string authority,
      string password,
      LogonSessionType logonType,
      LogonProvider logonProvider,
      out IntPtr token);
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool RevertToSelf();
    // ReSharper disable UnusedMember.Local
    enum LogonSessionType : uint
    {
        Interactive = 2,
        Network,
        Batch,
        Service,
        NetworkCleartext = 8,
        NewCredentials
    }
    // ReSharper disable InconsistentNaming
    enum LogonProvider : uint
    {
        Default = 0, // default for platform (use this!)
        WinNT35,     // sends smoke signals to authority
        WinNT40,     // uses NTLM
        WinNT50      // negotiates Kerb or NTLM
    }
    // ReSharper restore InconsistentNaming
    // ReSharper restore UnusedMember.Local
    /// <summary>
    /// Class to allow running a segment of code under a given user login context
    /// </summary>
    /// <param name="user">domain'user</param>
    /// <param name="password">user's domain password</param>
    public Impersonation(string user, string password)
    {
        var token = ValidateParametersAndGetFirstLoginToken(user, password);
        var duplicateToken = IntPtr.Zero;
        try
        {
            if (DuplicateToken(token, 2, ref duplicateToken) == 0)
            {
                throw new Exception("DuplicateToken call to reset permissions for this token failed");
            }
            var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
            _impersonatedUserContext = identityForLoggedOnUser.Impersonate();
            if (_impersonatedUserContext == null)
            {
                throw new Exception("WindowsIdentity.Impersonate() failed");
            }
        }
        finally
        {
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (duplicateToken != IntPtr.Zero)
                CloseHandle(duplicateToken);
        }
    }
    private static IntPtr ValidateParametersAndGetFirstLoginToken(string user, string password)
    {
        if (string.IsNullOrEmpty(user))
        {
            throw new ConfigurationErrorsException("No user passed into impersonation class");
        }
        var userHaves = user.Split('''');
        if (userHaves.Length != 2)
        {
            throw new ConfigurationErrorsException("User must be formatted as follows: domain''user");
        }
        if (!RevertToSelf())
        {
            throw new Exception("RevertToSelf call to remove any prior impersonations failed");
        }
        IntPtr token;
        var result = LogonUser(userHaves[1], userHaves[0],
                               password,
                               LogonSessionType.Interactive,
                               LogonProvider.Default,
                               out token);
        if (!result)
        {
            throw new ConfigurationErrorsException("Logon for user " + user + " failed.");
        }
        return token;
    }
    /// <summary>
    /// Dispose
    /// </summary>
    public void Dispose()
    {
        // Stop impersonation and revert to the process identity
        if (_impersonatedUserContext != null)
        {
            _impersonatedUserContext.Undo();
            _impersonatedUserContext.Dispose();
            _impersonatedUserContext = null;
        }
    }
}

当在此类的模拟块实例中时,远程ini文件由以下人员访问:

int bufLen = GetPrivateProfileSectionNames(buffer, 
                                           buffer.GetUpperBound(0),     
                                           iniFileName);
if (bufLen > 0)
{
     //process results
}

在处理远程计算机时,如何让GetPrivateProfileSectionNames返回有效数据?我的用户在此计算机或远程计算机上是否需要权限?

当ini文件是远程文件时,在模拟块中运行的GetPrivateProfileSectionNames返回0

目前,我还无法找到有关模拟以及它如何与win32 dlls/api交互的信息,但是,我知道以下内容:

1) 如果整个进程是在一个可以访问ini文件所在的远程文件夹的用户下运行的,那么GetPrivateProfileSectionNames将按所需的工作

2) 如果在模拟块内调用GetPrivateProfileSectionNames,则它不能按所需的工作

3) 如果打开文件流,并将ini文件复制到本地,则在本地ini文件上使用GetPrivateProfileSectionNames,然后根据需要使用GetPrivatProfileSectionName,并且允许文件流访问远程文件。

根据结果,我推测win32api调用GetPrivateProfileSectionNames没有从c#获得模拟上下文,因此在没有访问权限的整个进程上下文下运行。我通过在本地缓存ini文件并跟踪它上次更改的时间来解决这个问题,这样我就知道ini文件是否需要重新缓存,或者本地副本是否正确。