准备工作

  • 安装 Scoop
  • 安装 Visual Studio 2022
  • 安装 Ninja v1.10.2
  • 安装 Python v3.10.4 和 Numpy
  • 安装 wget 1.21.3
  • 安装 7zip 21.07
  • 安装 Cuda SDK v11.7
  • 一键下载 OpenCV 4.5.5 源码并编译
  • 部署
  • 测试 OpenCV

安装 Scoop

打开 PowerShell 后执行:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-WebRequest get.scoop.sh | Invoke-Expression

安装参考: https://scoop.sh

安装 Visual Studio 2022

  1. https://visualstudio.microsoft.com 下载你喜欢的版本,推荐免费的 Community 版本
  2. 安装 使用C++的桌面开发 工作负荷

    使用所选工具(包括 MSVC、CLang、CMake 或 MSBuild)生成适用于 Windows 的现代 C++ 应用

安装 Ninja v1.10.2

scoop install ninja@1.10.2

安装 Python v3.10.4 和 Numpy

scoop install python@3.10.4
pip install numpy

安装 wget 1.21.3

scoop install wget@1.21.3

安装 7zip 21.07

scoop install 7zip@21.07

安装 Cuda SDK

安装流程: https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html#installing-cuda-development-tools

一键下载 opencv 4.5.5 源码并编译

download_and_build_opencv.bat

@ECHO OFF
ECHO -- Downloading OpenCV Source Code --
wget https://github.com/opencv/opencv/archive/refs/tags/4.5.5.zip -O opencv-4.5.5.zip
IF NOT ERRORLEVEL 0 GOTO :end
7z x opencv-4.5.5.zip
IF NOT ERRORLEVEL 0 GOTO :end
wget https://github.com/opencv/opencv_contrib/archive/refs/tags/4.5.5.zip -O opencv_contrib-4.5.5.zip
IF NOT ERRORLEVEL 0 GOTO :end
7z x opencv_contrib-4.5.5.zip
IF NOT ERRORLEVEL 0 GOTO :end
SET OPENCV_SRC_DIR=%cd%/opencv-4.5.5
SET OPENCV_CONTRIB_SRC_DIR=%cd%/opencv_contrib-4.5.5
SET OPENCV_BUILD_DIR=%cd%/opencv-build
SET SCOOP_ROOT=%USERPROFILE%/scoop
SET CUDA_SDK_DIR="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.7"
@REM CUDA_ARCH=8.6 for GeForce RTX 3080 Ti
@REM Choice value for your GPU with: https://developer.nvidia.com/cuda-gpus
SET CUDA_ARCH=8.6
ECHO -- Starting OpenCV Configuration --
call "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
@REM If you want to use the Visual Studio 2019 MSVC:
@REM call "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat" -vcvars_ver=14.29
call cmake.exe ^
-S%OPENCV_SRC_DIR% ^
-B%OPENCV_BUILD_DIR% ^
-DCMAKE_INSTALL_PREFIX=%OPENCV_BUILD_DIR%/install ^
-DOPENCV_EXTRA_MODULES_PATH=%OPENCV_CONTRIB_SRC_DIR%/modules ^
-DPYTHON3_EXECUTABLE=%SCOOP_ROOT%/apps/python/current/python.exe ^
-DPYTHON3_INCLUDE_DIR=%SCOOP_ROOT%/apps/python/current/include ^
-DPYTHON3_LIBRARY=%SCOOP_ROOT%/apps/python/current/libs/python310.lib ^
-DPYTHON3_NUMPY_INCLUDE_DIRS=%SCOOP_ROOT%/apps/python/current/Lib/site-packages/numpy/core/include ^
-DPYTHON3_PACKAGES_PATH=%SCOOP_ROOT%/apps/python/current/Lib/site-packages ^
-DPYTHON_INCLUDE_DIRS=%SCOOP_ROOT%/apps/python/current/include ^
-DPYTHON_LIBRARIES=%SCOOP_ROOT%/apps/python/current/libs/python310.lib ^
-DCUDA_TOOLKIT_ROOT_DIR=%CUDA_SDK_DIR% ^
-DENABLE_FAST_MATH=ON ^
-DCUDA_FAST_MATH=ON ^
-DWITH_CUDA=ON ^
-DWITH_CUDNN=ON ^
-DOPENCV_DNN_CUDA=ON ^
-DCUDA_ARCH_BIN=%CUDA_ARCH% ^
-DCUDA_ARCH_PTX=%CUDA_ARCH% ^
-DWITH_OPENGL=ON ^
-DWITH_GSTREAMER=ON ^
-DBUILD_opencv_python3=ON ^
-DCMAKE_CONFIGURATIONS_TYPES=Release ^
-DCMAKE_BUILD_TYPE=Release ^
-DOPENCV_PYTHON3_VERSION=3.10 ^
-DBUILD_TESTS=OFF ^
-DBUILD_PERF_TESTS=OFF ^
-DBUILD_JAVA=OFF ^
-DBUILD_opencv_objc_bindings_generator=OFF ^
-DBUILD_opencv_js=OFF ^
-GNinja ^
-DBUILD_opencv_world=ON
IF NOT ERRORLEVEL 0 GOTO :end
ECHO -- OpenCV Configuration has finished, press any key to proceeding to build phase... --
PAUSE
call cmake.exe --build %OPENCV_BUILD_DIR% --target install
ECHO -- OpenCV Build has finished, press any key to exit... --
PAUSE
:end

