tech.guitarrapc.cóm

Technical updates

PowerShell でユーザー名と異なる userフォルダを自由に設定する

先日、@muu000さんが、このような問題で困っていたそうです。

Windows 8.1環境でSourceTreeがインストールできない?

しかし私は問題なくインストールできます、他の方も問題なかったようです。

どうやら原因は、ユーザーフォルダが2byte文字 = 日本語だったようです。

導かれしむー (@muu000)

今回は、2byte文字のユーザーフォルダができてしまった時に、「ユーザーフォルダのパスだけ変更」してみましょう。

そもそも2Byte文字になぜなったのか

Windows 8やWindows 8.1は、Microsoftアカウントでログインが可能です。(Windows 8.1は必須)

muu000さんは、MSアカウントでこのようにしていたようです。

導かれしむー (@muu000)

そうすると、入力した名前 = 日本語名だったら日本語フォルダでユーザーフォルダが初回ログイン時に生成されます。

対処法法

2Byte文字でのパス解釈が問題になる事例は、古くは石器時代からWindows 8まで言われており、Windows 8.1でも防げていません。

Microsoft アカウントを日本語で作らない

色々しょうがないですが、そもそもMicrosoftアカウントでOSログオン = Microsoftアカウントに日本語を入れないのがベストでしょう。

と、いうと辛いので以下の方法があります。

ユーザーフォルダの参照とフォルダ名を変更する

superuser.com - How to rename user folder in Windows 8?

ようは、以下を求めています。

  • ローカルアカウントで管理者を別途用意する
  • 変更対象のユーザーはサインアウトする
  • 変更作業のため、対象とは別の管理者でログオン
  • 対象ユーザーのフォルダを、新たに利用したいユーザーフォルダ名に変更
  • レジストリHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\のProfileImagePathにユーザーフォルダパスが入っているので、変更したいユーザーフォルダ名に変更

決まりきった作業のために、マウス操作とか辛いですね。

PowerShellで自動実行しましょう。

PowerShell コード

PowerShell 4.0向けに、GitHubにおいておきます。

PowerShellUtil / Set-NewUserProfileImagePath / Set-NewUserProfileImagePath.ps1

#Requires -RunAsAdministrator
#Requires -Version 4.0

function Set-NewUserProfileImagePath
{
<#
.Example
# change username share's userFolder from c:\users\share to c:\users\hoge
Set-NewUserProfileImagePath -user share -currentUserFolderName share -newUserFolderName hoge -Verbose

.Example
# evenif userfolder name not same as username, you can change username share's userFolder from c:\users\hoge to c:\users\share
Set-NewUserProfileImagePath -user share -currentUserFolderName hoge -newUserFolderName share -Verbose

.Example
# with -force switch, you can force input desired imagepath c:\users\share to registry for user share. This never depend on how userfolder is set
Set-NewUserProfileImagePath -user share -currentUserFolderName hoge -newUserFolderName share -Verbose -Foce

#>
    [CmdletBinding()]
    param
    (
        # enter username to be changed
        [parameter(
            mandatory,
            position = 0)]
        [string]
        $user,

        # enter current user folder name to be changed
        [parameter(
            mandatory,
            position = 1)]
        [string]
        $currentUserFolderName,

        # enter new user Folder name change to
        [parameter(
            mandatory,
            position = 2)]
        [string]
        $newUserFolderName,

        # enter new user Folder name change to
        [switch]
        $force
    )

    begin
    {
        # get foler information
        $private:usersFolder = Split-Path $env:USERPROFILE -Parent
        $private:currentUserFolder = Get-ChildItem $usersFolder | where PSISContainer | where Name -eq $currentUserFolderName
        $private:newuserFolder = Join-Path $usersFolder $newUserFolderName

        # get registry information
        $private:registryPath = "registry::HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
        $private:users = Get-CimInstance Win32_UserAccount
        $private:sid = $users.Where({$_.Name -eq $user}).SID

        # set registry information
        $private:regSidDetail = Get-ItemProperty -Path (Join-Path $registryPath $sid)
        $private:currentProfileImagePath = $regSidDetail.ProfileImagePath
        $private:newProfileImagePath = if ($force)
            {
                $newuserFolder
            }
            elseif($currentProfileImagePath -eq $currentUserFolder.FullName)
            {
                $newuserFolder
            }
            else
            {
                $null
            }
    }

    process
    {

        if ($user -ne $env:USERNAME)
        {

            # userFolder change
            Write-Verbose ("Start changing user Folder '{0}' to '{1}'" -f $currentUserFolder.FullName, $newuserFolder)
            if($currentUserFolder.FullName)
            {
                if ($currentUserFolder.FullName -ne $newuserFolder)
                {
                    Rename-Item -Path $currentUserFolder.FullName -NewName $newuserFolder -PassThru -Confirm
                }
                else
                {
                    Write-Warning ("newUserFolder '{0}' detected as same as currentUserFolder '{1}'." -f $newUserFolder, $currentUserFolder.FullName)
                }
            }
            else
            {
                Write-Warning ("currentUserFolder '{0}' detected as null." -f $currentUserFolder.FullName)
            }

            # registry change
            Write-Verbose ("Start changing Registry from '{0}' to {1} , for user '{2}' with sid '{3}'" -f $currentProfileImagePath, $newProfileImagePath, $user, $sid)
            if ($newProfileImagePath)
            {
                if ($newProfileImagePath -ne $currentProfileImagePath)
                {
                    Set-ItemProperty -Path "registry::HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid" -Name ProfileImagePath -Value $newProfileImagePath -Confirm -PassThru
                }
                else
                {
                    Write-Warning ("newProfileImagePath '{0}' detected as same as currentProfileImagePath '{1}'" -f $newProfileImagePath, $currentProfileImagePath)
                }
            }
            else
            {
                Write-Warning ("newProfileImagePath '{0}' detected as null." -f $newProfileImagePath)
            }
        }
        else
        {
            Write-Warning ("Current user '{0}' is same as target user '{1}'. Please execute this command with other user who have admin priviledge"-f $env:USERNAME, $user)
        }
    }

    end
    {
    }
}

