当前位置: 首页 > news >正文

QMake指南(Pro文件指南)

QMake相关介绍

QMake是一个通过编译Pro文件自动生成Makefile文件的工具。

这里扩展一下make、makefile、cmake、CMakeLists.txt、qmake等之间的关系:

  1. 首先,当我们初学helloworld项目的时候,一般是用gcc命令进行编译的,gcc是GNU Compiler Collection(GNU编译器套件),可以理解为编译器。
  2. 当项目比较复杂,需要编译的文件比较多时,再用gcc命令逐个编译就不现实了,所以就衍生出了makefile文件和make工具
  3. makefile是一个项目的编译规则文件,描述了项目的编译和链接的规则,比如项目中哪些文件需要编译,哪些文件不需要编译,哪些文件要先编译,哪些文件要后编译等等。当然文件编译还是需要通过调用gcc编译器(或其他编译器)去编译文件。
  4. make工具就是通过调用makefile文件进行编译和链接项目。
  5. 因为makefile文件非常复杂,需要使用CMake工具来生成,CMake工具支持跨平台生成makefile文件。
  6. 但是,CMake是怎么生成makefile文件的呢?这里又牵扯到了一个CMakeLists.txt文件(相信大家对这个很熟悉)。
  7. CMakeLists.txt文件有自己的语法、函数,主要是定义项目文件路径、项目环境以及编译信息等。这个文件一般都由IDE自动生成,当然自己能写是最好的。
  8. qmake和CMake的意义是差不多的,都是用来生成makefile文件的,有区别的是qmake还包含了moc(元对象编译器)和uic(用户界面编译器)的构建规则。
  9. qmake是怎么生成makefile文件的呢?当然是通过Qt的工程文件(.pro文件)来生成,那么下面就详细介绍一下pro文件相关的语法

内置变量

qmake的基本行为受到变量声明的影响,这些声明定义了每个项目的构建过程。其中一些声明资源,例如HEADERS和SOURCES是每个平台所共有的。其他的则是用来定制特定平台上的编译器和链接器的行为。

QT

QT变量主要包含项目所需的Qt模块,Qt基础模块有以下:

基础模块描述
core(默认包含该模块)核心模块
gui(默认包含该模块)图形用户界面模块
multimedia音频、视频、广播和相机功能模块
multimedia widgets用于实现多媒体功能的widget模块
network网络编程模块
qml支持QML和JavaScript语言的模块
quick构建具有自定义用户动态界面的应用程序模块
quick controls提供轻量级QML控件类型的模块
quick dialogs为quick应用程序提供对话框的模块
quick layouts基于Qt Quick 2项目的用户界面布局模块
quick testQML单元测试模块
sql数据库模块
testQt应用程序和库的单元测试模块
widgets扩展GUI窗口类的模块

默认Qt项目将包含core和gui两个模块,如果项目不需要界面显示,则需要添加以下代码:

QT -= gui

Qt还扩展了很多附加模块:

