2023年6月2日金曜日

Windows hosts ファイルを編集して名前解決させる。

hosts

Virtual Machine で開発することが圧倒的に多くなってきましたね。
本番環境と開発環境を分けるためにドメインの名前解決を一時的に変更することも多々あります。
そこで必要になるのが hosts の編集です。

hosts を編集して名前解決させるには

これが、なかなかめんどくさいw
1.メモ帳を管理者モードで起動
2.ファイルを開くで、右下のフィルタを「すべてのファイル」に変更。
3.C:\Windows\System32\drivers\etc\hosts を開いて編集。
4.保存。
5.コマンドプロンプトを開いて
6.C:\> ipconfig /flushdns

一日一回とかならまだいいけど、数回実施するとなるとめんどくさいね。
ということで、プログラム書いてみましたw

HostsEditor

今回は、Visual Studio 2022 Comunity の C# Windows Form .NET プロジェクトを作成しました。
Form1 には、保存終了用のツールボタンと、全画面に Dock した TextBox を配置します
TextBox のプロパティは、Multiline=true, MaxLength を大きめに。font も大きめに、程度かな。
Name は editText と付けておきました。

Form1.cs

プログラムは、こんな感じです。
hosts ファイルの位置を保持
全行を読み込んで TextBox に入れて編集可能にする
保存ボタンが押されたら、保存して、CMD.exe で ipconfig /flushdns を実行。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HostsEditor
{
    public partial class Form1 : Form
    {
        internal string HostsFileName { get; set; }
        public Form1()
        {
            InitializeComponent();
        }

        //初期表示
        private void Form1_Load(object sender, EventArgs e)
        {
            // ファイル名
            string system_folder = Environment.GetFolderPath(Environment.SpecialFolder.System);
            string hosts_path = Path.Combine(system_folder, "drivers", "etc");
            HostsFileName = Path.Combine(hosts_path, "hosts");

            System.IO.FileInfo fi = new System.IO.FileInfo(HostsFileName);
            if(4194304 < fi.Length)
            {
                MessageBox.Show("hosts ファイルが大きすぎます。他の手段で編集してください。",
                    "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Close();
            }
            // ファイルを 65,536 byte まで読み込む
            string[] lines = File.ReadAllLines(HostsFileName);
            string text = String.Join("\r\n", lines);
            editText.Text = text;
            // 全選択を解除
            editText.SelectionStart = 0;
        }

        private void closeClick(object sender, EventArgs e)
        {
            try
            {
                // 保存
                string text = editText.Text;
                using (StreamWriter streamWriter = new StreamWriter(HostsFileName))
                {
                    // Writeメソッドで文字列データを書き込む
                    streamWriter.Write(text);
                    // StreamWriterオブジェクトを閉じる
                    streamWriter.Close();
                }
                // ipconfig /flushdns を実行。完了まで待つ。
                System.Diagnostics.Process process = new System.Diagnostics.Process();
                //ComSpec(cmd.exe)のパスを取得して、FileNameプロパティに指定
                process.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
                //出力を読み取れるようにする
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = false;
                //ウィンドウを表示しないようにする
                process.StartInfo.CreateNoWindow = true;
                //コマンドラインを指定("/c"は実行後閉じるために必要)
                process.StartInfo.Arguments = @"/c ipconfig /flushdns";
                //起動
                process.Start();
                //(親プロセス、子プロセスでブロック防止のため)
                process.WaitForExit();
                process.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            // 画面を閉じる
            Close();
        }
    }
}

管理者として起動

プログラムを管理者として起動する必要があるので、Program.cs を編集します。
Main部分に以下の内容を追記します。

        static void Main(string[] args)
        {

            // 管理者権限に昇格させて自分自身を起動する
#if (!DEBUG)
            Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
            var pri = (WindowsPrincipal)Thread.CurrentPrincipal;
            //管理者権限以外での起動なら, 別プロセスで本アプリを起動する
            if (!pri.IsInRole(WindowsBuiltInRole.Administrator))
            {
                var proc = new ProcessStartInfo()
                {
                    WorkingDirectory = Environment.CurrentDirectory,
                    FileName = Assembly.GetEntryAssembly().Location,
                    Verb = "RunAs"
                };

                if (args.Length >= 1)
                    proc.Arguments = string.Join(" ", args);

                //別プロセスで本アプリを起動する
                Process.Start(proc);

                //現在プロセス終了
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
#endif
        }

まとめ

DOBON.NET さんの記事を参照しました。
デバッグ時は、VisualStudio を管理者モードで起動する必要があります。
わりと、楽になりましたw
めでたしめでたし。

2023年5月9日火曜日

チェックボックスがセットされていなくても POST パラメータに含める方法

Form の checkbox が not checked でも POST する

Formの内容をPOSTする際に、セットされていないcheckboxの値が渡されないと困ってしまうことが多いですよね?
ね?ね?w

以下 jQuery を使った(checked じゃないと POST できない)POSTの例

//フォーム入力情報取得
var form_data = new FormData($('#form-name').get(0));
//サーバに渡す。
$.post({
  type: 'POST', 
  url: url,
  data: form_data,
  }).done(function(response){
  }
  //以下略
});

これだとチェックボックスがチェックされている部分だけが渡されてしまいます。
 データベースの boolean 値を保存しようとしても、OFF の場合に値が渡されずに更新されなくなってしまいます。

そこで、こんな関数を作って対応しました。

/**
* フォームの入力情報を取得する。
* checkbox で OFF 状態の情報も含める
*/
GetFormData = function(form_tag)
{
    var tag_data = $(form_tag).get(0);
    var form_data = new FormData(tag_data);
    $(form_tag + ' input:checkbox').each(function(){
      var id = $(this).attr('id');
      var checked = $(this).prop('checked');
      if(!checked)
      {
        // OFF のチェックボックス状態も form_data に含める。
        form_data.append(id, checked);
      }
    });
    return form_data;
}

//フォーム入力情報取得
var form_data = GetFormData('#form-name');
//サーバに渡す。
$.post({
  type: 'POST', 
  url: url,
  data: form_data,
  }).done(function(response){
  }
  //以下略
});

まとめ

みごと、bool 値を更新することができるようになりました。
めでたしめでたし。

つか、なんで FormData は OFF の状態を取り込まない仕様にしてるんだろう・・・
困った仕様ですよね。

2023年3月28日火曜日

PowerShell で Mark of The Web 属性を付加するとエラーになる?

Mark of The Web

Mark of The Web (略して MoTW)は Windows のファイル拡張属性で、インターネットからダウンロードされたファイルに特別なしるしを付けて警告を促せるようにするものです。
メールソフト用のプラグインを作っていますので、この属性を使用するのは至極当然のことで。
というわけで、弊社の製品にも実装しました。

問題に遭遇

例によって、いろいろな問題に遭遇したのですが、特に顕著な問題について、ここで報告しておきます。
C:\Temp フォルダに2つのテキストファイルを置きます。(中身はなんでもいいです)
ファイル名は Test_(1.txt, Test_(2.txt です。

まず、PowerShell を起動して(*0)、以下を実行してみます。

PS C:\Temp> Set-Content -Path "C:\Temp\test_(1.txt" -Stream Zone.Identifier -Encoding oem -NoNewline -Value "[ZoneTransfer]`r`nZoneId=3"

エクスプローラで、プロパティを参照すると、セキュリティ属性が付加されているのがわかります。

プログラムから呼び出してみる

次に、対象ファイルを Test_(2.txt に変えて、同じ動作をするプログラムを実装して、実行してみます。
使用したのは VisualStudio 2022, .NET 4.8 ターゲットの C# コンソールアプリケーションです。
using 行を端折ってますが、デフォルトのものでかまいません。

namespace MoTW
{
    class Program
    {

        static void Main(string[] args)
        {
            string file2 = @"C:\Temp\test_(2.txt";
            setMoTW_byPowerShell(file2);
            Console.ReadLine();
        }

        // set MoTW by PowerShell
        private static bool setMoTW_byPowerShell(string filename)
        {
            bool rc = false;
            string arg_format = "Set-Content -Path \"{0}\" -Stream Zone.Identifier -Encoding oem -NoNewline -Value \"[ZoneTransfer]`r`nZoneId=3\"";
            try
            {
                var psinfo = new ProcessStartInfo();
                psinfo.FileName = "powershell.exe";
                psinfo.UseShellExecute = true;
                psinfo.Arguments = string.Format(arg_format, filename);
                psinfo.CreateNoWindow = true;
                psinfo.WindowStyle = ProcessWindowStyle.Hidden;
                Process proc = Process.Start(psinfo);
                proc.WaitForExit();
                rc = (proc.ExitCode == 0);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                rc = false;
            }
            return rc;
        }
    }
}

Power Shell、普段使わないので、改行の指定方法が '`r`n' だということも今回調べましたよw
で、結果。エクスプローラで Test_(2.txt のプロパティを見るとですね。
MoTW 付いていないんですね、これが。
実際に作ったのは、アドインソフトウェアなので DLL ファイルなんですが、同じように MoTW 付いてくれなくて、本当に困ってしまいました。
ちなみに、ファイル名が Test1.txt, Test2.txt であれば、PowerShell も C# プログラムも正常に MoTW が付加されます。

つまり、'(' の文字が含まれていると、実行プログラムからの呼び出しではうまくいかないんですよ。

メールソフト用のアドインでは

1.添付ファイルをディスク上に保存する。
2.ShellExecuteEx を使って(*1)、保存したファイルを開く。
という動作を実装しているので、MoTW 属性のついていないファイルがそのまま開かれたり実行されてしまうんですね。
ZIP書庫を展開したファイルが実行ファイルだった場合、無条件に実行されてしまうことになってしまいます。
危険な状態です。
とても MoTW 対応です!とは言えませんねw

まとめ

結局、各種記号のいくつかが含まれるファイル名では失敗することがわかったので

1.ファイル名を Base64Url エンコードした名前に変更する。
2.PowerShell で Set-Content する(上記のコード)。
3.ファイル名を Base64Url デコードする。
という、本来やらなくてもいいような処理を実装することで問題を回避できました。
めでたしめでたし。(*2)

*0

MoTW を付加する方法には、「デバイスパス指定子」を使って FileStream を操作し Zone識別ファイルを作成する方法もあります。
わんくま同盟の掲示板で魔界の仮面弁士さんに教えていただきました。
この方法は、.NET 4.6.2 以上でうまくいくのですが、Outlook 用のアドインを .NET 4.6.2 以上で構築してもDLL内では .NET4.6.2以前の場合と同じようにエラーとなってしまうため、利用を断念しました。
Outlook がなにかやらかしてるのだと思います。

*1

保存したファイルを開く処理で、System.Diagnostics.Process を使って Start() すると、MoTW を無視して実行してしまいます。
んで、MoTW 属性も消されてしまいます。
しかたなく WIN32API の ShellExecuteEx で Mask 値を SEE_MASK_NOZONECHECKS がセットされないようにして呼び出すようにしました。

*2

「めでたしめでたし」じゃないんですよ。
多くのウィルスソフトやマルウェアなどがメールを介して感染しています。
弊社のプログラムはこれで問題を解決できましたが、同様の処理を実装しているメール用アドインなんかでは致命的です。

とっても致命的な問題だと思ったので、マイクロソフトに報告しておきました。

2022年12月31日土曜日

Hyper-V でホストから Windows7 のゲストにファイルをコピーする

Hyper-V でホストから Windows 7 のゲストにファイルをコピーする

突如、Windows7 環境のシステムを保守する役割が回ってきまして
でもって、仮想環境を構築しようとしたんですよ。
お客様の環境からコピーしてきた数GBのファイルを転送しようとしたんですが。
Windows 7 だと、Hyper-Vの『拡張セッションモード』というのが使えない!
そこで。SSH接続できるようにして WinSCP でコピーできる環境を作ったので、まとめ。

Windows 7 への SSH インストール(失敗)

Windows 7 用のSSHをインストールする。
ここを参考に
https://github.com/PowerShell/Win32-OpenSSH/releases から msi とか zip をダウンロードしても、PowerShell でエラーになって使えなかった。
ちゃんと、管理者モードで PowerShell を実行してもスクリプト1行目でエラーが出て動かなかった・・・

Windows 7 への SSH インストール(成功)

次は、こちらの記事を見つけて試してみた。 http://sshwindows.sourceforge.net/から[setupssh371-20031015.zip]をダウンロードしてインストーラを実行
お、Windows 7 上で問題なくインストールできました。
標準で C:\Program Files(x86)\OpenSSH にインストールしましたが、完了後は PATH 環境変数もちゃんと設定されていて、問題なく動作してるようです。
ホストからコマンドプロンプトで

 > ssh username@windows7-ip-address

おお、つながった!

WinSCPで接続してみよう!

ちゃんとホストからゲストのドライブにファイルコピーできました!

まとめ

むかーしの Windows 7 のインストールCDからゲストOSを構築したので
ブラウザが Internet Explorer。
これじゃ、上記サイトはアクセスしても何が何だかわからない表示でした(笑
Chrome をインストールしてから対応しました。
めでたしめでたし。

2022年8月18日木曜日

VM上のLinuxをVSCodeでPHPデバッグできなかった

ステップ実行できない!

もう何度も環境構築していて、手慣れているはずのVSCodeでPHPをデバッグする環境を用意していたら、なかなか動いてくれなかったのよ、これが。
簡単に環境を書きますと
ホスト:Windows PC+Visual Studio Code
リモート:VMWare + CentOS7.9
Apache:2.4.6
PHP:7.4.30 Zend Engine v3.4.0
Xdebug:3.1.5
ってな環境で、VM上のソースをデバッグできるようにようにしてます

SSHの設定とか、インストールの手順とかは書きません。悪しからずm(_ _"m)

やったこと(失敗の記録)

WEBを検索すると、以下のような記述が目立ちましたが、これはxdebugの古いバージョン用なので動きません

# vi /etc/php.ini

[xdebug]
zend_extension=/usr/lib64/php/modules/xdebug.so
xdebug.remote_host=192.168.xxx.xxx
xdebug.remote_enable = 1
xdebug.remote_autostart = 1

ちなみに、192.168.xxx.xxxは、VM上のOSのIPアドレスです。

xdebug の新バージョンでの記述

以下のように記述するのが正しいと書いてありました。

# vi /etc/php.ini

[xdebug]
zend_extension=/usr/lib64/php/modules/xdebug.so
xdebug.mode=debug
xdebug.client_host=192.168.xxx.xxx
xdebug.client_port=9003
xdebug.start_with_request=yes
xdebug.discover_client_host=1

動いてくれません・・・

どうやって原因を突き止めればいいか、いろいろ悩みました

phpinfo()を使って、動きを確認してみました。

$ vi /public_html/phpinfo.php

<?php
phpinfo();


このファイルを作ってブラウザで表示し、xdebug の部分を見てみますと・・・

Step Debuggerの部分が disabled になってます。
xdebug.client_hostの部分が localhost になってます。
設定したはずの内容とは異なっています。なぜじゃぁぁぁぁ?
と悩むこと数時間。

xdebug.iniを修正する。

/etc/php.d/15-xdebug.ini ファイルを修正します。

# vi /etc/php.d/15-xdebug.ini

zend_extension=xdebug.so
xdebug.mode=debug
xdebug.client_host=192.168.xxx.xxx
xdebug.client_port=9003
xdebug.start_with_request=yes
xdebug.discover_client_host=1

# service httpd restart

再度 phpinfo()を見てみましょう

Step Debuggerの部分が enabled になってます。
xdebug.client_hostの部分が 192.168.0.xxx と設定どおりになってくれてますね。

無事、VSCode上のブレークポイントで止まってくれるようになりました。(^_^)v
めでたしめでたし。

2022年6月17日金曜日

Outlookで行間の改行が広くなるのを防ぐ

Outlookで作成されて送られてくるHTML形式のメール、行間が広くなってることがありますね。
メールの書式設定で、行間が多めに取られているのが原因のようです。
以下の方法で行間を狭くすることができます。
1.Outlook で「新しいメール」を作成する。
2.メールの本文に適当に何か書く。
3.リボンメニューの「書式設定」タブをクリックし「スタイルの変更」のサブメニューから 「段落の間隔」-「段落間隔なし」または「狭い」をクリック。

4.「スタイルの設定」メニューの「既定に設定」をクリック。

5.どうもこれだけではダメっぽいとの情報がありました。段落も設定したほうがいいようです。

段落前と段落後を「0行」に設定。
行間を「最小値」に設定します。
間隔は「12 pt」にしました。

とりあえず、これで行間を小さくできます。

むーん・・・DIVタグ入れてるのに表示がずれるなぁ・・・

2022年5月9日月曜日

PPAPはメールを受信する側の問題だ。

PPAPが問題なのは、受信側が大変な点ですよ。

PPAP問題についての議論が各所で行われています。
今更ながら、と思いますが、もしご存知でない方は「PPAP問題」で検索してみてください。

で、どのページにも
PPAPは危険とか、クラウドストレージを使うべし、とかの説明が書かれていますね。
ほとんどの記事が、「暗号化添付ファイルを送信する人」の視点で書かれてます。

「PPAP」が問題化した第一の理由は、『「暗号化添付ファイルを受信した人」のめんどくささ』にある!
と思うんですよ。

添付ファイル付きのメールが送られてきて、そのファイルをダブルクリックすると、パスワードの入力を求められます。
しかしながら、パスワードは、そのメールとは別のメールで届けられているものですから、いったんファイルを開くのをキャンセルしてパスワードメールに切り替えて、通知されたパスワードをクリップボードに入れて、んでもって添付ファイルのメールにまた戻ってからファイルを開く、と。

なんとまあ、めんどくさいことでしょう?

なのに、ネット上の記事のほとんどは、送信者側に立った危険性の説明とかクラウドストレージを使えって解決方法のことばかり。

「PPAP」で検索するほとんどの人は、受信したときの面倒を解決できないかって検索してるのに、検索結果には送信する側の人へのアドバイスが書いてあるわけです。
見当違いの事しか書いてないってことですよ。しかも、その後もイライラさせてくれるメールが次々とやってくるわけで・・・
その結果、「PPAP爆発しろ!」ってなるわけです。

送信する側の問題対策よりも、その前に受信側の対策が必要なんだと思います!

さて、そのPPAP問題の(メールを受信する側の)対策について、です。

暗号化添付ファイル付きのメールで迷惑をこうむる受信者ってのは、同じ組織内にもいます。
社外宛のメールであっても、Cc: や Bcc: を使って、同朋送信されてきたメールを受信することになると、同じようにパスワード通知メールからコピペして保存しないと開けません。
めんどくさいですよね。
おんなじ社内ならパスワードなんて入力しなくてもよくならないのかねぇ?

多くのメールソフトって、添付ファイルを開く動作は毎回保存先フォルダを聞いてきます。
でもって、そのフォルダ「マイドキュメント」なんですよねー。
メールそのものは、メールソフトの振り分け機能(仕分け機能)で部署や取引先ごとに振り分けられているのに、保存先だけは毎回「マイドキュメント」
ファイルの保存先を取引先ごとに(振り分けられたメールフォルダごとに)変えられるといいんじゃないですか?

あと、以前受信したメールを再度照会することってありますよね。
取引先『えーと、前に送ったメールの件なんだけど・・・』てなとき、過去のメールを探り出して、その添付ファイルをクリック。
またパスワード入れなきゃならないし、保存先もどこだったかわからないから、とりあえず「マイドキュメント」に保存して対応、と・・・
一度保存したことのある添付ファイルは、クリックだけで開くようにできればいいんじゃないですか?

こういった機能があれば、PPAPメールを受信する側の負担はずいぶん軽くなります。
手間が省けるぶん、時間も短縮できるようになりますから、業務効率も向上します。

あるんです。
そういう便利なソフトがあるんですよ。

Microsoft Outlook を使っている方はこちら。
添付ファイルユーティリティ Biz Attacher for Outlook

Becky! Internet Mail を使っている方はこちら。
添付ファイルユーティリティBiz Attacher for Becky!

両方とも、送信側のPPAPメールの問題点への対策も取られていますよ、と。
お試しでも利用できます。

以上、製品PRでした。