それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

windows で arp テーブルを追加削除する。ダイナミックをスタティックにする。

windowsARPを弄りたいと思ったので調べた。

以下はUACのエレベーションが必要なコマンドとなる。

arp をスタティックにしたい

arp をダイナミックから、スタティックに変えたい。

arp -d 192.168.1.7
arp -s 192.168.1.7 70-85-c2-xx-cf-xx

arp エントリを削除したい

削除する

arp -d 192.168.1.10

arp エントリを追加したい(スタティック)

既存エントリがなければ追加される。

arp -s 192.168.1.10  MA-C0-AD-DR-ES-SS

arp エントリを追加したい(ダイナミック)

ダイナミックはその名の通り、動的に追加されるのでLAN内部にブロードキャストして、応答をもらう方法しか無いと思う

ping 192.168.1.10

wake on LAN で使う

ARP管理なんて何に使うの?と思うかもしれないが、WOLWake On Lanするときに、応答しないホストに対して、パケットを流したいときに使う。

最近のWindowsと一般的なPCはSSH接続やSMB接続をすれば、WOLするので、簡単に起こせる。

windows 7 バックアップをコマンドから起動する

windows 7のバックアップ

設定にはリンクだけが存在する

コントロールパネルにはWin7スタイルのバックアップが存在する。

この機能をコマンドから起動したい。

wbadmin コマンドでバックアップを取る

wdadmin は windows backup admin の略だと思う

バックアップを取る例

ネットワーク先にすべてのバックアップを取る。

wbadmin start backup -backupTarget:\\192.168.12.7\backup -include:C: -allCritical  -quiet

ドライブを指定する場合

wbadmin start backup -backupTarget:X:-include:C: -allCritical  -quiet

ネットワーク先にパスワードを指定する

wbadmin start backup -backupTarget:\\192.168.12.7\backup -include:C: -allCritical -user:YourUsername -password:YourPassword -quiet

ただし、パスワードを平文で書いたら、管理が面倒くさいので、Windows Credential Manager (windows 資格情報管理)を使って事前に保存するのがよい

または、先に、net use コマンドで接続しておく

net use コマンドで接続後にバックアップ

net use \\192.168.12.7\backup
wbadmin  start backup -backupTarget:\\192.168.12.7\backup -include:C: -allCritical  -quiet 
net use /delete * 

net use コマンドでドライブを割り当ててバックアップ

net use Z: \\192.168.12.7\backup
wbadmin  start backup -backupTarget:Z:\ -include:C: -allCritical  -quiet 
net use /delete * 

Wbadmin の利点

wbadmin でバックアップを取ると嬉しい。

進捗がリアルタイムでわかる。

コントロールパネルの画面を使わなくても進捗がリアルタイムで把握できる。

ほかにも次のような利点が生まれる。

コマンドならではのメリットを得られる。

rsync よりもいい

Rsync などと違い、ファイルをロックしない(スナップショット利用する)ので、作業を妨害しない。

先程のスクリーンショットを見ればわかるが、シャドウコピー(スナップショット)を準備してからバックアップしているのがわかる。スナップショットを経由するので、バックアップ中にファイルが書き換わってもスナップショットがあるためバックアップ開始時点のファイルがバックアップされている。

ssh で外マシンから起動できる

SSH経由で起動できる。sshWindowsに入って起動すればいい

処理をバッチ化しやすい

バックアップ前後に処理を挟める(net use の例でわかる)

まとめ

wbadmin でコマンドを使ったバックアップのほうが圧倒的に使いやすいと分る、

windows バックアップの変遷