附加模块开发平台应用平台描述
Active QtWindowsWindows支持ActiveX和COM模块
Qt 3DAllAll实时仿真系统,支持2D和3D渲染
Qt Android ExtrasAllAndroid为Android提供特定于平台的API
Qt BluetoothAllAndroid,ios,Linux,MacOS,UWP提供对蓝牙的访问
Qt ConcurrentAllAll在不使用低级线程语法下编写多线程程序
Qt D-BusAllAll通过D-Bus协议进行进程间通信
Qt GamepadAllAndroid,ios,macOS,tvOS,Linux,Windows,QNX支持游戏手柄硬件的应用程序
Qt Graphical EffectsAllAll用于Qt Quick 2的图形效果
Qt HelpAllAll像Qt助手一样将文档集成到应用程序中
Qt Image FormatsAllAll支持其他图形格式:TIFF、MNG、TGA、WBMP
Qt LocationAllAll在QML应用程序中显示地图、导航和地点内容
Qt Mac ExtrasAllmacOS为macOS提供特定于平台的API
Qt NFCAllAndroid,Linux支持NFC硬件访问
Qt PositioningAllAndroid,ios,macOS,Linux,UWP提供对位置、卫星和区域监控类的访问
Qt Print SupportAllAll提供更便捷的打印类
Qt Quick ExtrasAllAll提供一组专门的Quick控件
Qt Quick TimelineAllAll启用基于关键帧的动画和参数化
Qt Quick WidgetsAllAll提供用于显示Quick用户界面的小部件类
Qt Remote ObjectsAllAll提供一种用于在进程或设备之间共享QObject的API
Qt SCXMLAllAll提供从SCXML文件中创建状态机并将其嵌入到应用程序中
Qt Serial PortAllWindows,Linux,macOS,QNX提供对硬件和虚拟串口的访问
Qt SpeechAllAll except QNX提供对语音方面的支持
Qt SVGAllAll用于显示SVG文件内容的类
Qt UI ToolsAllAll运行时动态加载基于QWidget的表单类
Qt WebChannelAllAll提供从HTML客户端对QObject或QML对象的访问,用来实现应用程序和HTML/JavaScript客户端的联系
Qt WebEngineAllWindows,Linux,macOS将Chromium浏览器引擎嵌入应用程序中
Qt WebSocketsAllAll提供符合RFC6455的Websocket通信
Qt WebViewAllAll支持原生API显示Web浏览器视图
Qt Windows ExtrasAllWindows为Windows提供特定于平台的API
以下在商业许可证或GNU通用公共许可证V3下可以用的附加模块:
Qt ChartsAllAll图表模块
Qt Data VisualizationAllAll3D数据可视化组件
Qt Lottie AnimationAllAll以JSON格式渲染图形和动画的QML API
Qt Virtual KeyboardAllLinux,Windows desktop,Boot to Qt targets支持本地的虚拟键盘
Qt Quick 3DAllAll提供Quick 3D相关API
Qt Quick WebGLAllWebGL-enabled web browsers支持WebGL插件

HEADERS

项目包含头文件列表:

HEADERS += widget.h

SOURCES

项目包含源文件列表:

SOURCES += main.cpp \
          widget.cpp

FORMS

项目包含UI文件列表:

FORMS += widget.ui

RESOURCES

指定目标的资源文件(qrc)的名称。

RESOURCES += src.qrc

INCLUDEPATH

指定编译项目时要包含当前目录。例如:

INCLUDEPATH = c:/msdev/include d:/stl/include

LIBS

该变量包含链接到工程的库列表。可以使用Unix平台-L(库路径)和-l(库名称)的标识,qmake会正确处理Windows和symbian平台上的这些库。例如:

LIBS += -L./lib -ltest   # 链接到./bin目录下的test.lib
LIBS += -ltest           # 链接到当前目录下的test.lib

CONFIG

通用配置,主要包含编译器选项和属性以及链接库标志。