点我下载:download_and_build_opencv.bat

部署

将编译成功后的目录 %OPENCV_BUILD_DIR%/install/x64/vc17/bin 下的:

  • opencv_img_hash455.dll
  • opencv_videoio_ffmpeg455_64.dll
  • opencv_world455.dll 拷贝到 Windows 的 %PATH% 目录,例如: %SCOOP_ROOT%/apps/python/current/Scripts

测试 OpenCV

python -c "import cv2; print(cv2.__version__); print(cv2.cuda.getCudaEnabledDeviceCount())"

内容部分占满页面的剩余高度

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dock.Full</title>
  <style type="text/css">
    html,
    body,
    #full {
      color: #EFEFEF;
      background-color: #423F3E;
      margin: 0;
      padding: 0;
      height: 100%;
    }

    #full {
      background-color: #171010;
      display: flex;
      flex-direction: column;
    }

    #someid {
      background-color: #362222;
      flex-grow: 1;
    }
  </style>
</head>

<body>
  <div id="full">
    <div id="header">Dock.Top</div>
    <div id="someid">Dock.Full</div>
  </div>
</body>

</html>

线上预览

登录框居中显示

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style type="text/css">
    html,
    body,
    #parent {
      color: #EFEFEF;
      background-color: #423F3E;
      margin: 0;
      padding: 0;
      height: 100%;
    }

    #parent {
      background-color: #171010;
      /* 使用Flex布局 */
      display: flex;
      /* 主轴位于中间 */
      justify-content: center;
      /* 交叉轴位于中间 */
      align-items: center;
    }

    #someid {
      background-color: #362222;
      width: 200px;
      height: 200px;
    }
  </style>
</head>

<body>
  <div id="parent">
    <div id="someid">Dialog Content</div>
  </div>
</body>

</html>

线上预览

问题出现

今天在Windows里使用 Cocos-Quick 创建的项目在使用ADT往Android里面部署的时候遇到了下面的问题 {% img /images/20160420/adt_add_library_bug.webp %}

出现上图的情况是这样的操作步骤:

  1. 添加 D:\Tools\Quick-Cocos2dx-Community\cocos\platform\android\java 到 Library 引用里
  2. 点击 OK
  3. 重新打开这个配置界面

问题解决

测试了许久后,无意间发现有次选错了路径后Library正常了

当时我选择的 Quick 是 ** F:\Quick-Cocos2dx-Community ** 选择的项目目录是 ** F:\Code\Cocos\QuickHello **

终于发现了解决这个BUG的办法:*** 将项目和想要引用的Library放在同一个分区内 *** 遂将项目和 Quick 放到同一个分区内后问题解决,效果如下: {% img /images/20160420/adt_add_library_bug_ok.webp %}

引起这个错误的原因是:*** Eclipse 本身并不是给Windows 这种多分区系统定制的,本身是用来给 MAC 或者 Linux 这种以目录为结构的系统使用的 ***

不能再构造函数内使用 shared_from_this() 函数

class class_a : public std::enable_shared_from_this<class_a>
{
public:
    class_a(void)
    {
        auto self(shared_from_this());// 这里会报 bad_weak_ptr 错误
    }
};

子类无法重复继承

class class_a : public std::enable_shared_from_this<class_a>
{
};

class class_b : public class_a, public std::enable_shared_from_this<class_a>
{
};

这段代码将无法通过编译。 如果想返回子类的 shared_from_this 指针,则可以进行如下操作

class class_a : public std::enable_shared_from_this<class_a>
{
public:
    virtual ~class_a()// 为了确保 dynamic_pointer_cast 可以工作,需要父类拥有虚函数。
    {}
};

class class_b : public class_a
{
public:
    std::shared_ptr<class_b> shared_from_this(void)
    {
        return std::dynamic_pointer_cast<class_b>(class_a::shared_from_this());
    }
};

通常来说如果定义一个类时,如果这个类可能被继承使用时,将这个类的析构函数定义为虚函数来确保析构的调用顺序

打开 Visual Studio 2013 Command Prompt

wget http://www.nasm.us/pub/nasm/releasebuilds/2.11.08/win32/nasm-2.11.08-win32.zip
unzip nasm-2.11.08-win32.zip -d C:/nasm
set PATH=%PATH%;C:/nasm/
wget https://www.openssl.org/source/openssl-1.0.2d.tar.gz
tar xzf openssl-1.0.2d.tar.gz
cd openssl-1.0.2d
perl configure VC-WIN32 --prefix=C:/openssl
ms\do_nasm
nmake -f ms\nt.mak
nmake -f ms\nt.mak install
echo "build successed."

这样编译不会产生 error A2070:invalid instruction operands 这个错误

有时,把旧项目转换成新版本项目时,旧版本项目里使用的 maxmin 宏无法在新版本中正常编译.

