none
DOSアプリケーションで、呼び出し側DOS環境の環境変数を設定・操作する方法 RRS feed

  • 質問

  • お世話になっております。

    厳密にはOSの領域であってVC++と関係ないのかもしれませんが、
    プログラミングを伴うためこちらに投稿させて頂きます。

    コンソールアプリケーション(Win32 Console Application)で、
    呼び出し側DOS環境上へ環境変数を設定する方法はありますでしょうか?

    読み出しは、GetEnvironmentVariable()
    書き込みは、SetEnvironmentVariable()
    のWin32APIで可能かと思います。

    これらの処理結果は自分自身のプロセス内では有効ですが、
    書き込みをした内容は自分自身がExitした際に消えてしまいます。
    DOSの仕組み上そのようになっているのだと思いますが、
    呼び出し元のDOSプロセス(cmd.exe)の環境ブロックに
    呼び出された側のプロセスがアクセスできる方法があれば
    ご教示いただければと思います。

    宜しくお願い致します。
    2009年3月17日 2:49

回答

  • DEKOCHAN の発言:

    お世話になっております。

    厳密にはOSの領域であってVC++と関係ないのかもしれませんが、
    プログラミングを伴うためこちらに投稿させて頂きます。

    コンソールアプリケーション(Win32 Console Application)で、
    呼び出し側DOS環境上へ環境変数を設定する方法はありますでしょうか?

    読み出しは、GetEnvironmentVariable()
    書き込みは、SetEnvironmentVariable()
    のWin32APIで可能かと思います。

    これらの処理結果は自分自身のプロセス内では有効ですが、
    書き込みをした内容は自分自身がExitした際に消えてしまいます。
    DOSの仕組み上そのようになっているのだと思いますが、
    呼び出し元のDOSプロセス(cmd.exe)の環境ブロックに
    呼び出された側のプロセスがアクセスできる方法があれば
    ご教示いただければと思います。

    宜しくお願い致します。


     SetEnvironmentVariable()をMSDNで検索(http://msdn.microsoft.com/ja-jp/library/cc429326.aspx)したところ、しっかりと「この関数は、システム環境変数や他のプロセスの環境変数へ影響を及ぼしません。」と記述されています。

    設定した環境変数を残すためには、以下のレジストリの値を変更する必要があると思います。
    ※当然ですが、当該のレジストリを更新できる権限が必要です。

    ユーザー環境変数
    \HKEY_CURRENT_USER\Environment

    システム環境変数
    \HKEY_LOCAL_MACHINE\CurrentControlSet\Control\Session Manager\Environment


     あと、呼び出されたプロセスではレジストリの内容が変更された後なので、レジストリに登録した環境変数は有効だと思いますが、呼び出し元のプロセスでは、レジストリに登録した内容は関知されないため、呼び出し元プロセスで設定した環境変数を使うためには、GetEnvironmentVariable、SetEnvironmentVariableを使用、もしくはレジストリから値を読み込む必要があると思います。
    • 回答としてマーク sk7474 2009年4月1日 7:10
    2009年3月17日 4:30
  • システム全体の環境変数という意味ではCatTailさんが答えていますが…そういう意味の質問なら「呼び出し元」という表現にはならないと思いました。

    当然ながら子プロセス以外の環境変数を操作することはできません。子プロセスに対してもプロセス起動前にのみ操作できます。
    で、質問の意図ですが、cmdの環境変数を設定することは本当の目的ではありませんよね? (cmd自体はPATHぐらいしか見ませんので)
    BATファイルなどで、cmdが起動する他の子プロセスの環境変数を設定したいのだと解釈し、回答します。

    BATファイル側にコンソールアプリケーションの出力を受け取る意志があるならば(そのようにBATを組めるのなら)手段はあります。
    例えば今日の日付を環境変数MYDATEに保存する場合

    FOR /F %i IN ('DATE /T') DO @SET MYDATE=%i
    ECHO %MYDATE%

    とか。やり出すとコンソールアプリケーション側でBATを生成し、cmd側でそのBATをCALLしてしまうとかもあります。

    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月17日 11:45
  • そうですね。永続的な環境変数なら問題ありませんけど、一時的な環境変数だと問題になりますね。

    もしバッチファイルを使っても良いなら、STARTコマンドを使えば元の環境変数を引き継ぎますので、これを使うのも手です。
    プログラムではなく、コマンドプロンプトでの検証結果ですが、

    呼び出し元のコマンドプロンプト
    SET TEST=テスト
    START CMD

    新しいコマンドプロンプトが表示される
    ECHO %TEST%
    テスト  ←と表示されます。

    あとはDEKOCHANさんが、どのような形で別プロセスを起動するかによると思います。

    • 回答としてマーク sk7474 2009年4月1日 7:10
    2009年3月17日 14:10
  • DEKOCHAN さん の発言:

    コンソールアプリケーション(Win32 Console Application)で、
    呼び出し側DOS環境上へ環境変数を設定する方法はありますでしょうか?

    既に回答がついていますように、できません。
    このため、何が目的だったのか、何がやりたかったのかに論点が移っています。

    今回のご質問自体は実現できないとしても、本当にやりたかったことが見えることで別の解決策が得られる可能性がありますので、是非とも、「(次に)何がやりたかったのか」について情報を出してみて下さい。


    なお、"DOS"と言われていますが、Windows NT系あたりからはコマンドプロンプトやコンソールと呼び、DOSとは呼ばなくなりました。
    基本的にはDOSとは別物ですので、きちんと呼び分けましょう。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月17日 15:00
    モデレータ
  • DEKOCHANさん の発言:

    実現したかった機能ですが
    同一バッチ内における
    DOSコマンド、もといコンソールアプリケーション同士で値(特に文字列)の受け渡しをしたかったのです。
    また、環境変数を利用できれば
    echo コマンドなどで途中で値確認もできるので便利かも、と考えていました。


    「コンソールアプリケーション」というのは自作のアプリケーションでしょうか?
    それでしたら、単純にmain関数への引数として渡すだけでいいような気がするんですが、どうでしょうか?
    戻り値として文字列が必要であれば、標準出力に出力してパイプラインで繋げば良さそうですし。

    というか、環境変数に拘らなくても実現できそうな気がします。
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月18日 11:38
  • 戻り値として文字列が必要であれば、標準出力に出力してパイプラインで繋げば良さそうですし。

    こんな感じですかね。

    [console_out 側]
    #include <iostream>

    int main(int argc, char* argv[])
    {
        std::cout << "test" << std::endl;
        return 0;
    }

    [console_get 側]
    #include <iostream>
    #include <string>

    int main(int argc, char* argv[])
    {
        std::string str;
        std::cin >> str;
        std::cout << str << " received." << std::endl;
        return 0;
    }

    で、

    >console_out | console_get
    test received.

    となります。
    • 編集済み zakio 2009年3月18日 14:13 _tmain -> main
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月18日 14:10

すべての返信

  • DEKOCHAN の発言:

    お世話になっております。

    厳密にはOSの領域であってVC++と関係ないのかもしれませんが、
    プログラミングを伴うためこちらに投稿させて頂きます。

    コンソールアプリケーション(Win32 Console Application)で、
    呼び出し側DOS環境上へ環境変数を設定する方法はありますでしょうか?

    読み出しは、GetEnvironmentVariable()
    書き込みは、SetEnvironmentVariable()
    のWin32APIで可能かと思います。

    これらの処理結果は自分自身のプロセス内では有効ですが、
    書き込みをした内容は自分自身がExitした際に消えてしまいます。
    DOSの仕組み上そのようになっているのだと思いますが、
    呼び出し元のDOSプロセス(cmd.exe)の環境ブロックに
    呼び出された側のプロセスがアクセスできる方法があれば
    ご教示いただければと思います。

    宜しくお願い致します。


     SetEnvironmentVariable()をMSDNで検索(http://msdn.microsoft.com/ja-jp/library/cc429326.aspx)したところ、しっかりと「この関数は、システム環境変数や他のプロセスの環境変数へ影響を及ぼしません。」と記述されています。

    設定した環境変数を残すためには、以下のレジストリの値を変更する必要があると思います。
    ※当然ですが、当該のレジストリを更新できる権限が必要です。

    ユーザー環境変数
    \HKEY_CURRENT_USER\Environment

    システム環境変数
    \HKEY_LOCAL_MACHINE\CurrentControlSet\Control\Session Manager\Environment


     あと、呼び出されたプロセスではレジストリの内容が変更された後なので、レジストリに登録した環境変数は有効だと思いますが、呼び出し元のプロセスでは、レジストリに登録した内容は関知されないため、呼び出し元プロセスで設定した環境変数を使うためには、GetEnvironmentVariable、SetEnvironmentVariableを使用、もしくはレジストリから値を読み込む必要があると思います。
    • 回答としてマーク sk7474 2009年4月1日 7:10
    2009年3月17日 4:30
  • システム全体の環境変数という意味ではCatTailさんが答えていますが…そういう意味の質問なら「呼び出し元」という表現にはならないと思いました。

    当然ながら子プロセス以外の環境変数を操作することはできません。子プロセスに対してもプロセス起動前にのみ操作できます。
    で、質問の意図ですが、cmdの環境変数を設定することは本当の目的ではありませんよね? (cmd自体はPATHぐらいしか見ませんので)
    BATファイルなどで、cmdが起動する他の子プロセスの環境変数を設定したいのだと解釈し、回答します。

    BATファイル側にコンソールアプリケーションの出力を受け取る意志があるならば(そのようにBATを組めるのなら)手段はあります。
    例えば今日の日付を環境変数MYDATEに保存する場合

    FOR /F %i IN ('DATE /T') DO @SET MYDATE=%i
    ECHO %MYDATE%

    とか。やり出すとコンソールアプリケーション側でBATを生成し、cmd側でそのBATをCALLしてしまうとかもあります。

    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月17日 11:45
  • そうですね。永続的な環境変数なら問題ありませんけど、一時的な環境変数だと問題になりますね。

    もしバッチファイルを使っても良いなら、STARTコマンドを使えば元の環境変数を引き継ぎますので、これを使うのも手です。
    プログラムではなく、コマンドプロンプトでの検証結果ですが、

    呼び出し元のコマンドプロンプト
    SET TEST=テスト
    START CMD

    新しいコマンドプロンプトが表示される
    ECHO %TEST%
    テスト  ←と表示されます。

    あとはDEKOCHANさんが、どのような形で別プロセスを起動するかによると思います。

    • 回答としてマーク sk7474 2009年4月1日 7:10
    2009年3月17日 14:10
  • DEKOCHAN さん の発言:

    コンソールアプリケーション(Win32 Console Application)で、
    呼び出し側DOS環境上へ環境変数を設定する方法はありますでしょうか?

    既に回答がついていますように、できません。
    このため、何が目的だったのか、何がやりたかったのかに論点が移っています。

    今回のご質問自体は実現できないとしても、本当にやりたかったことが見えることで別の解決策が得られる可能性がありますので、是非とも、「(次に)何がやりたかったのか」について情報を出してみて下さい。


    なお、"DOS"と言われていますが、Windows NT系あたりからはコマンドプロンプトやコンソールと呼び、DOSとは呼ばなくなりました。
    基本的にはDOSとは別物ですので、きちんと呼び分けましょう。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月17日 15:00
    モデレータ
  • CatTail様 佐祐理様 Azulean様
    どうも有り難う御座います。

    実現したかった機能ですが
    同一バッチ内における
    DOSコマンド、もといコンソールアプリケーション同士で値(特に文字列)の受け渡しをしたかったのです。
    また、環境変数を利用できれば
    echo コマンドなどで途中で値確認もできるので便利かも、と考えていました。

    たとえば、バッチの最初で
    CD-ROMがマウントされているドライブレターを調べて呼び出し側コンソールの環境変数%CDDIR%に格納するコンソールアプリケーションを起動して、
    これに続くコマンドがこの%CDDIR%を利用する
    といったことです。
    場合によってはカレントドライブを移動するプログラムを組めば済むかもしれませんが、
    もう少し手の込んだ処理をするときに環境変数に格納されていたほうが便利です。

    SetEnvironmentVariable()では自分の環境ブロックしか操作できないと思いますが
    何か他の手段でできないものかと思いました。

    ・メモリブロックの構造がはっきり公開されていることが前提で
     GetImageConfigInformation()やWriteProcessMemory()のようなものを利用する。
     危険ですしほぼ実現不可能だとは思うのですけど・・・
    ・PowerShellのようなコンポーネントを利用することで
     (機能があったとして)呼び出し側の環境変数を間接的に操作する。

    など・・・

    バッチが同時に1つしか動かないことと
    自作コマンドだけで利用することを前提にすれば
    こんな危険なことをしなくても
    Tempフォルダ内にiniファイルを作って受け渡しするのが現実的だとは思うのですが...

    2009年3月18日 6:04
  • DEKOCHANさん の発言:

    実現したかった機能ですが
    同一バッチ内における
    DOSコマンド、もといコンソールアプリケーション同士で値(特に文字列)の受け渡しをしたかったのです。
    また、環境変数を利用できれば
    echo コマンドなどで途中で値確認もできるので便利かも、と考えていました。


    「コンソールアプリケーション」というのは自作のアプリケーションでしょうか?
    それでしたら、単純にmain関数への引数として渡すだけでいいような気がするんですが、どうでしょうか?
    戻り値として文字列が必要であれば、標準出力に出力してパイプラインで繋げば良さそうですし。

    というか、環境変数に拘らなくても実現できそうな気がします。
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月18日 11:38
  • 戻り値として文字列が必要であれば、標準出力に出力してパイプラインで繋げば良さそうですし。

    こんな感じですかね。

    [console_out 側]
    #include <iostream>

    int main(int argc, char* argv[])
    {
        std::cout << "test" << std::endl;
        return 0;
    }

    [console_get 側]
    #include <iostream>
    #include <string>

    int main(int argc, char* argv[])
    {
        std::string str;
        std::cin >> str;
        std::cout << str << " received." << std::endl;
        return 0;
    }

    で、

    >console_out | console_get
    test received.

    となります。
    • 編集済み zakio 2009年3月18日 14:13 _tmain -> main
    • 回答としてマーク sk7474 2009年4月1日 7:09
    2009年3月18日 14:10
  • こんにちは。中川俊輔です。

    皆様、回答ありがとうございます。

    DEKOCHANさん、フォーラムのご利用ありがとうございます。
    その後いかがでしょうか?疑問は解決しましたか?

    勝手ながら、有用な情報と思われる回答へ回答マークをつけさせていただきました。

    今後ともフォーラムをよろしくお願いします。
    それでは!
    マイクロソフト株式会社 フォーラム オペレータ 中川 俊輔
    • 編集済み sk7474 2009年4月1日 7:30 編集
    2009年4月1日 7:12
  • 中川様 他皆様

    お世話になっております。

    今回の私の希望例
    ”CD-ROMがマウントされているドライブレターを調べて呼び出し側コンソールの環境変数%CDDIR%に・・・”につきましては
    CD-ROMドライブ探索とカレントドライブ変更を1つの自作アプリケーション内でやってしまえば出来るのですが、
    xcopyコマンドなど2つ以上のときのパイプ処理ができるのかどうかや、
    そもそも自作だけで処理を済ませるなら、わざわざ引数を介する必要がない(直接コマンドを呼び出せばよい)と思っていました。

    本日マイクロソフト様のスクリプトに関するセミナーに参加したのですが
    ITエバンジェリスト安納様より、ずばり環境変数へ入れる解決策をご教示頂きました。

    CD-ROMのドライブレターを標準出力するコマンドを作成した上で
    for /F %i IN ('コマンド') DO SET CDDIR=%i
    とすることで環境変数へ渡す方法です。

    セミナーでは、「スクリプトで出来ないはずがない、という信念が重要」とおっしゃっていました。
    power-shellには、すっかり惚れてしまいました。
    他、WMIなど大変勉強になる素晴らしいセミナーで、とても感謝しております。
    2009年4月2日 18:43
  • こんにちは。中川俊輔です。

    弊社開催のセミナーがお役に立てたようでなによりです。
    また、解決方法を投稿していただき、ありがとうございます。

    セミナーで紹介があったかと思われますが、スクリプトセンターに様々な資料やサンプルが用意されていますので、紹介させていただきます。
    Script Center
    http://www.microsoft.com/technet/scriptcenter/default.mspx (英語)
    http://www.microsoft.com/japan/technet/scriptcenter/default.mspx (日本語)


    それでは!


    マイクロソフト株式会社 フォーラム オペレータ 中川 俊輔
    2009年4月3日 9:51