背景
VSCodeの拡張機能API自体にはクリップボード画像を直接取得する手段がありません。TypeScriptマクロでクリップボード画像をMarkdown挿入する機能を実装する際、PowerShell経由でのアクセスが必要でした。
1. Node.jsのchild_processからPowerShellを呼ぶ
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
child_process.execをpromisifyしてasync/awaitで扱えるようにするのが、VSCode拡張機能から外部コマンドを呼ぶ際の定石です。
2. PowerShellのSystem.Windows.Forms.Clipboardで画像取得
const psScript = `
Add-Type -AssemblyName System.Windows.Forms;
Add-Type -AssemblyName System.Drawing;
$img = [System.Windows.Forms.Clipboard]::GetImage();
if ($img) {
$img.Save('${outputPath.replace(/\\/g, '\\\\')}', [System.Drawing.Imaging.ImageFormat]::Jpeg);
exit 0;
} else {
exit 1;
}
`;
System.Windows.Forms.ClipboardとSystem.Drawingの2アセンブリをAdd-Typeで読み込むことで、.NETのクリップボードAPIをPowerShell経由で呼び出せます。画像がクリップボードにない場合はexit 1で異常終了させ、TypeScript側でtry/catchによりエラーメッセージを表示します。
3. パスのバックスラッシュエスケープに注意
$img.Save('${outputPath.replace(/\\/g, '\\\\')}', ...)
Windowsパスの\はPowerShell文字列内でもエスケープが必要なため、replace(/\\/g, '\\\\')で二重化してから埋め込みます。これを忘れると、パスの区切りが正しく解釈されずファイル保存に失敗します。
4. VSCode API側の処理はシンプル
const currentDir = path.dirname(editor.document.uri.fsPath);
const imagesDir = path.join(currentDir, 'images');
if (!fs.existsSync(imagesDir)) {
fs.mkdirSync(imagesDir, { recursive: true });
}
imagesDirの自動作成にはrecursive: trueを指定するだけで、親フォルダも含めて一括作成されます。画像取得さえPowerShellで解決してしまえば、あとは通常のVSCode拡張機能開発の範囲で完結します。
まとめ
VSCode拡張機能単体では手が届かないクリップボード画像アクセスも、child_process経由でPowerShellのSystem.Windows.Formsを叩けば解決できます。バックスラッシュのエスケープ忘れがハマりどころでした。ショートカットキー登録を含む完全なコードは元記事にまとめています。