-Verboseをつけなくても動作は変わりませんが、付けると今どのような操作しているか表示されます。

利用する前に

最低この手順だけは必要ですの。ファイル共有制限で処理できない場合があるので。

  • ローカルアカウントで管理者を別途用意する
  • 変更対象のユーザーはサインアウトする
  • 変更作業のため、対象とは別の管理者でログオン
  • スクリプト実行

利用例.1

例えば、 ユーザー名shareが存在して、ユーザーフォルダをc:\users\shareからc:\users\hogeに変えたい場合は以下のようにします。

Set-NewUserProfileImagePath -user share -currentUserFolderName share -newUserFolderName hoge -Verbose

利用例.2

例えば、 ユーザー名shareが存在して、ユーザーフォルダがc:\users\hogeだった時、c:\users\shareに変えたい場合は以下のようにします。

Set-NewUserProfileImagePath -user share -currentUserFolderName hoge -newUserFolderName share -Verbose

利用例.3

例えば、 ユーザー名shareが存在して、ユーザーフォルダがc:\users\hogeだった時、c:\users\shareに変えたい場合。 さらにすでにユーザーフォルダが、c:\users\hogeで、レジストリのみc:\users\shareだった場合は、以下でレジストリを強制的に変更可能です。

Set-NewUserProfileImagePath -user share -currentUserFolderName hoge -newUserFolderName share -Verbose -Foce

muu000さんの事例の対応

muu000さんの事例の場合は、以下のやり方で自由に変更可能です。

Set-NewUserProfileImagePath -user loginUserName -currentUserFolderName 日本語フォルダ名 -newUserFolderName 変更したいフォルダ名 -Verbose -force

ただし、%userProfile%対策なので、この変更前にインストールしたアプリケーションで、絶対パスでユーザーフォルダを見てるアプリケーションには通じません。

この場合は、シンボリックリンクでリダイレクトさせます。例えば、hogehogeユーザーのフォルダc:\users\hogehogeを、c:\users\hogeにする場合は以下のようにすると、%userprofile%と絶対パスの両方に対して手が打てます。

Set-NewUserProfileImagePath -user hogehoge -currentUserFolderName hogehoge -newUserFolderName hoge -Verbose
mklink /d C:\users\hoge C:\users\hogehoge

まとめ

なんというか、ニッチなスクリプトを作ってしまいました。

OSインストール直後でもない限り、よほどの理由がない場合はユーザー作り直す方が安全でしょう。