选项描述
release该项目以发布模式构建。如果同时定义了debug,则将忽略release
debug该项目以调试模式构建。
debug_and_release该项目以调试和发布模式构建。
debug_and_release_target该项目使用debug和release两种模式构建,目标会被构建到debug和release两个目录下
build_all如果指定了debug_and_release,项目默认以调试和发布模式构建。
autogen_precompile_source自动生成一个.cpp文件,包含.pro文件中指定的预编译头文件。
ordered当使用subdirs模板时,该选项指定按照列出的目录顺序构建。
precompile_header项目支持预编译头文件
precompile_header_c项目支持对C的预编译头文件
warn_on编译器警告信息输出打开
warn_off编译器警告信息输出关闭
exceptions默认启用异常
exceptions_off关闭异常
ltcg启用链接时间代码生成,默认是关闭的
rtti启用对RTTI的支持,默认情况下使用编译器的默认值
rtti_off关闭对RTTI的支持,默认情况下使用编译器的默认值
stl启用对STL的支持,默认情况下使用编译器的默认值
stl_off关闭对STL的支持,默认情况下使用编译器的默认值
thread启用对线程的支持,当CONFIG包含了qt模块时,这将被启用,这是默认的。
c99启用对C99的支持。如果编译器不支持C99,或者不能选择C标准,这个选项就没有作用。默认情况下使用编译器的默认值
c11启用对C11的支持。如果编译器不支持C11,或者不能选择C标准,这个选项就没有作用。默认情况下使用编译器的默认值
strict_c禁用对C编译器扩展的支持。默认情况下被启用。
c++11启用对C++11标准的支持。如果编译器不支持C++11,或者不能选择C++标准,这个选项就没有作用。默认情况下是启用的。
c++14启用对C++14标准的支持。如果编译器不支持C++14,或者不能选择C++标准,这个选项就没有作用。默认情况下使用编译器的默认值。
c++1z / c++17启用对C++17标准的支持。如果编译器不支持C++17,或者不能选择C++标准,这个选项就没有作用。默认情况下是禁用的。
c++2a启用对C++2a标准的支持。如果编译器不支持C++2a,或者不能选择C++标准,这个选项就没有作用。默认情况下是禁用的。
c++latest启用编译器所支持的最新C++标准的支持,默认情况下是禁用的。
strict_c++禁用对C++编译器扩展的支持。默认情况下是被启用的。
depend_includepath启用将INCLUDEPATH的值追加到DEPENDPATH。默认启用
lrelease对 TRANSLATIONS 和 EXTRA_TRANSLATIONS 中列出的所有文件运行 lrelease。如果 embed_translations 没有被设置,请将生成的 .qm 文件安装到 QM_FILES_INSTALL_PATH。使用 QMAKE_LRELEASE_FLAGS 来为 lrelease 调用添加选项。默认情况下不设置。
embed_translations在QM_FILES_RESOURCE_PREFIX下的可执行文件中嵌入从lrelease生成的翻译。需要lrelease也被设置。默认情况下不设置。
create_libtool为当前构建的库创建一个libtool.la文件
create_pc为当前构建的库创建一个pkg-config .pc 文件。
no_batch仅限NMake。关闭NMake批处理规则或推理规则的生成。
skip_target_version_ext在Windows下,禁止自动附加在DLL文件名上的版本号。
suppress_vcproj_warnings禁止VS项目生成警告信息。
windeployqt在链接后自动调用windeployqt,并将输出添加为部署项。
dont_recurse禁止qmake递归当前子项目
no_include_pwd不要将当前目录添加到INCLUDEPATHS中
compile_include_sources默认情况下,qmake不编译包含在其他源文件中的源文件

当您使用该debug_and_release选项(Windows 下的默认设置)时,项目将被处理三次:一次生成“元”Makefile,另外两次生成 Makefile.Debug 和 Makefile.Release。

在后者构建时,debugorrelease选项被附加到CONFIG. 这使得执行特定于构建的任务成为可能。

以下选项定义应用程序或库的类型:

选项描述
qt默认定义。该项目依赖Qt库
x11该项目是一个X11应用程序或库
testcase该项目是自动化测试。检查并添加到生成的Makefile中以运行测试
insignificant_test自动测试的退出代码将被忽略。
windows该项目是一个win32窗口应用程序
console该项目是一个win32控制台应用
shared该项目是一个共享项目
dll该项目是一个动态库项目,构建后自动生成dll文件
dylib该项目是一个Mac OS的动态库项目,构建后自动生成dylib文件
static该项目是一个静态库项目
staticlib该项目是一个静态库项目
plugin该项目是一个插件项目
designer该项目是一个Qt designer插件项目
uic3
no_Iflags_merge配置该项使LIBS的库列表不会被简化为唯一名称的列表
resources配置rcc资源模块

DESTDIR

指定目标(可执行文件)放置的路径

SUBDIRS

该变量和subdirs模板类型一起使用时,指定项目需要构建的所有子项目(子目录)名称。包含的子目录必须包含自己的pro文件。例如:

SUBDIRS = kernel \
          tools \
          myapp

注意:当CONFIG 配置添加了 ordered,则SUBDIRS包含子目录的顺序决定了构建的顺序。

TARGET

指定目标文件的名称。默认包含项目文件的基本名称。例如:

TARGET = myapp

TEMPLATE

指定生成项目时要使用的模板名称。模板选项包含以下:

选项描述
app(默认)。构建为应用程序
lib构建为库
subdirs构建子目录
aux不构建任何东西
vcapp构建为Windows系统下的VS应用程序项目
vclib构建为Windows系统下的库项目

DEPENDPATH