原因是: 新版本内有了新的函数 std::maxstd::min 函数来实现这一功能

这时可以尝试使用如下方案解决:

  1. 包含algorithm文件
#include <algorithm>
  1. 明确使用 std::max(a,b) 而非 max(a,b)
  2. 定义宏 NOMINMAX
#define NOMINMAX

在 Windows 里 DLL 卸载自身模块是无法通过字节调用 FreeLibrary 自己来实现的。

原因分析

在当前线程中调用 FreeLibrary 后,当前模块就会立即被释放掉,而当前线程还没有运行结束。所以程序就运行到了一块不可访问的内存里,产生异常从而导致程序崩溃。

解决方案

Windows 里面 提供了一条可以用来释放自身模块的函数 FreeLibraryAndExitThread 可以释放自身。

函数

VOID WINAPI FreeLibraryAndExitThread(_In_ HMODULE hLibModule, _In_ DWORD dwExitCode)
#!/usr/env python
"""
:license: MIT

Copyright (C) 2012 HustMoon

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from __future__ import with_statement

import zlib, sys

def b128encode(buf, escape=False):
    if not buf: return ''
    buf = bytearray(buf)
    res = bytearray((len(buf) * 8 + 6) / 7)
    p, s, i = 0, 1, 0
    for n in buf:
        res[i] = ((p << (8 - s) | (n >> s)) &amp; 0x7F) | 0x80; i += 1
        if s == 7:
            res[i] = n | 0x80; i += 1
            p, s = 0, 1
        else:
            p, s = n, (s + 1) % 8
    if s != 1:
        if s == 2 and p < 0x80:
            i -= 1
        else:
            p = (p << (8 - s)) &amp; 0x7F
        p = chr(p)
        if escape:
            p = p.encode('string-escape')
        res[i:] = p
    return str(res)

def dcode():
    code = r'''def code(__=code):
 (_______)=(globals)();del((_______)['code'])
 if(((_______).get('__doc__'))is((None))):
  (__)=(map)((ord),(__)[(339):]);(______)=[0]*(((((len)((__))+(1))*(7))/(8)));((___),(____),(_____))=((0),(0),(0))
  for((__))in((__)):
   if((__)<(128)):break
   if((____)==(0)):((___),(____))=((__),(1))
   else:
    (______)[(_____)]=((((___)<<(____))|(((__)&amp;(127))>>((7)-(____))))&amp;(255));(_____)+=(1);((___),(____))=((__),(((____)+(1))%(8)))
  if((__)<(128)):
   if((____)!=(0)):
    (__)=((((___)<<(____))|((__)>>((7)-(____))))&amp;(255))
   (______)[(_____):]=[((__))]
  elif((____)!=(0)):del((______)[(_____):])
  exec((''.join((map)((chr),(______))).decode('zlib')))in((_______))
  if(((_______).get('__doc__'))is((None))):(_______)['__doc__']=''
code()
'''
    return ''.join([c.encode('string-escape') if ord(c) < 128 else c
                    for c in zlib.compress(code, 9)])
dcode = dcode()

def encode(infile, outfile):
    with open(infile, 'rU') as fp:
        code = fp.read().rstrip('n') + 'n'
    code = b128encode(zlib.compress(code, 9), True)
    code = r'''# -*- coding: latin-1 -*-
code = '%s'
exec(code.decode('zlib'))
''' % (dcode + code)
    with open(outfile, 'wb') as fp:
        fp.write(code)

def decode(infile, outfile):
    with open(infile, 'rU') as fp:
        code = fp.read().rstrip('n') + 'n'
    code = code.replace("exec(code.decode('zlib'))", "exec(code.decode('zlib')"
        ".replace('exec','fp.write').replace('in((_______))',''))")
    with open(outfile, 'wb') as fp:
        eval(compile(code, 's', 'exec'), {'fp':fp})

def main():
    try:
        if sys.argv[1] == '-d':
            func = decode
            infile, outfile = sys.argv[2:4]
        else:
            func = encode
            infile, outfile = sys.argv[1:3]
    except ValueError:
        print >>sys.stderr, 'Usage: zipcode.py [-d] infile.py outfile.py'
        raise SystemExit(-1)
    func(infile, outfile)

if __name__ == '__main__':
    main()

不再需要导入atl.lib库了,直接包含以下头文件就可以了

#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>

要使用 AtlAxAttachControl 等函数的话,则必须初始化ATL模块

CComModule _Module;
_pAtlModule = &_Module;

原本的程序是控制台的。 但最近需要移植到windows下使用。 所以以MFC来做UI,代码照搬。 一面的一处代码使用了如下语句:

boost::split(v, data, boost::is_any_of(_T("n")));

结果编译时出现了如下错误:

error C4996: 'std::_Copy_impl'

而且是无论怎么修改都不认,差点打算重写了。

在网络中搜索了半天未果,只能自己分析。好在被我乱猜给猜对了。

我猜是因为boost的string和std的string产生了冲突而导致的。 所以添加上boost的string引用后,问题得以解决。

#include <boost/algorithm/string.hpp>

在此留字,希望能帮到遇到同样问题的人。