如何在不使用剪贴板的情况下复制图片?

7

问题:我有以下代码用于从网络摄像头中捕获图像。

我的问题在于这一部分:

SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 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", 0, 0, 0,  // create the hidden capture window
        350, 350, 0, 0);
    SendMessage(hCaptureWnd, WM_CAP_CONNECT, 0, 0);                 // send the connect message to it
    Thread.Sleep(connectDelay);                                     // sleep the specified time
    SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0);               // capture the frame
    SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0);                    // copy it to the clipboard
    SendMessage(hCaptureWnd, WM_CAP_DISCONNECT, 0, 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 nWidth, int nHeight, 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", 0, 0, 0,  // create the hidden capture window
                350, 350, 0, 0);
            SendMessage(hCaptureWnd, WM_CAP_CONNECT, 0, 0);                 // send the connect message to it
            Thread.Sleep(connectDelay);                                     // sleep the specified time
            SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0);               // capture the frame
            SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0);                    // copy it to the clipboard
            SendMessage(hCaptureWnd, WM_CAP_DISCONNECT, 0, 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, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    [DllImport("gdi32.dll")]
    static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, 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, 0, 0, width, height, windowDeviceContext, 0, 0, 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, WM_CAP_GET_FRAME, 0, 0);之后,紧接着这个操作。

ScreenshotWindow(new IntPtr(hCaptureWnd));

嗯,为什么我找不到任何关于它的文档呢?我猜我在这里已经超出了我的能力范围,我从未听说过它。 - Cody Gray
VFW(avicap32)不是一个好的API选择,该API已经过时,不方便,不能支持所有摄像头,并且功能集受限。 - Roman R.
1
好的,我本来想帮忙的,但是你的代码使用了虚构的消息常量名称。所以我太困惑了,无法继续下去。 - David Heffernan
@David Heffernan:是的,这些常量名字有点不对,但它们不是我的工作 ;) - Stefan Steiger
我本来要说罗曼说的话,但是被那些令人困惑的名字搞得不知所措了! - David Heffernan
显示剩余4条评论
3个回答

6

“WM_CAP_GET_FRAME”这个消息并不存在。正确的名称是WM_CAP_GRAB_FRAME,在MSDN上有详细说明。

它的作用是:

“WM_CAP_GRAB_FRAME”消息从捕获驱动程序中检索和显示单个帧。捕获、叠加和预览后被禁用。您可以显式地发送此消息或使用capGrabFrame宏。

要获取实际数据,您需要使用在MSDN上进一步描述的帧回调。回调会让您得到图片字节,您可以将其写入文件或进行任何处理,无需通过剪贴板传输。

...是与流式捕获一起使用的回调函数,可选择性地处理捕获的视频帧。capVideoStreamCallback是应用程序提供的函数名称的占位符。

[您在那里拥有一个] ...指向包含有关捕获帧的信息的VIDEOHDR结构的指针。

再次强调,这个API不适合视频捕获。太老,太有限。


3

您需要发送不同的消息,具体是WM_CAP_FILE_SAVEDIB,以将数据保存到磁盘文件中。然后您就能够加载该文件并将其放入Bitmap对象中进行进一步处理(我不知道是否有内置的cam-to-byte[]功能)。

[DllImport("user32", EntryPoint = "SendMessage")]
private static extern int SendMessage(
    int hWnd, uint Msg, int wParam, string strFileName);

private const int WM_USER = 0x0400;
private const int WM_CAP_START = WM_USER;
private const int WM_CAP_FILE_SAVEDIB = WM_CAP_START + 25;

//before
SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0);

//after
string tempFile = Server.MapPath("~/App_Data/tempCap.bmp");
SendMessage(hCaptureWnd, WM_CAP_FILE_SAVEDIB, 0, tempFile); //create tempfile
Bitmap bitmap = new Bitmap(tempFile); //read tempfile
using (MemoryStream stream = new MemoryStream())
{
    bitmap.Save(stream, ImageFormat.Bmp);
    return stream.ToArray();
}

