音川勝俊
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- type: slide slideOptions: controls: false slideNumber: false progress: true --- # Windows, dotnetの環境変数のバグ --- ## 皆さんこんにちは! :wave: --- <!-- 初めましての方も多いと思いますので自己紹介させて いただきます。 フリーランスのバックエンドエンジニアをやっております 音川といいます。 --> My Profile Otogawa Katsutoshi freelance backend enginner interesting golang, python, and more... twitter account[https://twitter.com/k_otogawa] --- <!-- Setting 舞台 --> ## 突然ですがみなさん!Windowsは好きですか! :sunglasses: --- ## 私はたまに気が狂いそうになります😇 <!-- msの中でも一貫性なかったりね。 --> --- <!-- 今回私が何しに来たか?というと --> ## 今回何しに来たか?というと WindowsはCui, Gui, プログラミング言語由来含めて色んな環境変数の設定方法があります。 しかし、それぞれ実際は違う値が設定されたり、上書きされたりします。 ## 要するに --- ### windowsの環境変数の~~限りなくバグに近い~~仕様があります。 ![布教](https://i.imgur.com/FqNC6gr.png =x400) これをマスターしたら君も立派なdotnet, windowsユーザー --- ## windowsユーザーの地獄の門 windowsユーザーは~~地獄~~天国の門を開いたと思って、聞いて頂けたら幸いです。 ![](https://i.imgur.com/hOjwHcB.jpg) --- <!-- ではそもそも、Windowsの環境変数とは? Linux, macとどう違うのかという説明をしていきます。 --> ## windowsの環境変数とは? ![問題提起](https://i.imgur.com/N4Umxfe.png =x500) --- ## windowsの環境変数にはスコープがある。 以下の3種類のスコープがある。 1. Process -> 現在のプロセスの環境変数。(printenvで見れるやつ) 2. User -> ユーザーの環境変数。(~/.bash_profile) 3. LocalMachine -> rootの環境変数に相当(/root/.bash_profile) <!-- UserとLoaclMachineの環境変数については --> --- ## UserとLoacalMachineの環境変数 最終的に環境変数はレジストリの値に設定される。 ![](https://i.imgur.com/l6J5lDQ.jpg) <!-- つまり --> --- ### 環境変数を設定するということはレジストリに値を設定するということ ![わかった](https://i.imgur.com/qZqaXsA.png =x400) --- ## ここで疑問が生じる ![](https://i.imgur.com/i4RU8hn.png =x500) <!-- さっきのレジストリの画像に不思議なものがあったと思うんですよ。 --> --- ## ここにあるTypeってなんや?! 環境変数に型?! ![](https://i.imgur.com/Sr8RfUq.jpg) <!-- って思われる型も多いと思います。 --> --- <!-- このTypeはレジストリとプログラミング言語で違う名前で呼ばれています。 --> ## レジストリのアプリとプログラミング言語で違う名前 レジストリのアプリ -> Type プログラミング言語(csharp) -> RegistryValueKind <!-- つまり、レジストリには型があります。 --> --- ## RegistryValueKindとは? 以下のどれかの型を表します。 1. String -> ただの文字列 2. ExpandString -> 展開可能(中に変数を埋め込める) 3. MultiString -> Stringの配列 4. DWORD -> 数字 5. Binary -> バイナリ <!-- ExpandStringっていうのは展開可能な文字列で、 MultiStringっていうのは、Stringの配列です。 Dwordは数字です。 今聞いた話で皆さん思ったと思います。 --> --- ### 環境変数を設定するときに型設定したことあったけ? ![](https://i.imgur.com/Ir83eUh.png =x500) --- <!-- という事で一度 --> ### windowsの環境変数の設定方法 <!-- をおさらいしましょう。 --> 以下の三種類が一般的 1. Environment::SetEnvironement ->(dotnetの関数を実行する) 2. Edit environment variable (アプリから変更) 3. setx (コマンド) ![](https://i.imgur.com/txuuMk2.jpg =x500) <!-- この3種類の方法が一般的です。 これを聞いて --> --- <!-- あっここから型を指定するんじゃないか --> ## RegistryValueKindを指定できる? ここからRegistryValueKindを指定して 設定できるんでしょ? ![](https://i.imgur.com/mxqD3dn.png =x400) <!-- そう思われるかと思います。 --> --- ## まさかのすべて指定することができません! どれもRegistryValueKindを引数に取らない。 1. SetEnvironemnt -> 引数にない。 2. Edit environment variable (項目にない) 3. setx (引数にない) --- ### つまりどういう事? ![どういうこと?](https://i.imgur.com/MtiS4gh.png =x500) --- ## 特定の条件下で自動的に上書き、設定されます! 1. SetEnvironment -> 常にStringで上書き 2. Edit environment variable -> 環境変数内に%があったらExpandStringで上書き、%が無かったら、Stringで上書き。 3. setx -> 環境変数が%%で囲まれていたらExpandStringで上書き。%に囲まれていなかったらStringで上書き。 <!-- ここの設定を詳しく説明したいんですが、 その前にwindowsの変数の展開について説明します。 --> --- ## windows変数の展開 変数は下記の書き方で展開できる。 <!-- bashでいうecho $varっていう展開で、 %で囲んだら変数の展開を意味します。 --> ```cmd @rem bashで言うecho $var echo %var% ``` つまり%変数%という書式を見つけて、ExpandStringにするなら 指定できなくてもまだマシな処理。 ベストは指定しない場合は、型を変更しないだが... <!-- ということで、各処理を見ていきます。 --> --- ## setx コマンドは環境変数が%%で囲まれていたらExpandString型で上書き。%に囲まれていなかったらString型で上書き。 ```cmd @rem ExpandStringでレジストリに登録 setx ENVNAME %var% @rem %で囲まれていなかったらStringでレジストリに登録 setx ENVNAME %var @rem 普通の値だとStringでレジストリに登録 setx ENVNAME var @rem 環境変数の長さが1024文字以上だと切り捨て setx ENVNAME "too long value" ``` <!-- %っていうのはコマンドプロンプトでwindowsの環境変数を展開するときに 使うシンボルでこれで囲っていたら、展開を意味するっていうのは先ほど説明したとおりです。 なので型を指定できないけど、展開される変数を見てちゃんと適切な型を設定しているので、これは許される処理かなと。 --> --- ## SetEnvironemnt dotnetの関数は常にString型で上書き。 %のパースもしないし、*なんも考えてない*。 ```powershell #ex) 下記はすべてnameという名前でString型でレジストリに登録 [Environment]::SetEnvironment("ENVNAME", "%Value%","User") [Environment]::SetEnvironment("ENVNAME", "%Value", "User") [Environment]::SetEnvironment("ENVNAME", "Value", "User") ``` <!-- だから、展開されるべき変数も普通の値もすべてString型の環境変数として上書きされます。 結構ヤバい作りです。 --> --- ## Edit environment variable GUIでは環境変数内に%があったらExpandStringで上書き、%が無かったら、Stringで上書き。*%%で囲わなくてもExpandString扱い*。 *2048文字を超えていたら勝手に切り捨て* | 環境変数名 | 値 | RegistryValueKind | | -------- | -------- | -------- | | ENVNAME | value | String | | ENVNAME | %value | ExpandString | | ENVNAME | %value% | ExpandString | | ENVNAME | 2048文字以上 | 容赦なく切り捨て | --- ## 変数が展開されるかどうか?は何も考えてない %が一個でもあったらそれは変数の展開やろと、ExpandString扱いするのでプログラムとしてはかなりお行儀が悪い。 普通だったら、実際パースしてみて囲っていなかったら、エラーにするかRegistryValueKindを設定させるか、どちらかにするのが普通の設計。 <!-- だから変数が展開されるかどうか?は何も考えてなくて、 %が一個でもあったらそれは変数の展開やろと、ExpandString扱いするのでプログラムとしてはかなりお行儀が悪い。 普通だったら、実際パースしてみて囲っていなかったら、エラーにするかRegistryValueKindを設定させるか、どちらかにするのが普通の設計。 --> <!-- ここまで3つ説明して --> --- ## まさかの動作に一貫性が無い... この中ではsetxが一番マシ。 <!-- でヤバすぎるのが --> --- ## ただ、setxには~~バグみてぇな~~厳しい仕様がある 環境変数の長さが1024文字を超えていたら、警告を出して問答無用で勝手に切り捨て。 普通にwindowsで開発していたら超える。 --- ## 警告したからバグじゃないよ。もう消したけどな。 <!-- windows serverとか、多分サーバー用とで使っているところは環境変数追加することはあっても不要なやつバシバシ消す運用になっていないと思うので、世界中で痛い目見ているかなと。 --> ![馬鹿にしている](https://i.imgur.com/01OciRv.png =x500) --- ## 特にヤバいのはSetEnvironmentVariable 例えばPATHに%UserProfile%を追加した後に SetEnvironmentVariableで変更すると 変数展開ができなくなって、パスが通らなくなる。 ```cmd @rem ユーザーディレクトリ配下のbinにパスを通す setx PATH %PATH%;%UserProfile%\bin ``` ```powershell # ExpandStringがStringで上書きされる $value = [Environment]::GetEnvironment("PATH", "User") [Environment]::SetEnvironment("PATH", $value, "User") ``` <!-- これを聞いてみなさんはこう思うと思うんですよ。 --> --- ## そんな壊れている処理、MSは放置しないでしょ? 代替案でカバーするっしょ? ![呆れ](https://i.imgur.com/eoBvcN5.png =x500) <!-- そんな事ないです! --> --- ## MSの色々なアプリ、ソースコードで使われています! 1. githubのdotnet/runtimeのソースコード 2. visual studio のインストーラー <!-- なので、 --> --- ## MSの環境変数の操作は基本的に壊れている Windowsとdotnetの環境変数の扱いは**壊れている**。 ![](https://i.imgur.com/g1tlEBC.png) --- ## できてるっぽいし、リリースしようぜ! MSの*とりあえず*リリースしたいという強い意志が感じられる ![とりあえずやろうぜ](https://i.imgur.com/noMV3ac.jpg) <!-- せめてdotnetの環境変数のバグだけでも治らないの? と思うと思うんですが、 --> --- ## dotnetの環境変数のバグ今後直らないの? dotnetのissue見ても、pwshのissue見てもたびたび議論に上がるが、MSは問題と思っていないので無理。 --- ## MSのバグは闇が深い... このバグ自体は.net framework時代から存在していて、最新のdotnet 7.0でも残っている 公式が直す可能性も望みが**薄い** <!-- 後、これはdotnetのissueで書いてあったんですが、 --> --- ## そもそも一般ユーザーが考える問題じゃなくない? そうだよ! :angry: <!-- ただ、私もプログラマーなんでプルリク出そうぜと思って、 powershellで環境変数を正しく操作できる処理を作りました。 dotnetのチームだと話進まないんで、powershellのチームなら 話通じるかなと思って、 --> --- ## powershellプルリク出したけど Powershellチーム、PMとしては powershellからdotnet由来の方法で設定できるから問題無いとのこと。 ```powershell= [Environment]::GetEnvironment("ENVNAME") [Environment]::SetEnvironment("ENVNAME", "VALUE") ``` コマンドレットを提供するとしても、この関数のラッパーになるから、作るとしてもPSGalleryに追加が妥当との事 <!-- PSGallerytとはPowershellのライブラリをまとめているサイトです。 これはその時だけこういう対応したとかでなくて、 Powershellから環境変数いじるならSetEnvironment使えるっていうのは、度々いろんなMSの人間がこの回答をしています。 --> --- ## いや、そもそもこの関数にバグがあるんやけど ![](https://i.imgur.com/UHUdEGd.png =x400) もちろんそういう反論したし、いままで皆さんに説明したのと同じ説明しました。しかし、リジェクトされました。 <!-- よって、 --> --- ### 今後公式として環境変数を正しく触れる処理は提供されません! さすが、俺達のMS!俺達にできないことを平然とやってのける! ![](https://i.imgur.com/NY30cX4.png =x500) --- ## ちなみにPSGalleryは PSGalleryで環境変数いじるライブラリはあるが、 すべてSetEnvironmentVariableを使っているため、 *すべて処理が壊れています。* *使っちゃダメです。* ![](https://i.imgur.com/ry1ODpU.png =x400) <!-- ダメダメな状態を説明するだけだと よくないので、解決策も述べます。 --> --- ## 今の範囲でできることは? という事で今の我々にできる事を述べていきます。 --- ## 安全に変更する方法はレジストリを操作するしかない。 この2つのみ 1. GUIのレジストリエディタ 2. pwshのSet-ItemProperty ただし、MSが環境変数であることを想定していない DWORD, Binaryなども設定できてしまう。 --- ## powershellのレジストリ操作 Set-ItemPropertyなら下記のように安全に指定できる ```powershell # Set-ItemPropertyは自動的に型を上書きしない。String型ならStringのまま。 Set-ItemProperty "HKCU:\Environment\" -Name ENVNAME -Value "%VALUE%" # Set-ItemProperty型を上書きしたい場合は指定する Set-ItemProperty "HKCU:\Environment\" -Name ENVNAME -Value "%VALUE%" -Type ExpandString ``` <!-- 特に指定が無い場合はString型ならString型のまま設定されますし、 指定したいなら、Typeを指定できます。 --> --- <!-- Powershellでなくて、csharpで安全に書く場合も 同様にレジストリを操作することになりますが、ソースコードは 長くなります。 というわけで --> ## まとめ <!-- に入らせて頂きますと、 --> 1. Windowsの環境変数を安全に操作するならレジストリを触る必要がある。 <!-- 今回プレゼンした問題はUserとLocalMachineの環境変数のみの問題なので --> 2. 現在のプロセスの環境変数の変更ならSetEnvironmentでもよい。 <!-- という事だけ覚えて頂けたら幸いです。 ご清聴ありがとうございました。 --> ![](https://i.imgur.com/c1s1RCE.png =x500) <!-- --> ---

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully