11

Win Vista のリリース以降、Microsoft は 32 ビット プロセスとして実行されるレガシー アプリケーションのファイル仮想化を導入しました。Microsoft のユーザー アカウント制御 (UAC) の一部としてリリースされた、オペレーティング システムによって保護されていると見なされる場所に書き込みを試みるレガシー アプリケーションは、VirtualStore にリダイレクトされます。

この時点で、問題のアプリケーションが UAC 対応の 64 ビット プロセスとして実行されるようにするための手順が実行されましたが、仮想化から安全と見なされる場所にユーザー データを移行するという問題にはほとんど対処できません。

この問題のトラブルシューティング中に、複数のユーザー アカウントを処理しているときに、 にあるレガシー パス内でいくつかの変更が行われているC:\Program Files(x86)\MyApp\Dataと同時に、 にある VirtualStore に変更が加えられていることがわかりました%localappdata%\VirtualStore\Programs\MyApp\Data。問題は、ファイル/フォルダーの仮想化が行われているかどうかをどのように検出し、2 つの場所をどのようにマージできるかということです。

編集:問題とその複製方法を詳しく説明しているいくつかのWebサイトを見つけましたが、修正方法を含むものはありません. 有望と思われるファイル属性を定義するこの参照FILE_ATTRIBUTE_VIRTUALを見つけました - どこか思い出せませんが、別の参照を見つけました。リダイレクトのリクエスト。

これらのリンクは問題を説明しています:

http://www.c-sharpcorner.com/uploadfile/GemingLeader/windows-file-and-registry-virtualization/

http://www.codeproject.com/Articles/66275/Windows-Vista-File-and-Registry-Virtualization

http://download.microsoftvirtuallabs.com/download/8/a/7/8a71365b-4c80-4e60-8185-8f12f59bf1d4/UACDataRedirection.pdf

4

3 に答える 3

15

簡単ではありませんでしたが、UAC仮想化が有効になっているかどうかを検出する方法を見つけました。情報クラスとして呼び出しGetTokenInformation()て渡すと、TokenVirtualizationEnabledファイルとレジストリの仮想化が有効になっているかどうかが返されます。これを行うためのC関数は次のとおりです。

// Gets whether the current process has UAC virtualization enabled.
// Returns TRUE on success and FALSE on failure.
BOOL GetVirtualizationEnabled(BOOL *enabled) {
    HANDLE token;
    DWORD tmpEnabled;
    DWORD returnLen;
    BOOL retVal = TRUE;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
        return FALSE;

    if(!GetTokenInformation(token, TokenVirtualizationEnabled,
            &tmpEnabled, sizeof(tmpEnabled), &returnLen)) {
        retVal = FALSE;
        goto err;
    }

    *enabled = tmpEnabled;

err:
    CloseHandle(token);

    return retVal;
}

P / Invokeでは少し難しいですが、P/Invokeヘッダーを含めて次のようになります。

enum TOKEN_INFORMATION_CLASS
{
    TokenUser = 1,
    TokenGroups,
    TokenPrivileges,
    TokenOwner,
    TokenPrimaryGroup,
    TokenDefaultDacl,
    TokenSource,
    TokenType,
    TokenImpersonationLevel,
    TokenStatistics,
    TokenRestrictedSids,
    TokenSessionId,
    TokenGroupsAndPrivileges,
    TokenSessionReference,
    TokenSandBoxInert,
    TokenAuditPolicy,
    TokenOrigin,
    TokenElevationType,
    TokenLinkedToken,
    TokenElevation,
    TokenHasRestrictions,
    TokenAccessInformation,
    TokenVirtualizationAllowed,
    TokenVirtualizationEnabled,
    TokenIntegrityLevel,
    TokenUIAccess,
    TokenMandatoryPolicy,
    TokenLogonSid,
    MaxTokenInfoClass
}

public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
public const UInt32 TOKEN_DUPLICATE = 0x0002;
public const UInt32 TOKEN_IMPERSONATE = 0x0004;
public const UInt32 TOKEN_QUERY = 0x0008;
public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
    TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
    TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
    TOKEN_ADJUST_SESSIONID);

[DllImport("advapi32.dll", SetLastError=true)]
static extern bool GetTokenInformation(
    IntPtr TokenHandle,
    TOKEN_INFORMATION_CLASS TokenInformationClass,
    IntPtr TokenInformation,
    int TokenInformationLength,
    out uint ReturnLength);

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
    ref uint TokenInformation, uint TokenInformationLength);

[DllImport("advapi32.dll", SetLastError=true)]
static extern bool OpenProcessToken(IntPtr ProcessHandle,
    uint DesiredAccess, out IntPtr TokenHandle);

[DllImport("kernel32.dll", SetLastError=true)]
    static extern bool CloseHandle(IntPtr hObject);

static bool TryGetVirtualizationEnabled(out bool enabled) {
    IntPtr processHandle = Process.GetCurrentProcess().Handle;
    IntPtr token;
    uint returnLen;
    object tmpEnabled = new uint();

    enabled = false;
    GCHandle handle = GCHandle.Alloc(tmpEnabled, GCHandleType.Pinned);

    try {
        if(!OpenProcessToken(processHandle, TOKEN_QUERY, out token))
            return false;

        try {
            if(!GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenVirtualizationEnabled,
                                    handle.AddrOfPinnedObject(), Marshal.SizeOf(typeof(uint)), out returnLen))
                return false;

            enabled = (uint)tmpEnabled != 0;
        } finally {
            CloseHandle(token);
        }
    } finally {
        handle.Free();
    }

    return true;
}

タスクマネージャーでUAC仮想化のオンとオフを切り替えてみて、正しい結果が返されることを確認しました。仮想化の有効化と無効化は、を呼び出すことで実行できますSetTokenInformation()

Microsoftは、将来のWindowsバージョンでUAC仮想化を削除し、プログラムが既存のUAC仮想化に依存しないようにすることを計画していると述べています。VirtualStoreからAppDataにファイルを移動するUACに対応していない別のプログラムを作成するという誰かの提案を見ましたが、それが良い解決策であるかどうかはわかりません。

于 2013-01-05T04:53:28.507 に答える
1

ローカルアプリのデータパスから「実行中」の場合は、フラグを立てようとしているようです。この場合、次のようになります。

var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);  
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
Console.WriteLine(
    "Current path is:{0}, AppData Path is {1}, Current is subpath of appdata path:{2}",
    currentPath, 
    appDataPath, 
    currentPath.StartsWith(appDataPath)
);
于 2012-12-31T18:43:01.680 に答える