C♯ のたすけをかりてクリップボードのイベント駆動に成功
拙文『スクリーンショットをExcelシートに半自動でペーストする(エビデンス取得用)』 (Excel VBA) ではイベント駆動に挫折し無限ループをまわしていますが、こちらは成功いたしました。
2019/04/09 更新
- ワークシートを追加するとアクティブ ワークシートにペーストされるようにしました。
- ただし、このスクリプトから起動した Excel でべつのワークブックをひらいても干渉しないようにしました1。
2019/04/09 再更新
- アクティブ ワークブックのきりかえではアクティブ ワークシートが「リセット」されてしまうので修正しました。
Paste-ScreenshotToExcelSheet.ps1
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ClipboardListeningForm : Form
{
[DllImport("user32.dll", SetLastError = true)]
private extern static void AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
private extern static void RemoveClipboardFormatListener(IntPtr hwnd);
public event EventHandler ClipboardUpdate;
public const int WM_CLIPBOARDUPDATE = 0x031D;
protected override void OnLoad(EventArgs e)
{
AddClipboardFormatListener(Handle);
base.OnLoad(e);
}
protected override void Dispose(bool disposing)
{
RemoveClipboardFormatListener(Handle);
base.Dispose(disposing);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
OnClipboardUpdate(EventArgs.Empty);
m.Result = IntPtr.Zero;
}
else
base.WndProc(ref m);
}
protected virtual void OnClipboardUpdate(EventArgs e)
{
EventHandler handler = ClipboardUpdate;
if (handler != null)
{
handler(this, e);
}
}
}
'@ -ReferencedAssemblies System.Windows.Forms
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $true
$xlWorkbook = $excel.Workbooks.Add([Microsoft.Office.Interop.Excel.xlWBATemplate]::xlWBATWorksheet)
$excel.ActiveWindow.Caption = 'Screenshots'
$xlWorksheet = $xlWorkbook.Worksheets(1)
$xlActiveSheet = $xlWorksheet
$form = New-Object -TypeName ClipboardListeningForm
$form.Text = 'Start Capturing'
$toggleButton = New-Object -TypeName System.Windows.Forms.CheckBox
$toggleButton.Appearance = [System.Windows.Forms.Appearance]::Button
$toggleButton.Left = ($form.ClientRectangle.Width - $toggleButton.Width) / 2
$toggleButton.Top = ($form.ClientRectangle.Height - $toggleButton.Height) / 2
$toggleButton.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$toggleButton.Text = 'Start'
$toggleButton.add_CheckedChanged({
if ($toggleButton.Checked) {
$toggleButton.Text = 'Stop'
$form.Text = 'Capturing'
} else {
$toggleButton.Text = 'Start'
$form.Text = 'Stopped'
}
})
$form.Controls.Add($toggleButton)
$form.add_ClipboardUpdate({
$xlActiveSheet = $excel.ActiveWorkbook.ActiveSheet
$xlWorksheet = $xlWorkbook.ActiveSheet
$targetColumn, $targetRow = 2, 2
if ($toggleButton.Checked -and [System.Windows.Forms.Clipboard]::ContainsImage()) {
$shapesCount = $xlWorksheet.Shapes.Count
if ($shapesCount -gt 0) {
$targetRow = $xlWorksheet.Shapes($shapesCount).BottomRightCell.Offset(1).Row
}
$xlWorksheet.Activate()
$xlWorksheet.Cells($targetRow, $targetColumn).Select()
$xlWorksheet.Paste()
$xlActiveSheet.Activate()
}
})
$form.ShowDialog()
$excel.Quit()
[void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlWorksheet)
[void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlActiveSheet)
[void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlWorkbook)
[void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
参考文献
- C#でClipboard監視(Windows API) - Qiita
- クリップボードを監視してテキストボックスに追加していく感じの。 · GitHub
- Monitor Clipboard changes in C# using AddClipboardFormatListener / WM_CLIPBOARDUPDATE. See Clipboard class: http://msdn.microsoft.com/en-us/library/system.windows.clipboard(v=vs.110).aspx · GitHub
- PowerShell で Excel をどうのこうのすることに興味を持ってくれると嬉しい - Qiita
-
アクティブ ワークシートおよびアクティブ ワークブックのきりかえで対処してるのでちらつきます。また、フォームをとじるとこのプロセスの Excel からひらいたワークブックはすべてとじます。 ↩