指定qmake要扫描的目录列表,用来解决项目中的依赖关系。该变量在qmake抓取你的源代码中#include的头文件时使用。

DEFINES

qmake将该变量的值作为C编译器的预处理宏。

VERSION

如果模板类型为app,则表示应用程序的版本号;如果模板类型为lib,则表示库的版本号。

win32:VERSION = 1.2.3.4 # major.minor.patch.build
else:VERSION = 1.2.3    # major.minor.patch

DLLDESTDIR

指定复制目标dll的目录。注意:该变量只适用于Windows平台。

RCC_DIR

指定Qt资源编译器输出文件的目录。例如:

unix:RCC_DIR = ../myproject/resources
win32:RCC_DIR = c:/myproject/resources

_PRO_FILE_

pro文件的绝对路径,包含pro文件名称,例如:

message(_PRO_FILE_)
# out:
# Project MESSAGE: E:/Test/ProTest/ProTest.pro

_PRO_FILE_PWD_

pro文件的目录路径,不包含pro文件名称,例如:

message(_PRO_FILE_PWD_)
# out:
# Project MESSAGE: E:/Test/ProTest

测试函数

CONFIG(config)

这个函数是用来测试放在CONFIG变量中的变量的,功能类似于条件作用域。函数的第二个参数是用来指定互斥条件的,例如:

# test1:
  CONFIG += debug
  CONFIG(debug) : message(debug build!)
  CONFIG(release) : message(release build!)
  # out: 因为没有指定互斥量,所以两种输出都将执行
  # Project MESSAGE: debug build!
  # Project MESSAGE: release build!
  # Project MESSAGE: debug build!
  # Project MESSAGE: release build!
  # Project MESSAGE: debug build!
  # Project MESSAGE: release build!

# test2:
  CONFIG += debug
  CONFIG(debug, debug|release) : message(debug build!)
  CONFIG(release, debug|release) : message(release build!)
  # out: 当前指定debug模式,所以只会输出debug build!
  # Project MESSAGE: debug build!
  # Project MESSAGE: debug build!
  # Project MESSAGE: debug build!

# test3:
  CONFIG += release
  CONFIG(debug, debug|release) : message(debug build!)
  CONFIG(release, debug|release) : message(release build!)
  # out:当前指定release模式,所以只会输出release build!
  # Project MESSAGE: release build!
  # Project MESSAGE: release build!
  # Project MESSAGE: release build!
  
# test4:
  CONFIG(debug, debug|release) : message(debug build!)
  CONFIG(release, debug|release) : message(release build!)
  # release构建 out:
  # Project MESSAGE: release build!
  # Project MESSAGE: release build!
  # Project MESSAGE: debug build!
  
  # debug构建 out:
  # Project MESSAGE: debug build!
  # Project MESSAGE: debug build!
  # Project MESSAGE: release build!

注意上面测试示例输出的内容为什么会有三次呢?因为在缺省情况下,qmake将创建三个makefile文件,分别为Makefile,Makefile.debug以及Makefile.release三个。所以当测试示例4中没有指定固定的构建配置,IDE选择release模式时Makefile和Makefile.release两个文件对应输出release build!,而Makefile.debug会输出debug build!

contains(variablename, value)

如果变量variablename包含value值,则成功,否则失败。

# 如果QT添加了network模块,则把对应的头文件和源文件添加到项目中
contains( QT, network ) {
    message( "Configuring for network build..." )
    HEADERS += network.h
    SOURCES += network.cpp
}

上面示例代码是contains的主要应用场景,通过判断是否添加某些模块来自动添加对应模块的代码文件。

count(variablename,number)

如果variablename比哪里中包含指定数量number的值列表则成功,反之失败。
该函数主要用于确保作用域内的声明仅在变量包含正确数值的情况下才会被处理。例如:

options = $$find(CONFIG, "debug") $$find(CONFIG, "release")
count(options, 2) { message(Both release and debug specified.)}

include(filename)

意义和C++中的include是一样的,将指定的文件名包含到当前项目中,返回true表示包含成功,反之包含失败。你可以通过使用这个函数作为条件来检查是否被包含。示例如下:

!include(test.pri) {
  message("test.pri加载失败")
}

