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:ファイル名のやり取りを追加