ターミナルはローカルエコーで文字を表示しているわけではなく、ラインディシプリンからのエコーバックを表示している。バックスペースキーは1文字戻って空白で上書きをするというエコーバックが行われる
- ターミナルエミュレーターはキーボード入力時に、文字を表示しているわけではなくラインディシプリン(もしくはアプリケーション)からのエコーバックを表示している
- バックスペースは文字を直前の文字を消すのではなく「バックスペース」「スペース」「バックスペース」という3操作で直前の文字をスペースで上書き(&1文字戻る)している
確認手順 scriptでキー入力を保存して、odでダンプする
123と入力後バックスペース、その後4を入力する
123\b4
# キー入力をscriptコマンドで記録する $ script -q -f -c "cat" output.log
ダンプ出力すると、エコーバックで「バックスペース」「スペース」「バックスペース」が行われることによって、画面上で4が消えることがわかる
1 2 3 \b \b 4 \r \n
# 記録したファイルをダンプ出力する
$ cat output.log | od -tx1 -c
0000100 81 97 e3 81 be e3 81 97 e3 81 9f 0d 0a 0d 0a 31
201 227 343 201 276 343 201 227 343 201 237 \r \n \r \n 1
0000120 32 33 08 20 08 34 0d 0a 31 32 34 0d 0a 5e 43
2 3 \b \b 4 \r \n 1 2 4 \r \n ^ C
Windows Terminal(1.22)でSixcelグラフィックスがサポートされたけど、node環境で画像が出力されない(.exeを付けると解決する)
長らくpreview版だった1.22がついに正式リリースされました(2025/03頃?)
このバージョンではSixelグラフィックスがサポートされるので、ターミナルに画像を表示することができるようになります
Windows Terminalで下記を実行すると、小さな四角が出力されます
$ printf '\x1BPq"1;1;96;96#0;2;255;0;0#1;2;0;255;0#0~#@@@@#~$?#1____\x1B\'

全く同じ文字をnode.jsで出力しても、小さな四角が出力されませんでした
console.log('\x1BPq"1;1;96;96#0;2;255;0;0#1;2;0;255;0#0~#@@@@#~$?#1____\x1B\\');
色々試した結果、nodeに拡張子.exeを付ければよいようです