好的,那么对于字节数组(不加载文件),有什么方法? - Stefan Steiger
我认为这个dll没有提供一个直接的cam-to-byte[]消息。 - Alex
当然不是,但是WinAPI有望提供一种将hCaptureWnd转换为位图的方法。 - Stefan Steiger
这就是 WM_CAP_FILE_SAVEDIB 的作用。我已经完善了代码片段,添加了读取临时文件并构建 byte[] 的行。 - Alex
我并没有直接提到,但我写了一句引用:“Visual Studio启动调试Web应用程序需要很长时间” :) 除此之外,该应用程序不知道日志文件路径在哪里(我不想开始配置它,因为每个版本和自定义配置肯定都不同),此外IIS会写入事件日志,而不是日志文件。 - Stefan Steiger
显示剩余2条评论

1

在Roman R.的回答基础上:

道德的细微之处在于您需要注册回调帧,然后调用grabframe,并且不能直接将C样式的char[]强制转换为byte[],并且您可以获得原始位图数据 - 而不是位图,并且图像大小为640x480,无论在capCreateCaptureWindowA中设置了什么,并且lpData需要是IntPtr,而不是UIntPtr,因为Marshal.Copy没有UIntPtr的重载,并且使用WriteBitmapFile,可以将原始位图数据写入位图,而不使用不安全的代码或映射位图文件头,谁写的Marshal.Copy使复制负值成为可能,因为长度是int,而不是uint...

此外,由于某种原因需要将图像旋转180度...
另外,我已更改WM常量的名称。

    SendMessage(hCaptureWnd, WM_CAP_SET_CALLBACK_FRAME, 0, capVideoStreamCallback);
    SendMessage(hCaptureWnd, WM_CAP_GRAB_FRAME, 0, 0);               // capture the frame

