前言
在C++编程中,了解程序的入口函数是至关重要的。入口函数是程序执行的起点,它接收参数并开始执行程序的主要逻辑。对于应用程序而言,有多种入口函数可供选择,每种都有其特定的用途和适用场景。本文将深入探讨C++程序开发中常见的入口函数。
概述
主要分成三类,控制台程序,图形用户界面GUI应用程序,动态库,他们分别是:
-
main
函数:- 主要用于命令行应用程序。
- 是 C/C++ 程序的入口函数。
- 接收
argc
和argv
参数,用于处理命令行参数。
-
WinMain
函数:- 主要用于 Windows 图形用户界面 (GUI) 应用程序。
- 是 Windows 程序的入口函数。
- 通过调用
RegisterClass
、CreateWindow
等函数来创建和管理窗口。 - 接收
hInstance
、hPrevInstance
、lpCmdLine
和nCmdShow
参数。
-
DllMain
函数:- 主要用于 DLL(动态链接库)。
- 在 DLL 加载或卸载时被调用,可以执行初始化或清理操作。
- 接收
hModule
、ul_reason_for_call
和lpReserved
参数。
-
_tmain
函数:- 是一个通用宏,根据项目字符集设置自动映射为
main
或wmain
函数。 - 可以根据项目设置在多字节或 Unicode 字符集下编译。
- 用于处理命令行参数。
- 是一个通用宏,根据项目字符集设置自动映射为
-
wmain
函数:- 类似于
main
函数,但接收的参数为宽字符集(Unicode)。 - 适用于需要处理非 ASCII 字符或跨语言环境的应用程序。
- 类似于
-
_tWinMain
函数:- 是一个通用宏,根据项目字符集设置自动映射为
WinMain
或wWinMain
函数。 - 可以根据项目设置在多字节或 Unicode 字符集下编译。
- 主要用于 Windows 图形用户界面 (GUI) 应用程序。
- 是一个通用宏,根据项目字符集设置自动映射为
-
wWinMain
函数:- 类似于
WinMain
函数,但接收的参数为宽字符集(Unicode)。 - 适用于需要处理非 ASCII 字符或跨语言环境的 Windows GUI 应用程序。
- 类似于
1. main函数:
C++程序中最常见的入口点,它是控制台应用程序的主函数。main函数可以带有参数,其签名通常为:
int main(int argc, char* argv[])
其中argc表示命令行参数的数量,argv是一个指向参数字符串数组的指针。通过argc和argv,我们可以获取用户在命令行输入的参数,从而进行相应的处理。
参数传递和解析
通过命令行传递参数给main
函数,并解析这些参数:
#include <iostream>
int main(int argc, char* argv[]) {
// 打印命令行参数数量
std::cout << "Number of command line arguments: " << argc << std::endl;
// 打印每个命令行参数
std::cout << "Command line arguments:" << std::endl;
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << std::endl;
}
// 解析命令行参数
for (int i = 1; i < argc; ++i) { // 注意从索引1开始,索引0是可执行文件的名称
std::string arg = argv[i];
if (arg == "-h" || arg == "--help") {
std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help Display this help message" << std::endl;
// 添加其他参数说明
return 0;
} else {
// 处理其他参数
}
}
// 如果没有参数,则执行默认操作
std::cout << "No arguments provided. Performing default operation." << std::endl;
return 0;
}
在这个示例中,main
函数接受两个参数:argc
表示命令行参数的数量,argv
是一个指向参数字符串数组的指针。我们首先打印命令行参数的数量,然后逐个打印每个参数的值。接着,我们解析命令行参数,检查是否存在特定的参数(例如-h
或--help
),如果存在,则打印帮助信息;最后,如果没有提供任何参数,则执行默认操作。
可以通过命令行传递参数并查看输出。
2. WinMain函数:
WinMain函数
:用于Windows GUI应用程序的入口函数。它类似于main函数,但不带参数,其签名通常为:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
其中hInstance是程序实例的句柄,hPrevInstance已经废弃,lpCmdLine是命令行参数字符串,nCmdShow是窗口显示标志。
#include <windows.h>
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
MessageBox(NULL, TEXT("一个简单的Win32应用程序"), TEXT("消息窗口"), MB_OK);
return 0;
}
3. DllMain函数:
DllMain函数
:用于动态链接库(DLL)的入口函数。它与main函数类似,但在DLL加载或卸载时调用,其签名通常为:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
/// <summary>
/// Windows下所有动态链接库(DLL)都必须包含的一个可选入口点函数,它在DLL加载或卸载时被操作系统自动调用
/// </summary>
/// <param name="hModule">当前被加载的DLL模块句柄</param>
/// <param name="dwReason">指示操作系统为何调用这个函数的值,可能的取值有</param>
/// <param name="lpReserved">保留参数</param>
/// <returns></returns>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD dwReason,
LPVOID lpReserved
)
{
UNREFERENCED_PARAMETER(hModule);
UNREFERENCED_PARAMETER(lpReserved);
switch (dwReason)
{
//表示该DLL被当前进程加载时调用
case DLL_PROCESS_ATTACH:
//表示新线程被创建时调用
case DLL_THREAD_ATTACH:
//表示线程退出时调用
case DLL_THREAD_DETACH:
//表示该DLL被当前进程卸载时调用
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
4. _tmain函数:
_tmain函数
:为了支持Unicode字符集,Microsoft的一些编译器提供了_tmain函数,它可以根据编译选项选择使用main或wmain。其签名通常为:
int _tmain(int argc, TCHAR* argv[])
-
如果项目的字符集设置为“多字节字符集”,则_tmain 会被映射为 main 函数。
-
如果项目的字符集设置为“Unicode 字符集”,则_tmain 会被映射为 wmain 函数。
5. wmain函数
wmain
函数是在 Windows 平台上使用宽字符(Unicode)编码的控制台应用程序中的入口函数。与 main
函数类似,wmain
函数也是程序运行时的起点,用于接收命令行参数并执行相应的逻辑。
以下是 wmain
函数的一般形式:
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
// 程序逻辑
return 0;
}
int argc
:命令行参数的数量。wchar_t* argv[]
:指向存储命令行参数的 wide-character 字符串数组的指针。wchar_t* envp[]
:指向存储环境变量的 wide-character 字符串数组的指针。
在使用 wmain
函数时,字符串和字符类型使用宽字符编码(Unicode),例如 wchar_t
类型代表宽字符。
与 main
函数相比,wmain
函数适用于处理 Unicode 字符和 Unicode 字符串的程序。这在需要处理非 ASCII 字符或跨语言的环境下非常有用,因为它可以处理更广泛的字符集。
以下是一个简单的示例:
#include <iostream>
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
std::wcout << L"Hello, wmain!" << std::endl;
for (int i = 0; i < argc; ++i)
{
std::wcout << L"Argument " << i << L": " << argv[i] << std::endl;
}
return 0;
}
在这个示例中,wmain
函数输出一条 Unicode 字符串,并打印出命令行参数。
需要注意的是,使用 wmain
函数编写的程序默认使用 Unicode 字符集,因此需要确保编译器和项目设置正确地处理宽字符。此外,在某些编译器中,需要将项目的字符集设置为“Unicode 字符集”才能使用 wmain
函数。
6. _tWinMain函数
_tWinMain
函数是在 Windows 平台上使用的一个通用宏,用于支持Unicode的Windows GUI应用程序的入口函数,它可以根据项目的字符集设置自动映射到 WinMain
或 wWinMain
函数。这个宏的目的是为了在使用 ANSI 字符集(多字节字符集)或 Unicode 字符集时具有一定的通用性。
具体来说:
- 如果项目的字符集设置为“多字节字符集”,则
_tWinMain
会被映射为WinMain
函数。 - 如果项目的字符集设置为“Unicode 字符集”,则
_tWinMain
会被映射为wWinMain
函数。
因此,通过使用 _tWinMain
函数,你可以编写一份代码,而不必担心在不同项目字符集设置下对入口函数的调整。
以下是 _tWinMain
函数的一般形式:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// 程序逻辑
return 0;
}
HINSTANCE hInstance
:当前实例的句柄。HINSTANCE hPrevInstance
:先前实例的句柄,在现代的 Windows 系统中通常为 NULL。LPTSTR lpCmdLine
:命令行参数字符串。int nCmdShow
:指定窗口的显示方式,比如窗口最初是正常显示、最小化、最大化等。
在 _tWinMain
函数内部,你可以进行窗口类注册、窗口创建、消息循环处理等一系列操作,以确保应用程序能够正确地显示和响应用户输入。
以下是一个简单的示例:
#include <windows.h>
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// 创建窗口等操作
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
这个示例演示了一个使用 _tWinMain
函数的简单 Windows 窗口应用程序。通过编写适当的代码,你可以在其中执行自定义的窗口创建和消息处理逻辑。
需要注意的是,使用 _tWinMain
函数编写的程序默认使用当前项目设置的字符集,因此需要确保编译器和项目设置正确地处理相应的字符集。
7. wWinMain函数
wWinMain函数
:用于支持Unicode的Windows GUI应用程序的入口函数。与WinMain函数类似,但使用宽字符(wchar_t)参数,其签名通常为:
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
总结
以上这些函数根据应用程序类型、字符集设置以及需要处理的参数类型,提供了灵活的入口函数选择。通过选择适当的函数,可以确保应用程序具有正确的入口点,并能够适应不同的字符集和环境要求。