/// <summary>
        /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
        /// </summary>
        /// <param name="swapChainPtr"></param>
        /// <param name="syncInterval"></param>
        /// <param name="flags"></param>
        /// <returns>The HRESULT of the original method</returns>
        private int PresentHook(IntPtr swapChainPtr, int syncInterval, PresentFlags flags)
        {
            Frame();
            var swapChain = (SwapChain)swapChainPtr;
            {
                try
                {
                    #region Screenshot Request
                    if (Request != null)
                    {
                        try
                        {
                            DebugMessage("PresentHook: Request Start");
                            var startTime = DateTime.Now;
                            using (var texture = Resource.FromSwapChain <Texture2D>(swapChain, 0))
                            {
                                #region Determine region to capture
                                var regionToCapture = new Rectangle(0, 0, texture.Description.Width, texture.Description.Height);

                                if (Request.RegionToCapture.Width > 0)
                                {
                                    regionToCapture = Request.RegionToCapture;
                                }
                                #endregion

                                var theTexture = texture;

                                // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
                                Texture2D textureResolved = null;
                                if (texture.Description.SampleDescription.Count > 1)
                                {
                                    DebugMessage("PresentHook: resolving multi-sampled texture");
                                    // texture is multi-sampled, lets resolve it down to single sample
                                    textureResolved = new Texture2D(texture.Device, new Texture2DDescription
                                    {
                                        CpuAccessFlags    = CpuAccessFlags.None,
                                        Format            = texture.Description.Format,
                                        Height            = texture.Description.Height,
                                        Usage             = ResourceUsage.Default,
                                        Width             = texture.Description.Width,
                                        ArraySize         = 1,
                                        SampleDescription = new SampleDescription(1, 0), // Ensure single sample
                                        BindFlags         = BindFlags.None,
                                        MipLevels         = 1,
                                        OptionFlags       = texture.Description.OptionFlags
                                    });
                                    // Resolve into textureResolved
                                    texture.Device.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format);

                                    // Make "theTexture" be the resolved texture
                                    theTexture = textureResolved;
                                }

                                // Create destination texture
                                var textureDest = new Texture2D(texture.Device, new Texture2DDescription
                                {
                                    CpuAccessFlags    = CpuAccessFlags.None,         // CpuAccessFlags.Write | CpuAccessFlags.Read,
                                    Format            = Format.R8G8B8A8_UNorm,       // Supports BMP/PNG
                                    Height            = regionToCapture.Height,
                                    Usage             = ResourceUsage.Default,       // ResourceUsage.Staging,
                                    Width             = regionToCapture.Width,
                                    ArraySize         = 1,                           //texture.Description.ArraySize,
                                    SampleDescription = new SampleDescription(1, 0), // texture.Description.SampleDescription,
                                    BindFlags         = BindFlags.None,
                                    MipLevels         = 1,                           //texture.Description.MipLevels,
                                    OptionFlags       = texture.Description.OptionFlags
                                });

                                // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
                                theTexture.Device.CopySubresourceRegion(theTexture, 0, new ResourceRegion
                                {
                                    Top    = regionToCapture.Top,
                                    Bottom = regionToCapture.Bottom,
                                    Left   = regionToCapture.Left,
                                    Right  = regionToCapture.Right,
                                    Front  = 0,
                                    Back   = 1 // Must be 1 or only black will be copied
                                }, textureDest, 0, 0, 0, 0);

                                // Note: it would be possible to capture multiple frames and process them in a background thread

                                // Copy to memory and send back to host process on a background thread so that we do not cause any delay in the rendering pipeline
                                var request = Request.Clone(); // this.Request gets set to null, so copy the Request for use in the thread
                                ThreadPool.QueueUserWorkItem(delegate
                                {
                                    //FileStream fs = new FileStream(@"c:\temp\temp.bmp", FileMode.Create);
                                    //Texture2D.ToStream(testSubResourceCopy, ImageFileFormat.Bmp, fs);

                                    var startCopyToSystemMemory = DateTime.Now;
                                    using (var ms = new MemoryStream())
                                    {
                                        Resource.ToStream(textureDest, ImageFileFormat.Bmp, ms);
                                        ms.Position = 0;
                                        DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory));

                                        var startSendResponse = DateTime.Now;
                                        ProcessCapture(ms, request);
                                        DebugMessage("PresentHook: Send response time: " + (DateTime.Now - startSendResponse));
                                    }

                                    // Free the textureDest as we no longer need it.
                                    textureDest.Dispose();
                                    textureDest = null;
                                    DebugMessage("PresentHook: Full Capture time: " + (DateTime.Now - startTime));
                                });

                                // Make sure we free up the resolved texture if it was created
                                textureResolved?.Dispose();
                            }

                            DebugMessage("PresentHook: Copy BackBuffer time: " + (DateTime.Now - startTime));
                            DebugMessage("PresentHook: Request End");
                        }
                        finally
                        {
                            // Prevent the request from being processed a second time
                            Request = null;
                        }
                    }
                    #endregion

                    #region Example: Draw overlay (after screenshot so we don't capture overlay as well)
                    if (Config.ShowOverlay)
                    {
                        using var texture = Resource.FromSwapChain <Texture2D>(swapChain, 0);

                        if (Fps.GetFps() >= 1)
                        {
                            var fd = new FontDescription
                            {
                                Height          = 16,
                                FaceName        = "Arial",
                                Italic          = false,
                                Width           = 0,
                                MipLevels       = 1,
                                CharacterSet    = FontCharacterSet.Default,
                                OutputPrecision = FontPrecision.Default,
                                Quality         = FontQuality.Antialiased,
                                PitchAndFamily  = FontPitchAndFamily.Default | FontPitchAndFamily.DontCare,
                                Weight          = FontWeight.Bold
                            };

                            // TODO: do not create font every frame!
                            using var font = new Font(texture.Device, fd);
                            DrawText(font, new Vector2(5, 5), $"{Fps.GetFps():N0} fps", Color.Red);

                            if (TextDisplay is { Display : true })
                            {
                                DrawText(font, new Vector2(5, 25), TextDisplay.Text, new Color4(Color.Red.R, Color.Red.G, Color.Red.B,
                                                                                                Math.Abs(1.0f - TextDisplay.Remaining)));
                            }
                        }
                    }
        public void RunAllTool()
        {
            HTuple /*start = null, */ end = null;

            //帧率统计增加
            fps.IncreaseFrameNum();
            fpsCount++;
            if (fpsCount > 10)
            {
                fps.UpdateFps();
                fpsCount = 0;
            }
            RunStatus runStatus = new RunStatus(settingIndex, cameraIndex);

            runStatus.FpsStr = string.Format("FPS:{0:F1}|帧:{1}|", fps.GetFps(), fps.GetTotalFrameCount());

            List <ToolBase> runToolList = ToolsFactory.GetToolList(settingIndex);

            Tools.CreateImage.CreateImageTool createImageTool = runToolList[0] as Tools.CreateImage.CreateImageTool;
            HWndCtrl hWndCtrl = cameraShowUnit.HWndUnit.HWndCtrl;

            try
            {
                HTuple       toolStart = new HTuple(), toolEnd = new HTuple();
                StatusManger statusManger = StatusManger.Instance;
                statusManger.RuningStatus = RuningStatus.图像检测中;
                runningResultFlag         = false;
                toolErr = null;

                //开始运行所有工具
                HOperatorSet.CountSeconds(out toolStart);
                //外部触发处理
                if (isExtTrigger || isGrabber)
                {
                    createImageTool.SettExtTriggerData(ImageData);
                    isGrabber = false;
                }
                else
                {
                    createImageTool.SetExtTriggerDataOff();
                }
                StringBuilder MyStringBuilder = new StringBuilder();
                //string yy = MyStringBuilder.ToString();
                string datSend = "";

                foreach (var item in runToolList)
                {
                    if (item is IToolRun)
                    {
                        try
                        {
                            item.Run();
                            string result = item.IsOk ? "OK" : "NG";
                            Util.Notify(string.Format("{0}_{1} T={2:f2}ms,结果: {3}", item.Name, result, item.ExecutionTime, item.Result));
                            MyStringBuilder.Append(string.Format("{0}_{1}_T={2:f2}ms\r\n", item.Name, result, item.ExecutionTime));
                            if (item.IsOutputResults)
                            {
                                string dat = item.GetSendResult();
                                if (dat != string.Empty)
                                {
                                    datSend += dat;
                                    datSend += toolDelimiter;
                                }
                            }
                            runStatus.RunStatusList.Add(item.IsOk);
                        }
                        catch (Exception ex)
                        {
                            //Util.WriteLog(this.GetType(), ex);
                            Util.Notify(Level.Err, string.Format("工具{0}运行出现异常{1}", item.Name, ex.Message));
                            runStatus.RunStatusList.Add(false);
                        }
                    }
                    else
                    {
                        runStatus.RunStatusList.Add(true);
                    }
                }
                runStatus.ResultMessage = MyStringBuilder.ToString();

                //时间统计
                HOperatorSet.CountSeconds(out toolEnd);
                double toolTime = (toolEnd - toolStart) * 1000.0;   //toolStart) * 1000.0;
                Util.Notify(string.Format("工具组{0}图像处理用时{1:f2}ms", settingIndex, toolTime));
                #region 3 查找是否存在运行错误的工具
                toolErr = runToolList.Find(x => x.IsOk == false && x is IToolRun);

                if (toolErr == null && ToolsFactory.ToolsDic.Count > 0)
                {
                    runningResultFlag = true;
                    ok++;
                }
                else
                {
                    runningResultFlag = false;
                    ng++;
                    Util.Notify(string.Format("工具{0}图像处理检测到异常", toolErr.Name));
                }

                if (runningResultFlag == true)
                {
                    datSend = Util.TrimEndString(datSend, toolDelimiter);
                    datSend = Util.TrimStartString(datSend, toolDelimiter);
                }
                else
                {
                    datSend = ("Image" + Environment.NewLine + "Done" + Environment.NewLine);
                }

                if (isOffLineMode == false)         //这个变量与AutoUnit中的isTestMode不是同一个,这里是指AutoUnit的测试模式与离线模式。
                {
                    //SerialHelper.Instance.WriteCommdToSerial(datSend);
                    //非相机输出模式下就直接输出文本信息
                    if (!CommHandle.Instance.CommunicationParam.IsCamIOOutput &&
                        datSend != string.Empty)
                    {
                        Util.Notify(string.Format("发送结果: {0}", datSend));
                        autoUnit.RunCommWriteDataThread.WriteData(datSend);
                    }
                    else
                    {
                        if (StatusManger.Instance.IsInterlocking && CameraManger.CameraDic.ContainsKey(settingIndex) && runningResultFlag == false)
                        {
                            CameraManger.CameraDic[settingIndex].Output();   //结果NG时相机外部输出信号导通。
                        }
                    }
                }
                else
                {
                    if (runningResultFlag == false)
                    {
                        Util.Notify(string.Format("测试结果:{0}", "NG"));
                    }
                    else
                    {
                        Util.Notify(string.Format("测试结果:{0}", "OK"));
                    }
                }
                #endregion
                #region 4 显示所有的图形
                //HTuple showStart;
                //HOperatorSet.CountSeconds(out showStart);
                autoUnit.Invoke(new Action <HWndCtrl, List <ToolBase>, bool>((h, l, f) =>
                {
                    ShowResult(h, l, f);
                }), hWndCtrl, runToolList, runningResultFlag);

                #endregion

                HTuple end1;
                HOperatorSet.CountSeconds(out end1);
                double time1 = (end1 - toolEnd) * 1000.0;
                Util.Notify(string.Format("工具组{0}分析显示用时{1:f2}ms", settingIndex, time1));
            }
            catch (Exception)
            {
                //Util.WriteLog(this.GetType(), ex);
                Util.Notify(string.Format("图像处理异常"));
            }
            finally
            {
                HOperatorSet.CountSeconds(out end);
                double runTime = 0;
                runTime = (end - startExTime) * 1000.0;

                runStatus.OKCount   = ok;
                runStatus.NgCount   = ng;
                runStatus.CylceTime = runTime;
                RunStatus runStatusTmp = runStatus.DeepClone();
                autoUnit.RefreshUI(runStatusTmp);

                //指示可以来图像处理
                Util.Notify(string.Format("---工具组{0}运行完成,用时{1:f2}ms\r\n", settingIndex, runTime));
                imageRunFinishSignalFlow.Set();
                //离线模式
                if (isOffLineMode &&
                    createImageTool.OffLineMode == true &&
                    createImageTool.AllReadFinish == false)
                {
                    Task.Run(async delegate
                    {
                        await Task.Delay(1000);
                        if (isOffLineMode)
                        {
                            TrigerRun(this.settingIndex, true);  //离线测试模式时,接着触发下次离线测试。
                        }
                    });
                }
                else
                {
                    isOffLineMode = false;   //将该对象的离线模式标志复位,不再执行离线。
                }
            }
        }