一般我们用于包含pri文件。

if(condition)

条件判断,和C++的if语句一样。

for(iterate, list)

循环变量list中的所有值。示例如下:

LIST = 1 2 3
for(a, LIST):message(I see a file.$${a}!)
# out:
# Project MESSAGE: I see a file.1!
# Project MESSAGE: I see a file.2!
# Project MESSAGE: I see a file.3!

message(string)

将字符串以一般信息输出给用户。

message( "This is a message" )  # 引号是可选的,建议使用

注意:默认情况下,qmake为特定项目生成的每个makefile都会输出信息。如果你想确保每个项目只输出一次信息,请参考下面的示例:

!build_pass:message( "This is a message" )

mkpath(dirPath)

创建目录路径dirPath,这个函数是QDir::mkpath函数的一个封装器。
该函数在Qt5.0中被引入。

system(command)

执行shell命令。执行命令返回0表示成功,否则失败。示例如下:

system("ls /bin"): HAS_BIN = TRUE

unset(variablename)

将变量名从当前上下文中移除。

NARF = zort
unset(NARF)
!defined(NARF, var) {
    message("NARF is not defined.")
}
# out:
# Project MESSAGE: NARF is not defined.

versionAtLeast(variablename, versionNumber)

比较variablename的版本好是否大于等于versionNumber。一般用于判断当前Qt的版本,示例如下:

versionAtLeast(QT_VERSION, 5.12.0):message("Qt 5.12.0")
# out:
# Project MESSAGE: Qt 5.12.0

versionAtMost(variablename, versionNumber)

比较variablename的版本好是否小于等于versionNumber。一般用于判断当前Qt的版本,示例如下:

versionAtMost(QT_VERSION, 5.15.2):message("Qt 5.15.2")
# out
# Project MESSAGE: Qt 5.15.2

warning(string)

以警告的形式输出字符串。同时会在IDE的问题栏中输出信息。

error(string)

以错误的形式输出字符串。同时会在IDE的问题栏中输出信息。

eval(string)

使用qmake语法规则评估字符串的内容并返回true。定义新的变量或修改现有变量的值。示例如下:

# TARGETS 变量没有定义时
eval(TARGETS = myapp) {
    message($$TARGETS)
}
# out:
# Project MESSAGE: myapp

# TARGET 变量已定义时
message($$TARGET)
eval(TARGET = myapp) {
    message($$TARGET)
}
# out: ProTest是当前测试项目的名称
# Project MESSAGE: ProTest
# Project MESSAGE: myapp

exists(filename)

判断文件名的文件是否存在。如果存在则返回成功,否则返回失败。

isEmpty(variablename)

如果variablename变量为空则返回true,反之返回false。等价于count(variablename,0)。例如:

isEmpty(CONFIG) {
CONFIG += qt warn_on debug}

替换函数

basename(variablename)

返回指定文件的base文件名称。例如:

FILE = /etc/passwd
FILENAME = $$basename(FILE)
# out:
# passwd

dirname(file)

返回指定文件的目录部分。例如:

FILE = /etc/X11R6/XF86Config
DIRNAME = $$dirname(FILE)
# out:
# /etc/X11R6

find(variablename, substr)

在variablename变量的所有值中查找匹配substr字符串的值,substr可能是正则表达式。例如:

MY_VAR = one two three four
MY_VAR2 = $$find(MY_VAR, t.*)
message($$MY_VAR2)
# out:
# two three

join(variablename,glue,before,after)

使用glue连接variablename变量中的值。如果变量的值非空,在值前面加一个前缀before,在值的后面加一个后缀after。Variablename是必须参数,其它参数默认是空字符串。如果需要在glue、before、after中对空格进行编码,必须对它们使用引号。例如:

MY_VAR = one two three four
MY_VAR2 = $$join(MY_VAR, " -L", -L) -Lfive
MY_VAR3 = $$join(MY_VAR, " -L", -L, -M)

message($$MY_VAR2) #out: -Lone -Ltwo -Lthree -Lfour -Lfive
message($$MY_VAR3) #out: -Lone -Ltwo -Lthree -Lfour-M

member(variablename,position)