詳しい原因はわからないのですが、この辺のページを参考にしました
OutputDebugString()を受信するソースをChatGPTに作ってもらった(けど動かなかったので修正した)
ログやデバッグ関連をしらべていて、ふと思い出した"OutputDebugString()"
文字列をデバッガ―に出力してくれるWindowsAPI
ログファイルを作ったり、閉じたりする必要がないのでデバッグ時に重宝していました。
(VB6でも使えるし、.NETでも(System.Diagnostics.Debug.WriteLine())で間接的に利用してた)
ただ、当時はどんな仕組みで送受信しているのか?といった情報がほとんどなかったから詳細がわからなかったのだけど、ふと存在を思い出したのでChatGPT(o3-mini-high)に聞いたら、仕組みと受信するためのコードを教えてくれた
- Windows のデバッグ出力チャネルに送信する API
- システムのデバッグ出力機構を利用して、システム全体でデバッグメッセージを共有する仕組み
- デバッガをアタッチしていなくても、デバッグ出力チャネルにフックすることでメッセージをキャッチできる
- 送信可能な最大文字数の規定はない(null終端まで)が、受信側で固定サイズのバッファを利用していたりするので一般的には4096バイト程度にした方がよい(分割されたり、切り捨てられる可能性がある)
- プロセスにアタッチしてデバッグ出力を受信する場合、デバッグイベント受信後、該当プロセスのアドレス空間を直接参照して読み取る仕組みが必要となる(ReadProcessMemory()等を利用して、別プロセスへの参照が必要となるので管理者権限が必要)
OutputDebugStringを受信するプログラムの流れ
- 共有メモリ領域("DBWIN_BUFFER")を作成します。共有メモリは4Kbytで、最初の4ByteはプロセスID、残りは受信するNull終端文字列です。OutputDebugString()呼び出し側はこの共有メモリ領域にデータを書き込みます。
- 2つのイベント"DBWIN_BUFFER_READY"と、"DBWIN_DATA_READY"を作成します。"DBWIN_BUFFER_READY"は、共有メモリ領域の準備ができたことをOutputDebugString()呼び出し側プロセスへ通知します。"DBWIN_DATA_READY"は、OutputDebugString()呼び出しにより共有メモリへの書き込みが完了したことを受信(WaitForSingleObject())するために利用します
- "DBWIN_BUFFER_READY"イベント シグナル状態にして、共有メモリが使用可能になったことをOutputDebugString()呼び出し側プロセスへ通知します。
- DBWIN_DATA_READYイベントが通知される まで待機(WaitForSingleObject())します。これは、OutputDebugString()が共有メモリに書き込み完了したタイミングで通知されます。
- 共有メモリからプロセス IDと、文字列を抽出します。
- ステップ3に進む
詳しい解説はこちら unixwiz.net
あとで、ChatGPTが作ってくれたサンプルソースを元に動作確認をしてみる
プロセスへアタッチせずに受信するサンプル(追記:下記コードは動作しないので最後に修正版を追記した)
#include <windows.h>
#include <stdio.h>
#define DBWIN_BUFFER_READY TEXT("DBWIN_BUFFER_READY")
#define DBWIN_DATA_READY TEXT("DBWIN_DATA_READY")
#define DBWIN_BUFFER TEXT("DBWIN_BUFFER")
// DBWIN で利用される共有メモリの構造体
struct DBWIN_BUFFER_STRUCT {
DWORD dwProcessId;
char szData[4096];
};
int main(void)
{
// DBWIN_BUFFER_READY イベントを作成(手動リセットではなく自動リセット)
HANDLE hBufferReady = CreateEvent(NULL, FALSE, FALSE, DBWIN_BUFFER_READY);
if (!hBufferReady) {
printf("CreateEvent(DBWIN_BUFFER_READY) failed with error %lu\n", GetLastError());
return 1;
}
// DBWIN_DATA_READY イベントを作成
HANDLE hDataReady = CreateEvent(NULL, FALSE, FALSE, DBWIN_DATA_READY);
if (!hDataReady) {
printf("CreateEvent(DBWIN_DATA_READY) failed with error %lu\n", GetLastError());
return 1;
}
// 共有メモリ領域 DBWIN_BUFFER を作成
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READONLY,
0, sizeof(DBWIN_BUFFER_STRUCT), DBWIN_BUFFER);
if (!hMapFile) {
printf("CreateFileMapping failed with error %lu\n", GetLastError());
return 1;
}
// 共有メモリをマップする
LPVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, sizeof(DBWIN_BUFFER_STRUCT));
if (!pBuf) {
printf("MapViewOfFile failed with error %lu\n", GetLastError());
return 1;
}
DBWIN_BUFFER_STRUCT* pDBWin = (DBWIN_BUFFER_STRUCT*)pBuf;
// 最初にバッファが使用可能であることをシステムに通知
SetEvent(hBufferReady);
printf("Waiting for debug output...\n");
// 無限ループでデバッグ文字列を受信
while (true) {
// DBWIN_DATA_READY イベントがシグナル状態になるのを待つ
DWORD dwWait = WaitForSingleObject(hDataReady, INFINITE);
if (dwWait == WAIT_OBJECT_0) {
// 共有メモリから送信元プロセスIDと文字列を取得
DWORD pid = pDBWin->dwProcessId;
char* msg = pDBWin->szData;
printf("PID %lu: %s\n", pid, msg);
// 次のメッセージ受信のために、バッファの使用可能状態を通知
SetEvent(hBufferReady);
} else {
printf("WaitForSingleObject failed with error %lu\n", GetLastError());
break;
}
}
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
CloseHandle(hDataReady);
CloseHandle(hBufferReady);
return 0;
}
プロセスへアタッチして受信するサンプル
#include <windows.h>
#include <stdio.h>
int main(void) {
DWORD pid = 1234; // アタッチする対象プロセスのIDに置き換える
// 対象プロセスにアタッチ
if (!DebugActiveProcess(pid)) {
printf("Failed to attach to process %lu\n", pid);
return 1;
}
DEBUG_EVENT debugEvent;
while (true) {
// 無限ループでデバッグイベントを待機
if (WaitForDebugEvent(&debugEvent, INFINITE)) {
if (debugEvent.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) {
// OUTPUT_DEBUG_STRING_EVENT の処理
OUTPUT_DEBUG_STRING_INFO &info = debugEvent.u.DebugString;
// 対象プロセスのメモリからデバッグ文字列を読み出す必要がある
// 例: ReadProcessMemory を利用して info.lpDebugStringData から文字列を取得する
printf("Received debug string event from process %lu\n", debugEvent.dwProcessId);
// ※詳細な文字列の取得処理は、対象プロセスのアドレス空間へのアクセスが必要
}
// デバッグイベントの処理が終わったら、対象プロセスを再開
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
} else {
printf("WaitForDebugEvent failed with error %lu\n", GetLastError());
break;
}
}
// アタッチを解除
DebugActiveProcessStop(pid);
return 0;
}
プロセスへアタッチせずに受信するサンプル(修正版)
#include <windows.h>
#include <stdio.h>
// DBWIN(共有メモリの構造体)
struct DBWIN_BUFFER_STRUCT {
DWORD dwProcessId;
char szData[4096 - sizeof(DWORD)];
};
void cleanUp(HANDLE hBufferReady, HANDLE hDataReady = NULL, HANDLE hMapFile = NULL, LPVOID pBuf = NULL)
{
if (!pBuf) { UnmapViewOfFile(pBuf); }
if (!hMapFile) { CloseHandle(hMapFile); }
if (!hDataReady) { CloseHandle(hDataReady); }
if (!hBufferReady) { CloseHandle(hBufferReady); }
}
int main(void)
{
// DBWIN_BUFFER_READY イベントを作成(自動リセットではなく手動リセット)
HANDLE hBufferReady = CreateEvent(NULL, TRUE, FALSE, TEXT("DBWIN_BUFFER_READY"));
if (!hBufferReady) {
printf("CreateEvent(DBWIN_BUFFER_READY) failed with error %lu\n", GetLastError());
return 1;
}
// DBWIN_DATA_READY イベントを作成
HANDLE hDataReady = CreateEvent(NULL, FALSE, FALSE, TEXT("DBWIN_DATA_READY"));
if (!hDataReady) {
printf("CreateEvent(DBWIN_DATA_READY) failed with error %lu\n", GetLastError());
cleanUp(hBufferReady);
return 1;
}
// 共有メモリ領域 DBWIN_BUFFER を作成
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, sizeof(DBWIN_BUFFER_STRUCT), TEXT("DBWIN_BUFFER"));
if (!hMapFile) {
printf("CreateFileMapping failed with error %lu\n", GetLastError());
cleanUp(hBufferReady, hDataReady);
return 1;
}
// バッファが使用可能になったことを通知
SetEvent(hBufferReady);
// 共有メモリをマップする
LPVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, sizeof(DBWIN_BUFFER_STRUCT));
if (!pBuf) {
printf("MapViewOfFile failed with error %lu\n", GetLastError());
cleanUp(hBufferReady, hDataReady, hMapFile);
return 1;
}
printf("Waiting for debug output...\n");
// 無限ループでデバッグ文字列を受信
while (true) {
// DBWIN_DATA_READY イベントがシグナル状態になるのを待つ
DWORD dwWait = WaitForSingleObject(hDataReady, INFINITE);
if (dwWait == WAIT_OBJECT_0) {
// 共有メモリから送信元プロセスIDと文字列を取得
DBWIN_BUFFER_STRUCT* pDBWin = reinterpret_cast<DBWIN_BUFFER_STRUCT*>(pBuf);
DWORD pid = pDBWin->dwProcessId;
char* msg = pDBWin->szData;
printf("PID %lu: %s", pid, msg);
}
}
cleanUp(hBufferReady, hDataReady, hMapFile, pBuf);
return 0;
}
個人事業主型開発からの脱却を読んで思ったことメモ
似たような状況だなと思い興味深く読みました。 違う点は - タスクにアサイン組み換えは発生しない。 - マストの締め切りが少ない代わりに優先度が見えずいつまでも終わらないタスクが残る といったあたりか
個人事業主型開発とは 個人事業主型開発の特徴として以下がある
このあたりは同じ
実際に起こること
- タスクがずるずる遅れる ⇒ 重要なタスクは遅れないようにしたい
- 指示者が全員を管理するのが大変 ⇒ 重要なタスクだけに管理するようにしたい
- お互い何やってるのか知らないので協力ができない ⇒ 気軽に協力できる雰囲気にしたい
マストの締め切りが少ない、タスク間の依存が少ないのでちょっと状況が違う
こんな感じにしたい
- 細かいタスクは誰がアサインする?(自分で取っていく形式にする) ⇒ 大量のタスクを棚卸して、やりたいタスク自己申告する形(特になければ今まで通り、適切に割り振る)
- (優先度ではなく)重要度?みたいな軸をつけて、着手は重要度で
重要度を明確にして、重要タスクはマイルストーンを切ってちゃんと管理する(他はまかせる)くらいがちょうどいいかなと(思うので、その方向に動かす)
wsl2のUbuntuにphp7.3をインストール際の「retrieving gpg key timed out.」エラー
を参考にphp7.3をインストールしようとしたが、途中でエラーになるので対応方法をメモ
- エラー内容(インストールが止まるためCtrl+Cで止めた)
sudo add-apt-repository ppa:ondrej/php ~中略~ error: retrieving gpg key timed out.
下記を実行すると「公開鍵を利用できないため、以下の署名は検証できませんでした」というエラーが発生。これがインストールが止まる原因ぽい。
sudo apt update
上記ページを参考に、エラーを回避する
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <エラーになった公開鍵> sudo apt update
これでインストールできるようになりました。
sudo apt install php7.3 php7.3-common php7.3-cli php7.3-fpm php7.3-mysql php7.3-dev php7.3-mbstring php7.3-zip php7.3-sqlite3 php7.3-curl php7.3-gd php7.3-xml
IEのFormのデータを保存、復元する方法
JavaScriptでFormの値をJavaScriptとして保存し、
再度実行すれば、Formの値を復元するスクリプトです。
(Web Development Helperのスクリプトコンソールを利用します)
長大なFormのテストデータ入力時に役立つかもしれません。
// 準備 // ・以下のページから「Web Development Helper」をダウンロードしてください。 // http://projects.nikhilk.net/Projects/WebDevHelper.aspx // ・ダウンロードしたファイルをインストールしてください。 // ・IEを起動し、「ファイル」メニューの「エクスプローラーバー」から「Web Development Helper」を // 選択してください。 // ・表示されたメニューの「HTTP Logging▼」を選択し、「Script Console」を選択してください。 // ・「Script Console」の右の「Enable Debugging」にチェックを付けてください。 // (「インターネットオプション」の「詳細」タブでスクリプトのデバッグを許可している必要があります。) // // 使い方 // 1:右ペインの「LoadScript」からこのスクリプトファイルを開き、「Execute」をクリックする。 // 2:左ペインに表示された実行結果を全選択し、「Copy」をクリックする。 // (フォームのデータを復元するためのJavaScriptがコピーされます) // 3:再度同じ画面を開き、2でコピーしたスクリプトを実行すればフォームの値が復元されます。 // (select,checkbox,radio,hiddenの項目も復元されます) var scr = ""; var selNm = ""; var selType = ""; var selIdx = 0; var aryIdx = ""; var nameHash = new Array(); dall = "window.document.all['"; var end = ";\r\n"; var forms; if( window.frames.length > 0 ) { // フレームがある場合は最初のフレームからデータを取得する forms = window.frames[0].document.forms; dall = "window.frames[0].document.all['" } else { // フレームがない場合 forms = window.document.forms; } for( i = 0; i < forms.length; i++ ) { frm = forms[i]; for( j = 0; j < frm.all.length; j++ ) { tag = frm.all[j]; if( tag.type != undefined ) { // viewstateは保存しない if( tag.name == "__VIEWSTATE" ) { continue; } if( frm.all[tag.name] != undefined && frm.all[tag.name].length >= 2 && frm.all[tag.name][0].type == tag.type ) // selectのname属性が1つの場合と2つの場合で動作が異なるので回避 { if( nameHash[tag.name] == undefined ) { nameHash[tag.name] = 0; } aryIdx = "[" + nameHash[tag.name] + "]"; nameHash[tag.name] += 1; } else { aryIdx = ""; } // text,textarea,hidden項目を保存 if( tag.type == "text" || tag.type == "textarea" || tag.type == "hidden" ) { scr += dall + tag.name + "']" + aryIdx + ".value = '" + tag.value.replace(/\r\n/g, '\\r\\n') + "'" + end; } else if( tag.type == "checkbox" || tag.type == "radio" ) { scr += dall + tag.name + "']" + aryIdx + ".checked = " + tag.checked + end; } else if( tag.type == "select-one" || tag.type == "select-multiple" ) { selIdx = 0; selNm = tag.name; selType = tag.type; } } else { if( tag.tagName == "OPTION" ) { if( selType == "select-one" && tag.selected ) { scr += dall + selNm + "']" + aryIdx + ".options[" + selIdx + "].selected = " + tag.selected + end; } else if( selType == "select-multiple") { scr += dall + selNm + "']" + aryIdx + ".options[" + selIdx + "].selected = " + tag.selected + end; } selIdx++; } } } } Debug.writeln(scr);
VSマクロ(プロパティー一括生成)をVS2003対応
2008/02/19 (火) VS2005マクロ(プロパティー一括生成)
のマクロをVisualStudio2003、VisualStudio2005両対応に修正。
Continue文がないだけで非常に読みづらい気がする・・・
If文をネストすればもっと単純になるけれど、ネスト深いプログラムは嫌いだし。
気になる点
VS2003でVBのソースからマクロを生成した場合
Private _bar As Integer = 0 ''' <summary> ''' ''' </summary> Public Property Bar() As Int32 〜以下略〜
Integer型が「Int32」として生成される。C#はちゃんと「int」になるのに。
VS2003のバグかも。
' 選択範囲の変数からプロパティーを生成する(VB.NET、C#対応) ' 既に同名のプロパティーがあれば作成しない ' ' プロパティー名の規則 ' 1:変数は"_"(アンダースコア)から始まること ' 2:プロパティー名は変数の先頭1文字を削除し、次の文字を大文字に変換した名前とする ' (ex. 「_foo 」→「Foo」) ' VS2003,VS2005対応 Sub MakeProperty() ' 現在の選択範囲 Dim sel As EnvDTE.TextSelection = DTE.ActiveDocument.Selection Dim selTop As VirtualPoint = sel.TopPoint Dim selBottom As VirtualPoint = sel.BottomPoint ' もし下から上へ選択している場合は、選択範囲を交換する If selTop.Line = DTE.ActiveDocument.Selection.ActivePoint.Line Then sel.SwapAnchor() End If Dim actPoint As TextPoint = DTE.ActiveDocument.Selection.ActivePoint ' 現在キャレットがある場所のクラスを取得 Dim elm As CodeElement = actPoint.CodeElement(vsCMElement.vsCMElementClass) Dim cls As CodeClass = elm Dim typ As CodeType = CType(elm, CodeType) 'クラス内に既に存在するプロパティー一覧を作成する(重複作成しないためのチェック用) Dim lstProp As New System.Collections.Specialized.StringCollection For Each ite As CodeElement In typ.Members If TypeOf ite Is CodeProperty Then lstProp.Add(ite.Name) End If Next ' 編集用オブジェクト Dim editPoint As EditPoint Dim sbProp As New System.Text.StringBuilder Dim blnContinue As Boolean = True Dim newLine As String = System.Environment.NewLine ' クラス内のメンバーに対して For Each ite As CodeElement In typ.Members blnContinue = True sbProp.Length = 0 ' 変数であるかチェック If blnContinue AndAlso Not TypeOf ite Is CodeVariable Then blnContinue = False End If ' 変数が選択範囲内かチェック Dim elmVal As CodeVariable = Nothing If blnContinue Then elmVal = DirectCast(ite, CodeVariable) Dim valLine As Integer = elmVal.StartPoint.Line If Not (selTop.Line <= valLine AndAlso valLine <= selBottom.Line) Then blnContinue = False End If End If ' 変数名の先頭がアンダースコアであるかチェック If blnContinue AndAlso elmVal.Name.Substring(0, 1) <> "_" Then blnContinue = False End If ' 同じ名前のプロパティーが存在している場合は作成しない If blnContinue AndAlso lstProp.Contains(GetPropNm(elmVal.Name)) Then blnContinue = False End If ' プロパティー文字列を生成する If blnContinue Then If DTE.ActiveDocument.Language = "Basic" Then ' 編集中ファイルがVBの場合 sbProp.AppendFormat("''' <summary>").Append(newLine) sbProp.AppendFormat("''' ").Append(newLine) sbProp.AppendFormat("''' </summary>").Append(newLine) sbProp.AppendFormat("Public Property {0}() As {1}", GetPropNm(elmVal.Name), elmVal.Type.AsString).Append(newLine) sbProp.AppendFormat(" Get").Append(newLine) sbProp.AppendFormat(" Return {0}", elmVal.Name).Append(newLine) sbProp.AppendFormat(" End Get").Append(newLine) sbProp.AppendFormat(" Set(ByVal value As {0})", elmVal.Type.AsString).Append(newLine) sbProp.AppendFormat(" {0} = value", elmVal.Name).Append(newLine) sbProp.AppendFormat(" End Set").Append(newLine) sbProp.AppendFormat("End Property").Append(newLine) ElseIf DTE.ActiveDocument.Language = "CSharp" Then ' 編集中ファイルがC#の場合 sbProp.AppendFormat("/// <summary>").Append(newLine) sbProp.AppendFormat("/// ").Append(newLine) sbProp.AppendFormat("/// </summary>").Append(newLine) sbProp.AppendFormat("public {0} {1}", elmVal.Type.AsString, GetPropNm(elmVal.Name)).Append(newLine) sbProp.AppendFormat("{{").Append(newLine) sbProp.AppendFormat(" get {{ return {0}; }}", elmVal.Name).Append(newLine) sbProp.AppendFormat(" set {{ {0} = value; }}", elmVal.Name).Append(newLine) sbProp.AppendFormat("}}").Append(newLine) End If ' 変数の次の行にプロパティーを追加する editPoint = elmVal.EndPoint().CreateEditPoint() editPoint.Insert(System.Environment.NewLine) editPoint.Insert(sbProp.ToString) End If Next ' プロパティーのフォーマットを行う sel.LineDown(True, 12) sel.SmartFormat() sel.LineUp(False) End Sub ' 変数名からプロパティー名を生成して返す Private Function GetPropNm(ByVal varNm As String) As String Dim nm As String = varNm.Substring(1) Return nm.Substring(0, 1).ToUpper() & nm.Substring(1) End Function