読者です 読者をやめる 読者になる 読者になる

tech.guitarrapc.cóm

C#, PowerShell, Unity, Cloud, Serverless Technical Update and Features

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

PowerShell

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

Windows8.1環境でSourceTreeがインストールできない?

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

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

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

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

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

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

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

対処方法

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 コード

GitHub

PowerShellUtil / Set-NewUserProfileImagePath / Set-NewUserProfileImagePath.ps1

対象 PowerShell バージョン

PowerShell 4.0*1

コード
#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にするなら

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

こうすることで、%userprofile% と 絶対パスの両方に対して手が打てます。

まとめ

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

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

*1:PowerShell3.0で動作するように書き換えが可能ですが、せっかくなので