いまのWindows10・11は本当にカオスで

  • win7 時代のバックアップツール
    • ファイル履歴(バックアップ)ツール
    • システムイメージ作成(バックアップ)ツール
    • リカバリメディア作成ツール(WinRE)
  • Win10 時代のバックアップツール
    • 設定ー>バックアップー>ファイル履歴(中身はWin7のファイル履歴と同じ)
    • OneDrive バックアップ(ファイル履歴)
  • win11 時代のバックアップ
    • Windows Backup(ウインドウズ・バックアップ MSアカウント紐づけ とこのような変遷をたどっているようですね。

windows バックアップ

これは、Win10/11に搭載されているWindows Backup

一見するとちゃんと動きそうですが。

MS アカウントが必要です。

Windows自体をログインしないと動きません

しかも、システム全体のバックアップを取ってくれません。

しかも、預けた内容はWEBで見られます。暗号化の解除キーもMSに取られているのです。

これはポエム

これは完全にポエムなのですが。

クラウドストレージは「もはや安全ではありません」

Google関連社がYouTubeの管理者権限で未公開動画にアクセスして事前に情報を流出させていたと判明、任天堂や著名人が被害に

https://gigazine.net/news/20240604-google-leak-youtube-nintendo-private-movies/ (原題は社員ですが、委託先が正しいので変更)

このように、クラウドストレージは「見られている」と思って行動するべきです。そもそゼロトラストセキュリティってそうですよね。暗号化と証明書以外を信じないのです。

他にも法律や行政によってその中身を暴露させられる危険性があります。

クラウドストレージは技術的には安全性を確保しているが、社員の好奇心や法律によって中身が暴露させる危険性は非常に高いのです。とくにUSAですらこうなのです。USSRの後継国や解放軍の国などは、諦めるしかありません。日本だって警察は令状を裁判所から好き勝手にもらえる現状です。

Bitlockerによる暗号化ドライブがWindowsで一般的になって、iOSがすべてを暗号化した現代において、PC・スマホ本体を押収後にデータ回収が困難です。デジタルフォレンジックといえども容易に閲覧できません。脆弱性を突くような、強引な調査が「法的な証拠能力を持つ」なのか、今後の判決が待たれます。

面倒を避けたいので、そのため、捜査機関は本人による解錠を迫っています。もしそれも拒否した場合は証拠隠滅に関する罪に問われることもありますが、今後はクラウドストレージ業者へ捜査機関への情報提供が行われるでしょう。そして、暗号化禁止が立法府で議論されることでしょう。

そういうことから、本人の意思とは無関係に、クラウドデータは漏洩されます。本人の許諾なく、捜査機関に提供(漏洩)する懸念が強く出てきました。政治権力や裁判所によってクラウドストレージは暴露させられる時代になりました。

ましてやGoogleiCloudのように、クラウドストレージ事業者が令状によりデータを漏洩(提供)する事例も多数出てきました。

技術的には安全なクラウド・バックアップであったとしても、司法の要請や世間の声の前には鍵を預けた状態の暗号化など無力です。

そのような時代を目前にして、「クラウド・バックアップ」なんて代物を平気で使うのはどうかと思います。Appleのように政府とすら対決する姿勢を示していても犯罪者のデータ(iMessageログなど)は政府に公開していますよね。ましてやGoogleMicrosoftクラウド・バックアップを信用するなんてもっとありえないことではありませんかね。

「やましいことがない」といっても、もし誤認逮捕を受けて痛くもない腹を探られる。「私の無実を証明するのは、私の責任なのです。」

ビットコインのマイニングの逮捕や無限ループでの逮捕事件、遠隔操作の誤認逮捕など捜査機関のサイバー犯罪の取り締まりは、やばい失敗続きです、今後もますますひどくなっていくことと思われます。

クラウド・バックアップを使わない。それだけで安全・安心できるのなら500GBのSSDの3千円程度など、本当に安いものです。

っていうか、クラウド・バックアップを契約するくらいなら、SSD買ってBitlockerでぶっ刺してるほうが安全かつ安価なのだから。

バックアップは手元に持っておくしか方法はないでしょう。クラウドストレージに預ける前に、暗号化してからクラウド保存しかないでしょう。この点においてMSが中身を閲覧できるWindowsBackupは「危険」なのです。

ffmpeg で copy は短縮形が使える。

ffmpeg でビデオとオーディオをコピーして、コンテナを変えたいとき

mp4 -> mkv の例

ffmpeg -i sample.mp4 -codec:a copy -codec:v copy  sample.mkv

copy と書けばもっと楽

2つのコードデックオプションを書くので、一つにまとめて省略できる。

ffmpeg -i sample.mp4 -codec copy  sample.mkv

更に短縮して

ffmpeg -i sample.mp4 -c  copy  sample.mkv

コレは、知っておいたらコマンドを書くのが楽でいい。

ubuntu 20.4 LTSでsnap lxd が起動しなくなった

ubuntu 20.4 LTSでsnap lxd が起動しなくなった

SnapdとLTS は相性が悪い。

SnapdのアップデートとAptのアップデートが独立して走るので、snapが何かのタイミングでzpool 最新版を要求するが、LTSのAptにはそれがなくてつんだ。HWEで追いかける必要があった。知らなかったので焦った。

LXD が起動しなくて泣いた。

LXDが起動しなくなった。

 cat /var/snap/lxd/common/lxd/logs/lxd.log

エラーが出てる

エラーの詳細をみていくと

modinfo zfs | grep version
lxd --debug --group lxd

OVMF_CODE.4MB.fd が見つからないと言われている、名前が変わったのかもしれない。

ln -s /usr/share/OVMF/OVMF_CODE_4M.fd /usr/share/OVMF/OVMF_CODE.4MB.fd

zpool がないとエラーになる。

確認すると、zpool はある

which zpool

しかしzpool関連でエラーになる。

Error: Required tool 'zpool' is missing

バージョンをみてみると

takuya@host:~$ zpool version
zfs-0.8.3-1ubuntu12.17
zfs-kmod-2.1.5-1ubuntu6~22.04.3

snapd の最新版LXDと合わないみたいなので追加で入れることになる。

sudo apt-get install --install-recommends linux-generic-hwe-20.04

ubuntu のHWEをインストールして適用する

takuya@host:~$ sudo apt list linux-generic-hwe*
Listing... Done
linux-generic-hwe-18.04-edge/focal-updates,focal-security 5.4.0.181.179 amd64
linux-generic-hwe-18.04/focal-updates,focal-security 5.4.0.181.179 amd64
linux-generic-hwe-20.04-edge/focal-updates,focal-security 5.15.0.105.115~20.04.1 amd64
linux-generic-hwe-20.04/focal-updates,focal-security,now 5.15.0.105.115~20.04.1 amd64 [installed]

再起動したら解決した。

ubuntu LTS はHWEが前提らしい。

「HWE」は、Ubuntuで使われる用語で、「Hardware Enablement Stack」の略です。これは、特定のUbuntuリリースで新しいハードウェアをサポートするための機能のセットを指します。

カーネル提供は3つに大別されます。

カーネル 種類 備考 GA General Availability 一般公開版(安定版) HWE Hardware Enablement 最新版(安定版) HWE edge Hardware Enablement edge プレビュー版(正式リリース前)

https://ararabo.jp/2018-06-25/?p=6436

ubuntu LTS ももう怖い。

僕が使ってるUbuntuは20.04 や 22.04 のLTSなんですよね。

LTSだから unattended updates (自動アップデート)だけでいいかとおもってたら、SnapdのバージョンはUbuntuに関係なくあがっていくのがあるので、HWEで追加しないといけないようです。

ほんとうに、Snapdは余計な機能になりましたね。

そして、snap 推しのUbuntuとは縁を切ってArchで人柱するか、Debianで安定運用に切り替えていきたいと思います。流石にこれは恐ろしすぎる。

Ubuntuを使う場合は、2年に一度アップデートをしたほうが良いでしょう。

BeautySearch でレコメンドを消して、スッキリしたスタートメニュー

Windows10のスタート・メニューがゴチャゴチャしててめんどくさい。

BeautySearch でレコメンド(おすすめのアプリ)は消せた。

Before

after

suggestedを消すのに必要な設定

ほかにもいくつも設定がある。

TopAppは編集ができる。(要RunasでUAC起動)

使用したソフトウェア

https://github.com/krlvm/BeautySearch

スッキリして嬉しい

Edge 使わないし、Emailも使わないので、本当にスッキリした。

Shift押しながらの右クリックメニュー(Shellex)は認知度が低すぎる。

Windowsでは、Shift+右クリックメニューで表示内容が変わる。 MacOS(旧OSX)では、Optionを押すと右クリックメニューが変わる。

Shift + 右クリック( ShellEx)

右クリックメニュー(コンテキスト・メニュー)はすべて表示される。

右クリックメニュー(Shell)

右クリックメニューは通常使わないものを消しておくとスッキリする。

Shift+右クリック、知名度低すぎ?

色んな人のWindowsの使い方を見る機会があるのだけど、ほとんど認知されていなのが、Shift+右クリックメニュー。

ExcelでさえShift+右クリックを諦めた。

昔は、ExcelのメニューもShift+右クリックがあった。貼り付けオプション(形式選択して貼り付け)が、それだった。エクセルもShift+右クリックだった。しかし、認知されなくて、アイコン化された。

このエクセルのShift+右クリックメニュー廃止が、成功例としてエクスプローラーのメニューの今後の方向性になるのかと思ってたらWin11でそうだった。

win11 の方向性は悪くないが。

Win11ではどうなったのだろうか。というと、更に劣化していそうである。

「ユーザーが右クリックメニューを編集する機能」が必要だったと思う。

個人用PCでは問題ないが、レジストリを触れないため職場PCなどで不自由が生じていることは多分解決しない。

Shift+右クリックメニューで表示されるベンチ入り選手と、右クリックで表示されるスタメンの入れ替え機能がWin11には欲しかった。

アイコン表示じゃないんだよ。本当に必要なのは。

ベンチ入り・スタメン入れ替え機能が欲しかった。

本当にほしいのは、右クリックメニューの編集機能だったと思う。スタメンとベンチ入りと2軍の編集機能だったと思う。

しかし、右クリックメニューのClassesは多岐にわたるため、一般ユーザー向けではないと判断されたのだろうか。レジストリ変更を伴うから仕様変更が困難だと判断されたのだろうか。残念である。

Win10でユーザーチョイスで関連付けを変更ができるようになったのだから、右クリックメニューの編集機能が欲しかったのよねぇ。

Shift+右クリックメニューの編集方法

ShellMenuView というソフトウェアが、手軽に編集をさせてくれる。

https://www.nirsoft.net/utils/shell_menu_view.html

コレを使うと、スタメン落ち・2軍行きを切替え可能だ。

  • 該当項目をSet Extended Mode するとShift+右クリック(スタメン落ち)
  • 該当項目をDisable Selected Item するとメニューから抹消(2軍行き)

とできる。

項目が見つからないときは検索する

スタメン落ちとはShift+右クリックメニューが必要な項目とすることで、Shell->Shellex に変えるレジストリ操作に相当する。

2軍行きとは、メニューを非表示にする操作を意味し、項目は残すが、LegacyDisableキー追加するレジストリ操作に相当する。

LegacyDisableではなく、今どきは HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked にエントリを追加するほうが良いかもしれないが。

レジストリ操作で消せるもの消したい。

レジストリ操作で消せるもの消してスッキリしたい

消せるものは極限まで消しておきたい。

Create Shortcut(ショートカット作成)は消せないし、送るメニューも消すことは出来ない。

レジストリで消せるものは限界まで消しておきたい。

ドキュメント類の制作の邪魔になるメニュー項目。

私は、たまに操作説明手順の書類を作るのだが、右クリックメニューが多すぎると、操作説明手順書が作りにくい。

余計なもの(VLCVSCode)が右クリックメニューにあると、スクショを編集したり、紛らわしい右クリックメニューが入っていると、操作手順を変えたものを説明する必要が出てきて面倒くさい。

スッキリしたメニューがよい

右クリックメニューに気軽に登録してくるマナーの悪いアプリケーションは嫌いです。

私達が本当に必要な右クリックメニュー

私達が本当に「必要な」右クリックメニューとは、ファイルを開くアプリケーションを選べることではないか。

例えば、.gitignore を開けるもの

例えば、package.json を開けるもの

例えば、Zipファイルを開くアプリケーションを選ぶ

こういうことではないか。右クリックメニューにアプリケーションが居座るのって整理出来てなくないか。

レジストリの探索方法

shmnview.exe でも消せない事があるので、そのときはレジストリを探索する。その場合知っておくべきことがある。

関連付けや右クリックメニューは、HKEY_CLASSES_ROOT(HKCR) になるが、HKCRはHKCUとHKCMを合成して表示するように再計算されたもの。

コレを知っておく必要がある。

HKEY_CLASSES_ROOT = HKEY_CURRENT_USER + HKEY_CURRENT_MACHINE

該当項目のなかで、ファイルタイプ以外にDrive , Directory , Directory/Background , DesktopBackground がエクスプローラー用に存在している。

ShellExとShellを入れ替えれば、目的は達成される。

昔は、移動がとてもめんどくさかったが、今は、Powershellのコマンドでとても手軽になった。

例えば、VSCodeが「Drive」のメニューにすら出てるのをやめさせたい。

$from = 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCode' 
$to   = 'HKLM:\SOFTWARE\Classes\Drive\shellex\VSCode'
Move-Item -LiteralPath $from -Destination $to

パーミッションエラー

更に最近は、レジストリUACですら書き換えられない場合がある。

Ps はパーミッションエラーでもキーが存在しないメッセージを返すので、さらに面倒くさい。

Move-Item : Cannot delete a subkey tree because the subkey does not exist.

一つ上の階層を書き換える。

上位階層からサブ(下位)のパーミッションを上書きする。

例えば、HKLM\SOFTWARE\Classes\Drive\shell\Powershell の所有者は、SYSTEMだが、SYSTEMにフルコントロールが存在しない。

TrustedInstaller にはフルコントロールが存在する。

TrustedInstallerになるのはちょっと面倒くさいので。

上位の所有者を書き換えて、サブコンテナも合わせて上書きする。

HKLM\SOFTWARE\Classes\Drive\shell\Powershell の上位だから `HKLM\SOFTWARE\Classes\Drive\shell\ を上書きする。

もとの所有者がSYSTEMの場合に、SYSTEMを上書きするのは手間が多いので、とりあえず適当なユーザー(Adminなど)書き換えて、SYSTEMに戻す。戻すとき、連動してサブツリーの所有者が修正される。

パーミッションを開いて

所有者をAdminなどに書き換えて、

所有者をSYSTEMに戻す。

戻すときに、サブツリー(子孫コンテナ)も合わせて上書きされるので、フルコントロールが手中に返却される。

まとめて消す例

PowershellでまとめてShellExへ移動させる例

foreach( $path in "Drive", "Directory","Directory\Background" ){
  foreach ( $name in "WSL", "PowerShell", "git_gui", "git_shell", "cmd" ) {
    $from = "HKLM:\SOFTWARE\Classes\$path\shell\$name"
    $to   = "HKLM:\SOFTWARE\Classes\$path\shellex\$name"
    Move-Item -LiteralPath $from -Destination $to
  }
}

パーミッションの書き換えも、そのうちにPowershellで作りたい。

7zipでワンタッチ解凍(展開)をしたい。

ダブルクリックでデスクトップに解凍してほしい。

zip ファイルを中身を見て、必要ファイルを取り出すとか面倒くさい。

ダブルクリックで解凍して、エクスプローラーで表示してほしい。

特に、USBメモリやSambaのフォルダを開くときに、ダブルクリックでデスクトップへ展開が重要なのである。

レジストリで操作する。

前に、試したことで、レジストリをイジれれば、好きな箇所にフォルダを作成し展開できることはわかった。

解凍後にエクスプローラーで開けてほしい。

今度の欲求は、解凍後にフォルダが見えないという点だ。

解凍(展開・伸長)後に、エクスプローラーでフォルダを開いてほしい。

レジストリの登録を次のようにしたい。

7-Zip.desktop というProgIdを作り、zipの関連付けを変更する。

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon]
@="\"C:\\Program Files (x86)\\Lhaplus\\LplsIcon.dll\",101"

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command]
@="\"C:\\Users\\takuya\\.app\\7zipToDesktop.exe\" %1"

[HKEY_CURRENT_USER\SOFTWARE\Classes\.zip]
@="7-Zip.desktop"

個々に必要なExeかスクリプトを作る

WSHやPSで書いても良かったんだけど。WindowsだしExe作ってやろうと思って、Windowsアプリケーションを書き始めた。

だいたい、書けた。

展開先の設定画面とかはコンパイルすればいいので、要らない。

ソースコード

シェルスクリプトで頑張ろうと思ったけど、WSHVBScript/JScript の未来が不透明だし、Powershellは面倒くさいし、いまさらcmd.exeを書く気になれなかった。

VisualStudioのC++なら、現代ではMicrosoftが無償で使わせてくれるので、超久しぶり書いたわ。

https://github.com/takuya/win-7zip-onclick/blob/master/7zipToDesktop/7zipToDesktop.cpp

// 7zipToDesktop.cpp : Defines the entry point for the application.
//
// License: GPLv3
// author : github.com/takuya
// modified : 2024-05-29
//
#include<iostream>
#include <Windows.h>
#include <filesystem>
#include <regex>

namespace fs = std::filesystem;
using string = std::string;
using wstring = std::wstring;

fs::path get_desktop() {
    char* env_var = nullptr;
    size_t size;
    _dupenv_s(&env_var, &size, "USERPROFILE");
    if (env_var == nullptr) {
        return "";
    }
    std::string UserProfile(env_var);
    return fs::path(UserProfile) / "Desktop";
}
string get_output_path(string src) {

    std::string dstDir = get_desktop().string();

    fs::path src_path(src);
    fs::path basename = src_path.stem();
    std::regex extensionRegex("\\.\\w+$");
    std::string basename_noext = std::regex_replace(basename.string(), extensionRegex, "");
    fs::path dstPath = fs::path(dstDir) / basename_noext;
    return dstPath.string();
}
void openExplorer(string path) {
    string application("explorer.exe");
    string out_dir = get_output_path(path);
    if (!fs::exists(out_dir)) {
        return;
    }
    HINSTANCE result = ShellExecuteA(nullptr, nullptr,
        application.c_str(),
        out_dir.c_str(),
        NULL,
        SW_SHOWNORMAL
    );
}
int extractToDesktop(string archivePath) {
    fs::path desktopPath = get_desktop();
    if (desktopPath.empty()) {
        return 1;
    }
    string args = archivePath;
    args = " x " + args + " -aos -o" + desktopPath.string() + "\\*";
    string cmd = R"(C:\Program Files\7-Zip\7zG.exe)" + args;
    wstring cmdW(cmd.begin(), cmd.end());
    LPCWSTR lpwargs = cmdW.c_str();
    LPWSTR  lpargs = const_cast<LPWSTR>(lpwargs);

    STARTUPINFO si = {};
    PROCESS_INFORMATION pi = {};
    if (CreateProcessW(NULL, lpargs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    
    }
    else {
        std::cerr << "Failed to execute command." << std::endl;
        return 2;
    }
    return 0;
}


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {

    if (lpCmdLine == NULL) {
        return 1;
    }
    // std::string を std::wstring に変換
    string args = lpCmdLine;
    extractToDesktop(args);
    openExplorer(args);
    return 0;


}

書くときに調べたメモ

Win32なんて超久しぶりなので、一つずつ調べて書いた。

  • シェルコマンドの実行方法
    • CreateProcess
    • std::system
    • ShellExecuteA
  • 環境変数の取得
  • ファイルパスの扱い
    • basename / stem
    • extname / replace extension
    • join / path
  • WinMainを使って画面なしExe
  • namespace
    • using
    • include
  • string
    • wstring と LPCWSTR
    • Raw 書式
    • LPCWSTR と LPCSTR の変換

特に、Windowsアプリケーションは文字列型キャストが本当に面倒くさい。c++で書いても、Cへキャストしないといけない。

文字列型の備忘録( 参考資料 )

Item 8-bit(ANSI) 16-bit ( Wide) Varies
character CHAR WCHAR TCHAR
string LPSTR LPWSTR LPTSTR
string (const) LPCSTR LPCWSTR LPCTSTR

略称がややこしいが、ConstとWideだけ覚えておけばいい。

  • LPCSTR は Long Pointer Const String
  • LPCWSTR は Long Pointer Wide Const String
  • LPSTR は Long Pointer String

CreateProcess の第1引数をいれると、第二引数が無視される仕様なのが、ハマりどころだった。

エクスプローラーの起動

#include<windows.h>
#include<iostream>

int main()
{
    LPCSTR application = "explorer.exe";
    LPCSTR parameters = "C:\\Users\\takuya\\Desktop";
    HINSTANCE result = ShellExecuteA(nullptr,nullptr,
        application, // アプリケーション
        parameters, // 引数
        NULL, // ディレクトリ
        SW_SHOWNORMAL // ウィンドウの表示状態
    );
    if ((int)result <= 32) {
        MessageBoxA(NULL, "explorer.exeの実行に失敗しました。", "エラー", MB_OK | MB_ICONERROR);
    }
    else {
        MessageBoxA(NULL, "explorer.exeは成功しました。", "成功", MB_OK | MB_ICONASTERISK);

    }
}

curl.exe の実行

#include<windows.h>
#include<iostream>

int main()
{
    //
    LPCSTR application = "C:\\Windows\\System32\\curl.exe";
    LPCSTR parameters = " -v https://g.co -o C:\\Users\\takuya\\Desktop\\out.html";
    //
    HINSTANCE result = ShellExecuteA(nullptr,nullptr,
        application, // アプリケーション
        parameters, // 引数
        NULL, // ディレクトリ
        SW_SHOWNORMAL // ウィンドウの表示状態
    );
    return (int)result;

}

ファイル(フルパス)から、拡張子を除去

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    fs::path path(pathString);
    fs::path basename = path.replace_extension();
    std::cout << basename.string() << std::endl;
    return 0;

}

ファイル(フルパス)からファイル名(Basename)を取得

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    fs::path path(pathString);
    fs::path basename = path.stem();
    std::cout << basename.string() << std::endl;
    return 0;

}

ファイル名とディレクトリ名を結合

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    std::string dir = R"(C:\Users\takuya\Desktop)";

    fs::path path(pathString);
    fs::path basename = path.stem();
    fs::path dstPath = fs::path(dir) / basename;
    std::cout << dstPath.string() << std::endl;
    return 0;

}

環境変数の値を取得し、Desktopのパスを取得

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    // 環境変数 USERPROFILE の値を取得
    char* env_var = nullptr;
    size_t size;
    _dupenv_s(&env_var, &size, "USERPROFILE");
    if (env_var == nullptr) {
        return 1;
    }
    std::string UserProfile(env_var);
    fs::path desktopPath = fs::path(UserProfile) / "Desktop";
    std::cout << desktopPath.string() << std::endl;
    return 0;

}

コマンド(エクスプローラー)起動をstd::string から

先程の例は、C++ というより、LPCSTRを使ったCサンプルなので。

もう少し、c++ らしく。

#include<windows.h>
#include<iostream>

namespace fs = std::filesystem;
using string = std::string;
int main()
{
    string application("explorer.exe");
    string parameters = R"(C:\Windows)";
    HINSTANCE result = ShellExecuteA(nullptr, nullptr,
        application.c_str(),
        parameters.c_str(), 
        NULL, 
        SW_SHOWNORMAL 
    );

    return 0;

}

もっと簡単に、std::systemを使う

#include<string>
using string = std::string;

int main(){
  string application("explorer.exe");
    string parameters = R"(C:\Windows)";
    string cmd = application + " " + parameters;
    std::system(cmd.c_str());
}

プロセス実行して待つ

system は、終了待ちする。それいいけど、CreateProcess系Windowsの正統派かもしれない。

Windowsの文字列変換が全然わからないので、不慣れなWindowsプログラミングでCreateProcessは文字列変換が地獄かもしれない。

#include<iostream>
#include <Windows.h>

//namespace fs = std::filesystem;
using string = std::string;
using wstring = std::wstring;
int main()
{
    LPCWSTR programPath = L"C:\\Windows\\System32\\cmd.exe";
    LPWSTR  args = const_cast<LPWSTR>(L"/c echo Hello world!");

    STARTUPINFO si = {};
    PROCESS_INFORMATION pi = {};
    if (CreateProcessW(programPath, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    else {
        std::cerr << "Failed to execute command." << std::endl;
    }
    return 0;
}

何も起きない(ウィンドウ開かない)アプリケーション

#include <windows.h>

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){
    return 0;
}

メッセージボックスだけ

#include <windows.h>
#include <iostream>

using wstring = std::wstring;


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){

    wstring msg = L"ハローワールド";
    MessageBox(NULL, msg.c_str(), L"Command Line Arguments", MB_OK | MB_ICONINFORMATION);
    return 0;
}

WinMainでコマンドラインの引数を取ってメッセージボックスにだす

#include <windows.h>
#include <iostream>

using string = std::string;
using wstring = std::wstring;


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){
    // 引数無しは、動作無し。
    if (lpCmdLine == NULL) {
        return 1;
    }
    // std::string を std::wstring に変換
    string commandLineStr(lpCmdLine);
    wstring commandLine = std::wstring(commandLineStr.begin(), commandLineStr.end());
    MessageBox(NULL, msg.c_str(), L"Command Line Arguments", MB_OK | MB_ICONINFORMATION);

    return 0;
}

参考資料

nginx の proxy_redirectでlocationヘッダを書き換える

nginx の proxy_redirectを知りました。

Locationを書き換えることができます。

シンタックス

proxy_redirect from to 

実例

proxy_redirect http://localhost:800 https://$server_name

効果

nginx のリバプロ先からLocation・Refreshが返されたとき、その転送先のアドレスを書き換える。

殆どの場合は、リダイレクトはnginxが書き換えてくれるが、websocketやポート番号が異なると書き換えてくれないので、sub_filterや proxy_redirectを使って書き換える必要がある。

正規表現も使える。基本的に proxy pass reverse 的な動きをする。ただし、いつもの通りコンテンツ(Body)は書き換えの対象外。

コンテンツ内部を書き換えるなら、別の方法(sub_filter, body_filter_by_lua_block)を取る。

zip(圧縮)ファイルを、ダブルクリックで指定フォルダに展開したい。

lhapuls の指定フォルダに解凍(展開)が便利だった。

lhapuls は死にました. zip ファイルは存命ですが、ソフトウェアが脆弱性や、その他理由で絶滅しそうです。

lhapuls はソフトウェアとしては、機能が他に比べて劣るところもあるが、「指定フォルダに展開」して「エクスプローラーで開く」機能が便利で愛用していた。

ほかのソフトウェアでは、ネットワーク(SMB)のZipを開いて展開すると、同一フォルダ内部に書き込みに行く。そのため、ネットワークフォルダや、USBドライブでは速度が遅くなったり書き込みエラーで止まったりで大変不便だった。

ダブルクリックで、指定箇所に展開してくれるのが本当に便利だった。

ダブルクリックで指定フォルダに解凍(展開)を7-Zip で実現したい。

ダブルクリックで、デスクトップへzip ファイルを展開したい。

そのために、レジストリを作成する。

レジストリを構成する。

関連付けの変更

CURRENT_USERの.zip の関連付け先を 7-zip.desktop(これから作る) という名前にする。

ProgID 7-zip.desktop の作成

7-zip.desktopを作成する

次のキーを順番に作る。

HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command

コマンドを指定する。

作った、command キーHKCU\SOFTWARE\Classes\7-Zip.desktop\shell\open\command のdefault値に、次のようにコマンドを入れる。

"C:\Program Files\7-Zip\7zG.exe" x "%1" -oC:\Users\takuya\Desktop\*

コマンド指定のポイントは、-oパス名\* ( -o のあとにスペースなし)である。-o の直後のスペースは不要です。x は 展開を指定します。%1 はファイルパスが来ます。%1 はスペース付きのファイル名が来るので、ダブルクォーテーションで囲みます。

アイコンも指定しておく

アイコン用のキーを作成する

HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon

デフォルト値に、次の値を入れた

"C:\Program Files (x86)\Lhaplus\LplsIcon.dll",101

今回は、サンプルのため、Lhaplus のアイコンを採用したが、別になんでもいい。

ダブルクリックで実行してみる。

ダブルクリックで実行して、デスクトップへ展開されることを確認。

レジストの例(コピペ用)

ProgIDのほう

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon]
@="\"C:\\Program Files (x86)\\Lhaplus\\LplsIcon.dll\",101"

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command]
@="\"C:\\Program Files\\7-Zip\\7zG.exe\" x \"%1\" -oC:\\Users\\takuya\\Desktop\\*"

関連付けの方

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\.zip]
@="7-Zip.desktop"

快適になった

コレで、ある程度快適である。

USBメモリ内部でZip展開が行われて「ああああ、、」とならず、ネットワークフォルダで書き込みパーミッション無しで怒られることもなく、ダブルクリックでビューワーが開いてめんどうにもならない。

ただ、展開後のフォルダがエクスプローラーで開いてくれないのが難点である。

そこまで解決するには、PS1(パワーシェル)を作るか、exeをサクッと作るか、batファイル化、WSHを施さなくてはいけない。それは次回に持ち越し。

参考資料

僕の他にもダブルクリックで展開してくれよ。って思う人がいた。

AdguardHome のDNSキャッシュをパージ(クリア)をAPIからサクッと行う。

Adguard のキャシュ削除をやりたいと思った。

aguard home にはAPIが用意されている

https://github.com/AdguardTeam/AdGuardHome/blob/master/openapi/openapi.yaml

adguard のAPIを呼び出すcurl スクリプト

サクッと書いたら、こんな感じ。プログラミングするまでもないや。

adg-api.sh

#!/usr/bin/env bash

adg_pass=PASSWORD
adg_user=ADMIN_USER
adg_host=ADGUARD_HOME_IPv4

function adg_api(){
  adg_host=$1
  path=$2
  data=$3
  if [[ ! -z $data ]] ; then
    ret=$(curl --show-error --fail -s -X POST -H 'Content-type: application/json' -u $adg_user:$adg_pass -d "$data"  http://$adg_host/control$path );
    if [[ $? == 0 ]] ;then
      echo Success. >>/dev/stderr
      echo { \"result\" : \"$ret\"};
    else
      echo Failed.
      echo $ret
    fi
  else
    curl -s -X GET -u $adg_user:$adg_pass http://$adg_host/control$path
  fi

}

function main(){
  api_path=$1
  post_data=$2
  adg_api $adg_host $api_path $post_data
}

main $@;

API リクエストを叩けば、軽快に操作できる。

使い方(POST)

./adg-api.sh '/cache_clear' 'dummy'         

使い方例 (GET)

./adg-api.sh '/dns_info'

API を叩けばなにかするときになにかできる。

例えば、DDNSを更新したときにキャッシュクリアするとかね。

gitミラーリングを試す。gitlab の特定ブランチをgithub の指定ブランチへミラーリングしたい

gitlab の特定ブランチをgithub の指定ブランチへミラーリングしたい

開発中では、適当にコミットして適当にプッシュしたくないですか?

自分の作業内容がある程度まとまって git rebase などでコミットをまとめてから ブランチに切り出して、プッシュしたいじゃないですか。

作業内容のブランチでコミットメッセージを考えるのが面倒じゃないですか。

ローカル・ブランチでいいと思うんだけど、ローカルだけにデータ持つのが怖いじゃないですか。

mirror ブランチへpush したら github にプッシュしてほしい。

自動でブランチをミラーリングすればいいじゃないか。

そこで、作業用にはGitLabをつかって、見せられる状態に整理し、特定ブランチにプッシュ。そしたら Github にプッシュしたらいいじゃないですか。

それ、ローカルだけでできるじゃん?

2つレポジトリを作っておいて。

git remote add my-work ssh://my-git.example.com/takuya/project01
git remote add origin ssh://github.com/takuya/project01
## 作業中のコミット
git commit -m toda-saved
git push my-work work
## 整理したコミットをプッシュ
git push origin revised:branch/patch/1234

でも、レポジトリを使い間違えて、事故が起きやすいじゃないですか。

あと、git clone するたびに レポジトリの設定を書くのが面倒くさいじゃないですか。

そういうこと考えれば、レポジトリを完全に分けたいじゃないですか。

ミラーリングで解決できるか試す。

そこで、gitlab の特定のブランチにpushしたら、github の特定ブランチにミラーリングするようにしただろうだろうか。と思ったわけですよ。

つまり、Gitlab の workflow を使って、Gitlab からgithub の指定ブランチへミラーリングを自動実行できれば、楽になりそうじゃん。

準備

最終的に成果物を設置する github プロジェクト(レポジトリ)を作成。

そこに、デプロイ・キーを書き込み可能で作成する。

鍵を作成して

ssh-keygen -t ecdsa -f ./id_ecdsa
cat ./id_ecdsa.pub | pbcopy

書き込み可能にチェックして登録する

元レポジトリでミラーリングを入れる。

元になるプロジェクト(レポジトリ)をGitLabに作成して、デプロイキーを作成する

元レポジトリでCI/CDを作る

そして、プロジェクトのCI/CDのランナーを有効にして

GitLabでCIをするように設定する。

stages:
  - mirror


copy-to-github:
  stage: mirror
  tags:
      - takuya
  script:
    - echo Start mirroring
    - |
      ssh-keyscan -t rsa github.com >  ./known_hosts
      git switch mirror
      echo $GITHUB_DEPLOY_KEY
      cp $GITHUB_DEPLOY_KEY  id_ecdsa
      chmod 400 id_ecdsa
      (git remote | grep gh ) || git remote add gh git@github.com:takuya/mirror-test.git
      cat ./id_ecdsa
      cat ./known_hosts
      cat .git/config
      git -c core.sshCommand="ssh -i ./id_ecdsa -o UserKnownHostsFile=./known_hosts" push gh mirror
    - echo End mirroring
  only:
    - mirror  

これで、特定の名前(今回はmirror)にpush したら、github にもpush されるわけである。

git remote add origin https://my-git.example.com/takuya/project01
git push origin mirror

なんかすごいめんどくさいけど、github 側のブランチ名が大量に溢れかえることがなくなって非常に便利である。

整理整頓が大事。

大量ブランチが以前未整理の社内githubプロジェクトとか、高頻度で見かけますものね。

ブランチ名前を変えてプッシュ

名前を変えてブランチにプッシュしたいときは

git でローカルブランチを別名でpushする方法を使えばいい。

git push origin local_name:remoteName

ただ、この方法にも欠点があって、github~/.gitlab-ci.ymlが同期されてしまう。これはちょっと問題ですね。

gitlab を使うから、このような問題が発生するのであって、gogs やベアレポジトリ使って、プッシュ時のフック・スクリプトで対応すれば解決すると思うんだよね。っていうか作業用にGitlabのような高機能サーバーは要らんしね。

まとめ

今回使った同期(ミラーリング)のスクリプトのポイントは次の通り

## github.com のサーバー ホスト鍵を登録
ssh-keyscan -t rsa github.com >  ./known_hosts
## デプロイ用のSSH秘密鍵を登録。
cp $GITHUB_DEPLOY_KEY  id_ecdsa && chmod 400 id_ecdsa
## git remote を登録(すでに登録済みなら上書きしない。
(git remote | grep gh ) || git remote add gh git@github.com:takuya/mirror-test.git
## ミラーリングしたいブランチに切り替える。
git switch mirror
## git -c で ssh オプションを指定してい鍵とホスト鍵を使う。
git -c core.sshCommand="ssh -i ./id_ecdsa -o UserKnownHostsFile=./known_hosts" push gh mirror:mirror01

特に、git -csshオプションを指定して、ホスト鍵と秘密鍵を指定してる箇所がポイントになると思う。

sshなら、鍵さえクリアすれば、git コマンドを自動実行でpush / pull できるようになるので、自動実行が簡単になると思う。

もしhttps アクセスのときは、credential を作れば解決できるし、もう少し楽かも。

lua でhttp リクエスト

lua でhttp リクエス

local http_request = require("http.request")

-- HTTPリクエストを作成
local headers, stream = http_request.new_from_uri("https://g.co"):go()
local body = stream:get_body_as_string()
if headers:get ":status" ~= "200" then
    error(body)
end
print(body)

追加パッケージのインストール無しでパッケージ管理も使わずに使えた。

なんでだろうと疑問を感じたので、apt を調べたら。なんかapt でインストール済みだったみたい。

現在の apt の lua 環境

sudo apt list '*lua*' --installed  | cut -d '/' -f 1  | grep -v dev

インストールされているパッケージ

liblua5.1-0
liblua5.2-0
liblua5.3-0
libluajit2-5.1-2
libluajit2-5.1-common
libnginx-mod-http-lua
lua-ansicolors
lua-argparse
lua-basexx
lua-binaryheap
lua-bit32
lua-bitop
lua-busted
lua-cgi
lua-cjson
lua-cliargs
lua-compat53
lua-copas
lua-cosmo
lua-coxpcall
lua-cqueues
lua-curl
lua-curses
lua-cyrussasl
lua-dbi-common
lua-dbi-mysql
lua-dbi-postgresql
lua-dbi-sqlite3
lua-discount
lua-dkjson
lua-event
lua-expat
lua-fifo
lua-filesystem
lua-geoip
lua-hamlib
lua-http
lua-iconv
lua-inifile
lua-inotify
lua-inspect
lua-json
lua-ldap
lua-lgi
lua-logging
lua-lpeg-patterns
lua-lpeg
lua-lpty
lua-luaossl
lua-luassert
lua-luv
lua-md5
lua-mediator
lua-messagepack
lua-mmdb
lua-mpack
lua-nvim
lua-penlight
lua-posix
lua-readline
lua-redis
lua-resty-core
lua-resty-lrucache
lua-rex-gnu
lua-rex-onig
lua-rex-pcre2
lua-rex-posix
lua-rex-tre
lua-rings
lua-rrd
lua-say
lua-sec
lua-socket
lua-sql-mysql
lua-sql-odbc
lua-sql-postgres
lua-sql-sqlite3
lua-system
lua-systemd
lua-term
lua-unbound
lua-unit
lua-uri
lua-wsapi-fcgi
lua-wsapi
lua-yaml
lua-zlib
lua5.1
lua5.2-doc
lua5.2

nvim 関連で結構いろいろはいっていたっぽい

ディレクトリがマウントされているか調べる

いまのマウントポイントは、proc ファイルシステムから取得できる。

cat /proc/mounts 

これをgrep すればマウント済みか判定できる

cat /proc/mounts  | grep dir 

また、grep には -qs で結果の true/false だけを取れるので。判定に使える。

ディレクトリ(PATH)がマウントされているか調べる。

dir=/path/to/check
grep -qs $dir /proc/mounts && echo $dir is  mounted 

コレを使うと、シェルスクリプトで便利になる。

dir=/path/to/check
if grep -qs $dir /proc/mounts ; do 
  echo $dir is  mounted 
else 
  mount $dir
if

curl でsocks プロキシを使ってssh先ネットワークから外部接続

curl でsocks プロキシを使って別ネットワークから外に出る。

curl でsocks プロキシを使う。

curl --socks4 127.0.0.1:1080 -v  g.co

socks オプションを使えば、HTTPプロキシではないにしろ、簡単にプロキシを挟める。

socks プロキシ作成(SSH

SOCKSプロ棋士の作成は ssh で瞬殺

 ssh -D 1080 remote-router.lan

これで、127.0.0.1:1080 にSOCKSプロキシが立ち上がるんで、あとはcurl で接続すればおっけ

ssh が通ればプロキシ経由できる。

SSHさえ通れば、プロキシを瞬間で作れてネットーワークを繋げれて外に出られる。

正規表現の\K で1つだけなら後方参照を省略できて便利

\K を使うと、そこまでのマッチ部を省略できる

ip -br a からIPv4 を取り出す例

$ ip -br a | grep eth2
eth2             169.254.70.21/16 fe80::4154:6db4:aec3:32f/64

コレを、\k で書くとこうなる。

ip -br a | grep -oP 'eth2\s+\K[\d.]+'

つまり、後方参照を使わなくても、「前読み」で戦えるわけだ。

php の例

これをphpで使うと。次の用になる。

before( グループ )

<?php 

preg_match_all( '/^eth2\s+([\d.]+)/m', `ip -br a ` , $m);
if ( sizeof ($m) > 0 ){
  var_dump($m[1][0]);
}

after ( \K 利用時 )

<?php
preg_match_all( '/^eth2\s+\K[\d.]+/m', `ip -br a ` , $m);

var_dump($m);

if 文が省略できて便利。

ruby とかでも

#!/usr/bin/env ruby
p `ip -br a `.match(/^eth2\s+([\d.]+)/)
p `ip -br a `.match(/^eth2\s+\K[\d.]+/)

結果はこの様になる。

#<MatchData "eth2             169.254.70.21" 1:"169.254.70.21">
#<MatchData "169.254.70.21">

if文などチェックが簡潔になる。

ネストが減らせて便利。

知ってたら楽、知らなくていいコト。そして、初見殺しである。知らない人にはなぜそうなるか全く想像がつかない間違ったソースコードに見える可能性があって怖いけど便利。

関連資料

Grep に'\K' という便利な書式がある。 - それマグで!