有了这些额外的东西

    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757688(v=vs.85).aspx
    [StructLayout(LayoutKind.Sequential)]
    private struct VIDEOHDR 
    {
        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx


        // typedef unsigned char BYTE;
        // typedef BYTE far *LPBYTE;
        // unsigned char* lpData


        //public byte[] lpData; // LPBYTE    lpData; // Aaargh, invalid cast, not a .NET byte array...
        public IntPtr lpData; // LPBYTE    lpData;
        public UInt32 dwBufferLength; // DWORD     dwBufferLength;
        public UInt32 dwBytesUsed; // DWORD     dwBytesUsed;
        public UInt32 dwTimeCaptured; // DWORD     dwTimeCaptured;


        // typedef ULONG_PTR DWORD_PTR;
        // #if defined(_WIN64)  
        //   typedef unsigned __int64 ULONG_PTR;
        // #else
        //   typedef unsigned long ULONG_PTR;
        // #endif
        public IntPtr dwUser; // DWORD_PTR dwUser; 
        public UInt32 dwFlags; // DWORD     dwFlags;

        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 4)]
        public System.UIntPtr[] dwReserved; // DWORD_PTR dwReserved[4];

        // Does not make a difference
        //public System.UIntPtr[] dwReserved = new System.UIntPtr[4]; // DWORD_PTR dwReserved[4];
    }




    private delegate System.IntPtr capVideoStreamCallback_t(System.UIntPtr hWnd, ref VIDEOHDR lpVHdr);
    [DllImport("user32", EntryPoint = "SendMessage")]
    private static extern int SendMessage(int hWnd, uint Msg, int wParam, capVideoStreamCallback_t routine);




    // http://eris.liralab.it/yarpdoc/vfw__extra__from__wine_8h.html
    private const int WM_USER = 0x0400; // 1024
    private const int WM_CAP_START = WM_USER;
    private const int WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
    private const int WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;

    private const int WM_CAP_FILE_SAVEDIB = WM_CAP_START + 25;
    private const int WM_CAP_SET_CALLBACK_FRAME = WM_CAP_START + 5;
    private const int WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
    private const int WM_CAP_EDIT_COPY = WM_CAP_START + 30;




    // http://lists.ximian.com/pipermail/mono-devel-list/2011-March/037272.html

    private static byte[] baSplendidIsolation;


    private static System.IntPtr capVideoStreamCallback(System.UIntPtr hWnd, ref VIDEOHDR lpVHdr)
    {
        //System.Windows.Forms.MessageBox.Show("hello");
        //System.Windows.Forms.MessageBox.Show(lpVHdr.dwBufferLength.ToString() + " " + lpVHdr.dwBytesUsed.ToString());
        byte[] _imageTemp = new byte[lpVHdr.dwBufferLength];
        Marshal.Copy(lpVHdr.lpData, _imageTemp, 0, (int) lpVHdr.dwBufferLength);
        //System.IO.File.WriteAllBytes(@"d:\temp\mycbfile.bmp", _imageTemp); // AAaaarg, it's raw bitmap data...

        // https://dev59.com/kEfRa4cB1Zd3GeqP7DFF
        // https://dev59.com/YHE85IYBdhLWcg3wr1ux

        // Tsssss... 350 x 350 was the expected setting, but never mind... 
        // fortunately alex told me about WM_CAP_FILE_SAVEDIB, so I could compare to the direct output
        int width = 640;
        int height = 480;
        int stride = width*3;

        baSplendidIsolation = null;
        baSplendidIsolation = WriteBitmapFile(@"d:\temp\mycbfilecc.bmp", width, height, _imageTemp);

        /*
        unsafe
        {
            fixed (byte* ptr = _imageTemp)
            {
                using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr)))
                {
                    image.Save(@"d:\temp\mycbfile2.bmp");
                }
            }
        }
        */

        //var hdr = (Elf32_Phdr)Marshal.PtrToStructure(ptr, typeof(Elf32_Phdr));
        return System.IntPtr.Zero;
    }


    private static byte[] WriteBitmapFile(string filename, int width, int height, byte[] imageData)
    {
        using (var stream = new MemoryStream(imageData))
        using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
        {
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,bmp.Width, bmp.Height)
                                                ,ImageLockMode.WriteOnly
                                                ,bmp.PixelFormat
            );

            Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);

            bmp.UnlockBits(bmpData);


            if (bmp == null)
                return null;

            bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
            bmp.Save(filename); // For testing only

            using (MemoryStream ms = new MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Png);    // get bitmap bytes
                return ms.ToArray();
            } // End Using stream

        }

    } // End Function WriteBitmapFile


    /// <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(); 
        int hCaptureWnd = capCreateCaptureWindowA("ccWebCam", 0, 0, 0,
            350, 350, 0, 0); // create the hidden capture window
        SendMessage(hCaptureWnd, WM_CAP_DRIVER_CONNECT, 0, 0); // send the connect message to it
        //SendMessage(hCaptureWnd, WM_CAP_DRIVER_CONNECT, i, 0); // i device number retval != 0 --> valid device_id

        Thread.Sleep(connectDelay);                                     // sleep the specified time
        SendMessage(hCaptureWnd, WM_CAP_SET_CALLBACK_FRAME, 0, capVideoStreamCallback);
        SendMessage(hCaptureWnd, WM_CAP_GRAB_FRAME, 0, 0);               // capture the frame

        //SendMessage(hCaptureWnd, WM_CAP_FILE_SAVEDIB, 0, "d:\\temp\\testmywebcamimage.bmp");
        //ScreenshotWindow(new IntPtr(hCaptureWnd));

        //SendMessage(hCaptureWnd, WM_CAP_EDIT_COPY, 0, 0); // copy it to the clipboard


        // using (Graphics g2 = Graphics.FromHwnd(new IntPtr(hCaptureWnd)))

        SendMessage(hCaptureWnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);              // disconnect from the camera

        return baSplendidIsolation;

        /*
        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

你的代码中使用了 WM_CAP_GRAB_FRAME 和 WM_CAP_SET_CALLBACK_FRAME 的十六进制值是多少?我在我的代码中使用了以下类似的数值: public const uint WM_CAP_DRIVER_CONNECT = 0x40a; public const uint WM_CAP_DRIVER_DISCONNECT = 0x40b; public const uint WM_CAP_EDIT_COPY = 0x41e; public const uint WM_CAP_SET_PREVIEW = 0x432; public const uint WM_CAP_SET_OVERLAY = 0x433; public const uint WM_CAP_SET_PREVIEWRATE = 0x434; - Zain Ul Abidin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接