W-ZERO3 で DirectShow 使ってカメラ制御 for C#
1つ前ので言ってたやつ。某友人の助けを借りてなんとかできました。参考までにコード載せときます。
Windows Mobile 5 向けDLLプロジェクトを作って、このコードと以下のフォルダに入ってるしかるべきコード(.cpp が2つ、.h が3つ)を入れ、graphcapture.cpp を5行修正すれば*1動きます。
C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Samples\CPP\Win32\Cameracapture
// camera.cpp #include "stdafx.h" #include <windows.h> #include <commctrl.h> BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } extern "C" __declspec(dllexport) void* APIENTRY InitializeCamera(HWND hWnd) { HRESULT hr = S_OK; CGraphManager *pGraphManager = new CGraphManager(); // 初期化 CHK( pGraphManager->RegisterNotificationWindow( hWnd )); CHK( pGraphManager->Init()); CHK( pGraphManager->BuildCaptureGraph()); // フィルタグラフ開始 CHK( pGraphManager->RunCaptureGraph()); return pGraphManager; Cleanup: return NULL; } extern "C" __declspec(dllexport) int APIENTRY CaptureStillImage(void* handle, LPWSTR fileName) { CGraphManager *pGraphManager = (CGraphManager*)handle; pGraphManager->CaptureStillImage(fileName); return NULL; } extern "C" __declspec(dllexport) int APIENTRY ReleaseCamera(void* handle) { delete handle; return TRUE; }
stdafx.h の末尾に追加
#include <streams.h> #include <dmodshow.h> #include <dmoreg.h> #include <wmcodecids.h> #include <atlbase.h> #include "struct.h" #include "CPropertyBag.h" #include "graphmanager.h" #define chk( x ) do{ if( FAILED( hr = ( x ))) { goto Cleanup; } } while( FALSE ); #define chk( x ) do{ hr = x; goto Cleanup; } while( FALSE ); // export functions extern "C" __declspec(dllexport) void* APIENTRY InitializeCamera(HWND hWnd); extern "C" __declspec(dllexport) int APIENTRY CaptureStillImage(void* handle, LPOLESTR fileName); extern "C" __declspec(dllexport) int APIENTRY ReleaseCamera(void* handle);
以上のコードで DLL を作って、以下のクラスで P/Invoke できます。
using System; using System.Collections.Generic; using System.Text; using Microsoft.WindowsCE.Forms; using System.Windows.Forms; using System.Runtime.InteropServices; namespace EbiSoft.ImadokoNavi { class Camera : MessageWindow { #region P/Invoke宣言 [DllImport("ESCamera.dll")] static extern IntPtr InitializeCamera(IntPtr hWnd); [DllImport("ESCamera.dll")] static extern int CaptureStillImage(IntPtr handle, string fileName); [DllImport("ESCamera.dll")] static extern int ReleaseCamera(IntPtr handle); private const int WM_USER = 0x400; private enum DSHOW_MESSAGE { MESSAGE_INFO, MESSAGE_ERROR, MESSAGE_ENDRECORDING, MESSAGE_FILECAPTURED } [StructLayout(LayoutKind.Sequential)] private struct DShowMessage { public DSHOW_MESSAGE dwMessage; public IntPtr wzMessage; } #endregion public event EventHandler SavedPicture; private IntPtr m_cameraObj = IntPtr.Zero; private Form m_owner; public Camera(Form owner) { m_owner = owner; m_cameraObj = InitializeCamera(this.Hwnd); // 初期化失敗したら例外スロー if (m_cameraObj == IntPtr.Zero) throw new Exception("カメラの初期化に失敗しました。"); } public new void Dispose() { if (m_cameraObj != IntPtr.Zero) { ReleaseCamera(m_cameraObj); } base.Dispose(); } public void CaptureImage(string fileName) { if (m_cameraObj != IntPtr.Zero) { CaptureStillImage(m_cameraObj, fileName); } else { throw new Exception("カメラが初期化されていません。"); } } protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_USER: DShowMessage returnedMessage = (DShowMessage) Marshal.PtrToStructure(m.LParam, typeof(DShowMessage)); if (returnedMessage.dwMessage == DSHOW_MESSAGE.MESSAGE_FILECAPTURED) { InvokeEvent(SavedPicture, EventArgs.Empty); } break; default: base.WndProc(ref m); break; } } protected void InvokeEvent(Delegate d, EventArgs e) { if (d != null) { // イベントを通知 if (m_owner != null) m_owner.Invoke(d, this, e); } } } }
わりとまともなコードを載せてみたけど、スクリプトはともかくC#とかだと長くなっちゃうね。
*1:ファイル名のやり取りを追加