我的问题是这部分:
SendMessage(hCaptureWnd,WM_CAP_COPY,0); // copy it to the clipboard
它的作用是将图像从窗口复制到剪贴板,然后从中创建一个字节数组.
它可以工作 – 只要你在程序运行时不使用剪贴板.
问题是,这对我自己来说甚至都不起作用,因为我有时复制一些东西,而Visual Studio需要很长时间才能开始调试Web应用程序,然后崩溃.
所以我的问题在这里:
如何在不使用剪贴板的情况下获取图像?
或者更具体地说,如何将hCaptureWnd转换为System.Drawing.Image?
– 编辑:
我错过了说“没有创建文件,我想要一个字节数组”.
它是一个Web应用程序,因此运行应用程序的用户不应具有对文件系统的写访问权限(仅写入临时测试文件)…
– 结束编辑:
/// <summary> /// Captures a frame from the webcam and returns the byte array associated /// with the captured image /// </summary> /// <param name="connectDelay">number of milliseconds to wait between connect /// and capture - necessary for some cameras that take a while to 'warm up'</param> /// <returns>byte array representing a bitmp or null (if error or no webcam)</returns> private static byte[] InternalCaptureToByteArray(int connectDelay = 500) { Clipboard.Clear(); // clear the clipboard int hCaptureWnd = capCreateCaptureWindowA("ccWebCam",// create the hidden capture window 350,350,0); SendMessage(hCaptureWnd,WM_CAP_CONNECT,0); // send the connect message to it Thread.Sleep(connectDelay); // sleep the specified time SendMessage(hCaptureWnd,WM_CAP_GET_FRAME,0); // capture the frame SendMessage(hCaptureWnd,0); // copy it to the clipboard SendMessage(hCaptureWnd,WM_CAP_DISCONNECT,0); // disconnect from the camera Bitmap bitmap = (Bitmap)Clipboard.GetDataObject().GetData(DataFormats.Bitmap); // copy into bitmap if (bitmap == null) return null; using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream,ImageFormat.Bmp); // get bitmap bytes return stream.ToArray(); } // End Using stream } // End Function InternalCaptureToByteArray
注(http://msdn.microsoft.com/en-us/library/windows/desktop/dd756879(v=vs.85).aspx):
HWND VFWAPI capCreateCaptureWindow( LPCTSTR lpszWindowName,DWORD dwStyle,int x,int y,int nWidth,int nHeight,HWND hWnd,int nID ); #define VFWAPI WINAPI typedef HANDLE HWND; typedef PVOID HANDLE; typedef void *PVOID;
完整代码供参考
using System; using System.IO; using System.Drawing; using System.Threading; using System.Windows.Forms; using System.Drawing.Imaging; using System.Collections.Generic; using System.Runtime.InteropServices; // http://www.creativecodedesign.com/node/66 // http://www.barebonescoder.com/2012/01/finding-your-web-cam-with-c-directshow-net/ // http://www.codeproject.com/Articles/15219/WebCam-Fast-Image-Capture-Service-using-WIA // http://www.c-sharpcorner.com/uploadfile/yougerthen/integrate-the-web-webcam-functionality-using-C-Sharp-net-and-com-part-viii/ // http://forums.asp.net/t/1410057.aspx namespace cc.Utility { // bool isCaptured = ccWebCam.CaptureSTA("capture.jpg"); // Access to path C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\capture.jpg" denied. // byte[] captureBytes = ccWebCam.CaptureSTA(); /// <summary> /// Timur Kovalev (http://www.creativecodedesign.com): /// This class provides a method of capturing a webcam image via avicap32.dll api. /// </summary> public static class ccWebCam { #region *** PInvoke Stuff - methods to interact with capture window *** [DllImport("user32",EntryPoint = "SendMessage")] private static extern int SendMessage(int hWnd,uint Msg,int wParam,int lParam); [DllImport("avicap32.dll",EntryPoint = "capCreateCaptureWindowA")] private static extern int capCreateCaptureWindowA(string lpszWindowName,int dwStyle,int X,int Y,int hwndParent,int nID); private const int WM_CAP_CONNECT = 1034; private const int WM_CAP_DISCONNECT = 1035; private const int WM_CAP_COPY = 1054; private const int WM_CAP_GET_FRAME = 1084; #endregion private static object objWebCamThreadLock = new object(); //CaptureToFile(@"D:\Stefan.Steiger\Documents\Visual Studio 2010\Projects\Post_Ipag\image3.jpg"): public static bool Capture(string filePath,int connectDelay = 500) { lock (objWebCamThreadLock) { return cc.Utility.ccWebCam.InternalCaptureAsFileInThread(filePath,connectDelay); } } // End Treadsafe Function Capture public static byte[] Capture(int connectDelay = 500) { lock (objWebCamThreadLock) { return InternalCaptureToByteArrayInThread(connectDelay); } } // End Treadsafe Function Capture /// <summary> /// Captures a frame from the webcam and returns the byte array associated /// with the captured image. The image is also stored in a file /// </summary> /// <param name="filePath">path the file wher ethe image will be saved</param> /// <param name="connectDelay">number of milliseconds to wait between connect /// and capture - necessary for some cameras that take a while to 'warm up'</param> /// <returns>true on success,false on failure</returns> private static bool InternalCaptureAsFileInThread(string filePath,int connectDelay = 500) { bool success = false; Thread catureThread = new Thread(() => { success = InternalCaptureAsFile(filePath,connectDelay); }); catureThread.SetApartmentState(ApartmentState.STA); catureThread.Start(); catureThread.Join(); return success; } // End Function InternalCaptureAsFileInThread /// <summary> /// Captures a frame from the webcam and returns the byte array associated /// with the captured image. The image is also stored in a file /// </summary> /// <param name="filePath">path the file wher ethe image will be saved</param> /// <param name="connectDelay">number of milliseconds to wait between connect /// and capture - necessary for some cameras that take a while to 'warm up'</param> /// <returns>true on success,false on failure</returns> private static bool InternalCaptureAsFile(string filePath,int connectDelay = 500) { byte[] capture = ccWebCam.InternalCaptureToByteArray(connectDelay); if (capture != null) { // Access to path C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\image1.jpg" denied. File.WriteAllBytes(filePath,capture); return true; } return false; } // End Function InternalCaptureAsFile /// <summary> /// Captures a frame from the webcam and returns the byte array associated /// with the captured image. Runs in a newly-created STA thread which is /// required for this method of capture /// </summary> /// <param name="connectDelay">number of milliseconds to wait between connect /// and capture - necessary for some cameras that take a while to 'warm up'</param> /// <returns>byte array representing a bitmp or null (if error or no webcam)</returns> private static byte[] InternalCaptureToByteArrayInThread(int connectDelay = 500) { byte[] bytes = null; Thread catureThread = new Thread(() => { bytes = InternalCaptureToByteArray(connectDelay); }); catureThread.SetApartmentState(ApartmentState.STA); catureThread.Start(); catureThread.Join(); return bytes; } // End Function InternalCaptureToByteArrayInThread /// <summary> /// Captures a frame from the webcam and returns the byte array associated /// with the captured image /// </summary> /// <param name="connectDelay">number of milliseconds to wait between connect /// and capture - necessary for some cameras that take a while to 'warm up'</param> /// <returns>byte array representing a bitmp or null (if error or no webcam)</returns> private static byte[] InternalCaptureToByteArray(int connectDelay = 500) { Clipboard.Clear(); // clear the clipboard int hCaptureWnd = capCreateCaptureWindowA("ccWebCam",// create the hidden capture window 350,0); SendMessage(hCaptureWnd,0); // send the connect message to it Thread.Sleep(connectDelay); // sleep the specified time SendMessage(hCaptureWnd,0); // capture the frame SendMessage(hCaptureWnd,0); // copy it to the clipboard SendMessage(hCaptureWnd,0); // disconnect from the camera Bitmap bitmap = (Bitmap)Clipboard.GetDataObject().GetData(DataFormats.Bitmap); // copy into bitmap if (bitmap == null) return null; using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream,ImageFormat.Bmp); // get bitmap bytes return stream.ToArray(); } // End Using stream } // End Function InternalCaptureToByteArray } }
我试过这样,但它只得到一个黑色的图像……
[DllImport("user32.dll")] static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("gdi32.dll",SetLastError = true)] static extern IntPtr CreateCompatibleDC(IntPtr hdc); enum TernaryRasterOperations : uint { /// <summary>dest = source</summary> SRCCOPY = 0x00CC0020,/// <summary>dest = source OR dest</summary> SRCPAINT = 0x00EE0086,/// <summary>dest = source AND dest</summary> SRCAND = 0x008800C6,/// <summary>dest = source XOR dest</summary> SRCINVERT = 0x00660046,/// <summary>dest = source AND (NOT dest)</summary> SRCERASE = 0x00440328,/// <summary>dest = (NOT source)</summary> NOTSRCCOPY = 0x00330008,/// <summary>dest = (NOT src) AND (NOT dest)</summary> NOTSRCERASE = 0x001100A6,/// <summary>dest = (source AND pattern)</summary> MERGECOPY = 0x00C000CA,/// <summary>dest = (NOT source) OR dest</summary> MERGEPAINT = 0x00BB0226,/// <summary>dest = pattern</summary> PATCOPY = 0x00F00021,/// <summary>dest = DPSnoo</summary> PATPAINT = 0x00FB0A09,/// <summary>dest = pattern XOR dest</summary> PATINVERT = 0x005A0049,/// <summary>dest = (NOT dest)</summary> DSTINVERT = 0x00550009,/// <summary>dest = BLACK</summary> BLACKNESS = 0x00000042,/// <summary>dest = WHITE</summary> WHITENESS = 0x00FF0062,/// <summary> /// Capture window as seen on screen. This includes layered windows /// such as WPF windows with AllowsTransparency="true" /// </summary> CAPTUREBLT = 0x40000000 } [DllImport("gdi32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool BitBlt(IntPtr hdc,int nXDest,int nYDest,IntPtr hdcSrc,int nXSrc,int nYSrc,TernaryRasterOperations dwRop); [DllImport("gdi32.dll")] static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,int nHeight); [DllImport("gdi32.dll",ExactSpelling = true,PreserveSig = true,SetLastError = true)] static extern IntPtr SelectObject(IntPtr hdc,IntPtr hgdiobj); [DllImport("gdi32.dll")] static extern bool DeleteDC(IntPtr hdc); [DllImport("user32.dll")] static extern bool ReleaseDC(IntPtr hWnd,IntPtr hDC); [DllImport("gdi32.dll")] static extern bool DeleteObject(IntPtr hObject); public static void ScreenshotWindow(IntPtr windowHandle) { Rect Rect = new Rect(); GetWindowRect(windowHandle,ref Rect); int width = Rect.Right - Rect.Left; int height = Rect.Bottom - Rect.Top; IntPtr windowDeviceContext = GetWindowDC(windowHandle); IntPtr destDeviceContext = CreateCompatibleDC(windowDeviceContext); IntPtr bitmapHandle = CreateCompatibleBitmap(windowDeviceContext,width,height); IntPtr oldObject = SelectObject(destDeviceContext,bitmapHandle); BitBlt(destDeviceContext,height,windowDeviceContext,TernaryRasterOperations.CAPTUREBLT | TernaryRasterOperations.SRCCOPY); SelectObject(destDeviceContext,oldObject); DeleteDC(destDeviceContext); ReleaseDC(windowHandle,destDeviceContext); Image screenshot = Image.FromHbitmap(bitmapHandle); DeleteObject(bitmapHandle); screenshot.Save("d:\\temp\\mywebcamimage.png",System.Drawing.Imaging.ImageFormat.Png); /* // TODO - Remove above save when it works using (MemoryStream stream = new MemoryStream()) { screenshot.Save(stream,System.Drawing.Imaging.ImageFormat.Png); return stream.ToArray(); } */ }
然后在SendMessage之后(hCaptureWnd,0);
ScreenshotWindow(new IntPtr(hCaptureWnd));
解决方法
WM_CAP_GRAB_FRAME
,它在MSDN上描述.
它的作用是:
The WM_CAP_GRAB_FRAME message retrieves and displays a single frame
from the capture driver. After capture,overlay and preview are
disabled. You can send this message explicitly or by using the
capGrabFrame macro.
要获得实际数据,您需要使用frame callback as described further on MSDN.回调可以获取图片字节,您可以将其写入文件或用于任何处理而无需通过剪贴板传输.
…is the callback function used with streaming capture to optionally
process a frame of captured video. The name capVideoStreamCallback is
a placeholder for the application-supplied function name.[And you have there a] … Pointer to a VIDEOHDR structure containing information about the captured frame.
同样,这个API是视频捕获的不吉利的选择.太旧了,太有限了.