返回variablename变量的值列表中位置为position的值。如果找不到对应位置,则返回空字符串。如果不指定position参数,则默认为0,返回值列表中的第一个值。

可以理解variablename为一个数组,而position为数组的下标。例如:

MY_VAR = one two three four
MY_VAR2 = $$member(MY_VAR, 3)
MY_VAR3 = $$member(MY_VAR, 2)

message($$MY_VAR2) #out: four
message($$MY_VAR3) #out: three

replace(string, old_string, new_string)

在string字符串中找到old_string字符串,然后用new_string进行替换。例如:

MESSAGE = This is a tent.
message($$replace(MESSAGE, tent, test))
# out:
# This is a test.

unique(variablename)

返回variablename变量的所有值,如果有重复的则删除。例如:

ARGS = 1 2 3 2 5 1
ARGS = $$unique(ARGS) #out: 1 2 3 5

其他

自定义变量以及变量内容的访问

CUSTOMVAL = ./bin     # 自定义变量名为CUSTOMVAL
message($$CUSTOMVAL)  # 输出CUSTOMVAL变量值
# out:
# Project MESSAGE: ./bin

值列表(每个值之间用空格分割)

LIST = 1 2 3
for(a, LIST):message(I see a file.$${a}!)
# out:
# Project MESSAGE: I see a file.1!
# Project MESSAGE: I see a file.2!
# Project MESSAGE: I see a file.3!

单值(单值且中间包含空格时需要用双引号来包含)

LIST = "1 2 3"
for(a, LIST):message(I see a file.$${a}!)
# out:
# Project MESSAGE: I see a file.1 2 3!

注释

# 注释是以'#'符合作为注释的开头,以行末尾作为结束

如果要在变量中使用’#'字符,必须使用内置变量LITERAL_HASH,示例如下:

urlPieces = http://doc.qt.io/qt-5/qtextdocument.html pageCount
message($$join(urlPieces, $$LITERAL_HASH))
# out:
# Project MESSAGE: http://doc.qt.io/qt-5/qtextdocument.html#pageCount

# 注意,想要在变量中使用'#'字符,只有这一种方式,用双引号来包含也是没有用的

QMake指南(Pro文件指南)_枫焱宇的博客-CSDN博客_qmake生成pro 

相关文章:

  • Qt常用命令和pro参数
  • Qt creator中项目的构建配置和运行设置的步骤
  • UTF-8中Bom和无 Bom区别
  • MSVC编译器介绍
  • QT解决MSVC中文乱码问题
  • qt中的toUtf8, toLatin1, Local8bit, toUcs4
  • C++队列queue用法详解
  • C++中,new/delete和malloc/free的区别
  • C++多线程讲解
  • 高速摄像机
  • Windows使用curl发送GET、POST请求
  • curl是什么
  • 极光推送使用curl调用REST API出现测试出现{“error“:{“code“:1002,“message“:“Missing parameter“}}
  • Window系统中Hosts文件介绍
  • DNS 域名解析流程
  • 【个人向】《HTTP图解》阅后小结
  •  D - 粉碎叛乱F - 其他起义
  • Java,console输出实时的转向GUI textbox
  • java多线程
  • Java方法详解
  • passportjs 源码分析
  • 初识MongoDB分片
  • 今年的LC3大会没了?
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 利用DataURL技术在网页上显示图片
  • 排序(1):冒泡排序
  • 盘点那些不知名却常用的 Git 操作
  • 前端之Sass/Scss实战笔记
  • 鱼骨图 - 如何绘制?
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 阿里云服务器购买完整流程
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​决定德拉瓦州地区版图的关键历史事件
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • # 计算机视觉入门
  • #NOIP 2014# day.1 T2 联合权值
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (+4)2.2UML建模图
  • (02)Hive SQL编译成MapReduce任务的过程
  • (23)Linux的软硬连接
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (层次遍历)104. 二叉树的最大深度
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (一)基于IDEA的JAVA基础1
  • (转)Unity3DUnity3D在android下调试
  • ***监测系统的构建(chkrootkit )
  • .Net Web项目创建比较不错的参考文章
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET4.0并行计算技术基础(1)
  • .net6 webapi log4net完整配置使用流程
  • .Net各种迷惑命名解释