控制台和GUI

windows有原生的gui,所以系统默认提供了控制台应用入口和应用入口.linux则不一样,x系统是由软件实现胡,所以没有默认gui,需要使用三方库才能开启gui

瘟到死

Windows操作系统在装载应用程序并且做完初始化工作后,就转到程序的入口点开始执行你编写的程序。

程序的默认入口点实际上是由连接程序设置的,不同的连接器选择的入口函数也大都不同。

在VC++ 6.0下,连接器对控制台程序所设置的入口函数是 mainCRTStartup,也就是说Windows操作系统在装载完程序并且初始化完成后,就进入入口函数mainCRTStartup ,然后由它调用你自己编写的 main 函数,开始执行具体的代码;

对于图形用户界面(GUI)程序设置的入口函数是 WinMainCRTStartup,和上面一样,WinMainCRTStartup再调用你写的WinMain 函数执行具体功能。

设置哪个入口点可以由连接器的“/subsystem:”参数确定的,它告诉操作系统如何运行编译连接所生成的.exe可执行文件。

四种方式:“CONSOLE | WINDOWS | NATIVE | POSIX”如果这个选项参数的值为“WINDOWS”,则表示该应用程序运行时不需要控制台。

CONSOLE

win32 字符模式应用程序,此种类型的应用程序在运行的时候会产生一个类似DOS 窗口的控制台窗口,如果在应用程序的主函数为main()或者wmain()时,在默认情况下 该应用程序就是一个控制台应用程序。

WINDOWS

该类型的应用程序不产生console窗口,该类型的应用程序的窗口由用户自己创建,简而言之 就是一个标准的Win32 application,其入口地址为WinMain()函数或者wWinMain()函数的地址, 如果你在应用程序中定义的主函数为WinMain或者wWinMain,在默认情况下该应用程序就是一个 Win32 Application !

内嵌式控制台

顾名思义,我们主动在自己写的GUI程序中,创建一个控制台。在Windows 的API,AllocConsole用来直接为一个进程创建一个控制台。注意,一个进程只能有一个控制台

#define WIN32_LEAN_AND_MEAN  

#include <windows.h>
#include <iostream>
#include <string>

#include "resource.h"

HINSTANCE hInst;

struct EmbeddedConsole
{
public:
static void Need()
{

if (!_instance)
{
_instance = new EmbeddedConsole;
}

}

static void Unneed()
{
delete _instance;
_instance = 0;
}

private:
EmbeddedConsole()
{
AllocConsole();
SetConsoleTitle("XXX程序内嵌测试控制台");

freopen("conin$", "r+t", stdin);
freopen("conout$", "w+t", stdout);
freopen("conout$", "w+t", stderr);
}

~EmbeddedConsole()
{
fclose(stderr);
fclose(stdout);
fclose(stdin);

FreeConsole();
}

static EmbeddedConsole* _instance;
};

EmbeddedConsole* EmbeddedConsole::_instance;

BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::string str;

switch (uMsg)
{
case WM_INITDIALOG:
/*
* TODO: Add code to initialize the dialog.
*/
return TRUE;

case WM_CLOSE:
EndDialog(hwndDlg, 0);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam))
{
/** TODO: Add more control ID's, when needed.
*/
case IDC_BTN_QUIT:
EndDialog(hwndDlg, 0);

EmbeddedConsole::Unneed(); //不要了!
return TRUE;

case IDC_BTN_TEST:
EmbeddedConsole::Need(); //我要!
std::cout << "please input :";
std::cin >> str;
std::cerr << str << std::endl;
::MessageBox(hwndDlg, str.c_str(), "Information"
, MB_ICONINFORMATION);
return TRUE;
}
}

return FALSE;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst = hInstance;

// The user interface is a modal dialog box
return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DialogProc);
}

外挂式控制台

这一次我们使用另一个函数:“AttachConsole”:attach的意思很明显:“贴上,依附上,缠上,赖上……:( ”,而它的参数也很直白: 进程ID。 这样,一个GUI程序,完全可以“缠上”另一个进程的控制台(假设它有的话)。如果是要取“父进程”的ID,就方便多了——事实上是不用取,只要填-1就可以了。

基本上, 电脑用户通过操作系统运行一个程序,比如画笔,比如浏览器,比如Word,都是以一个叫“Explorer.exe”的子进程身份启动的。而我们在IDE里调试程序,则程序会以调试器的子进程运行……,可见子进程其实是相当常见的。现在,我们事先写一个控制台的程序,假设称为P程序,以后当S程序(通常是一个GUI程序)需要附加的控制台来输入输出时(效果像方法.1),我们就用P程序来启动S程序,S程序中则通过“AttachConsole(-1)”来“挂”到其父程序(也就是P)的控制台。完成调试之后,就把P程序丢一边,以普通方式运行S程序,则AttachConsole(-1)失败,那些用于调试的cout/cin自然失效。

#define WIN32_LEAN_AND_MEAN  

#include <iostream>
#include <windows.h>

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst = hInstance;

if (0 == AttachConsole((DWORD)-1))
{
::MessageBox(0, "AttachConsole fail", "msg", 0);
}
else
{
freopen("conin$", "r+t", stdin);
freopen("conout$", "w+t", stdout);
freopen("conout$", "w+t", stderr);

::MessageBox(0, "AttachConsole OK", "msg", 0);
}

std::cout << std::hex << _WIN32_WINNT << std::endl;

// The user interface is a modal dialog box
return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DialogProc);
}

带控制台的父进程

GUI程序默认不带控制台,所以我们现在写一个控制台程序,用它来调用要调试的GUI程序。这本来是一件再简单不过的事了。只要用C语言的system程序就可以了,并且是跨平台的——问题是微软大叔总爱干些半拉子的事。Windows从DOS起家,都过去多少年了,它对控制台支持还是怪怪的,所以system函数在它身上,居然不支持带空格的路径,我们只好找CreateProcess来出气。

int main(int argc, char** argv)  
{
if (argc < 2)
{
cout << "Usage:" << argv[0] << " <filename> <args ...>" << endl;
return 1;
}

cout << argv[1] << endl;

//return system(argv[1]); linux下就这样...

STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);

PROCESS_INFORMATION pi;
ZeroMemory(π, sizeof(pi));

::CreateProcess (argv[1]
, NULL
, NULL //LPSECURITY_ATTRIBUTES
, NULL //LPSECURITY_ATTRIBUTES
, FALSE //bInheritHandles
, 0 //dwCreationFlags
, NULL //lpEnvironment
, NULL //LPCTSTR
, &si //LPSTARTUPINFO
, π); //LPPROCESS_INFORMATION

::WaitForSingleObject(pi.hProcess, INFINITE);

DWORD exitCode = 0;

int ret;

if (!::GetExitCodeProcess(pi.hProcess, &exitCode))
{
ret = -1;
}
else
{
ret = exitCode;
}

::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);

return ret;
}

libc扫盲

ANSI C

ANSI C 函数库是基本的 C 语言函数库,包含了 C 语言最基本的库函数。这个库可以根据头文件划分为 15 个部分,其中包括:

  • <ctype.h>:包含用来测试某个特征字符的函数的函数原型,以及用来转换大小写字母的函数原型;
  • <errno.h>:定义用来报告错误条件的宏;
  • <float.h>:包含系统的浮点数大小限制;
  • <math.h>:包含数学库函数的函数原型;
  • <stddef.h>:包含执行某些计算 C 所用的常见的函数定义;
  • <stdio.h>:包含标准输入输出库函数的函数原型,以及他们所用的信息;
  • <stdlib.h>:包含数字转换到文本,以及文本转换到数字的函数原型,还有内存分配、随机数字以及其他实用函数的函数原型;
  • <string.h>:包含字符串处理函数的函数原型;
  • <time.h>:包含时间和日期操作的函数原型和类型;
  • <stdarg.h>:包含函数原型和宏,用于处理未知数值和类型的函数的参数列表;
  • <signal.h>:包含函数原型和宏,用于处理程序执行期间可能出现的各种条件;
  • <setjmp.h>:包含可以绕过一般函数调用并返回序列的函数的原型,即非局部跳转;
  • <locale.h>:包含函数原型和其他信息,使程序可以针对所运行的地区进行修改。地区的表示方法可以使计算机系统处理不同的数据表达约定,如全世界的日期、时间、美元数和大数字;
  • <assert.h>:包含宏和信息,用于进行诊断,帮助程序调试。

GNU C

glibc是linux下面c标准库的实现,即GNU C Library。GNU C 函数库是一种类似于第三方插件的东西。

由于 Linux 是用 C 语言写的,所以 Linux 的一些操作是用 C 语言实现的,因此,GUN 组织开发了一个 C 语言的库,以便让我们更好的利用 C 语言开发基于 Linux 操作系统的程序。

glibc本身是GNU旗下的C标准库,后来逐渐成为了Linux的标准c库,而Linux下原来的标准c库Linux libc逐渐不再被维护。

Linux下面的标准c库不仅有这一个,如uclibc、klibc,以及上面被提到的Linux libc,但是glibc无疑是用得最多的。glibc在/lib目录下的.so文件为libc.so.6。

libc

libc 是 Linux 下的 ANSI C 函数库;glibc 是 Linux 下的 GUN C 函数库。

libc 实际上是一个泛指,凡是符合实现了 C 标准规定的内容,都是一种 libc 。

  • glibc 是 GNU 组织对 libc 的一种实现。它是 unix/linux 的根基之一。
  • 微软也有自己的 libc 实现,叫 msvcrt 。
  • 嵌入式行业里还常用 uClibc ,是一个迷你版的 libc 。

libc, glibc在一个层次,都是C的标准实现库,是操作系统级别的基石之一。

glib

glib 和 glibc 基本上没有太大联系,可能唯一的共同点就是,其都是 C 编程需要调用的库而已。

glib是用C写的一些utilities,即C的工具库,和libc/glibc没有关系。

glib 可以在多个平台下使用,比如 Linux、Unix、Windows 等。glib 为许多标准的、常用的 C 语言结构提供了相应的替代物。

/www/wwwroot/blog/source/_posts/激光打标glib是GTK+的基础库,它由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成,可以在[http://www.gtk.org gtk网站]下载其源代码。

是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据结构的定义、相关的处理函数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。

GTK+是可移植的,当然glib也是可移植的,你可以在linux下,也可以在windows下使用它。使用gLib2.0(glib的2.0版本)编写的应用程序,在编译时应该在编译命令中加入pkg-config --cflags --libs glib-2.0

linux GUI

在Linux下开发GUI程序的方法有很多,比如Gnome桌面使用GTK+作为默认的图形界面库,KDE桌面使用Qt作为默认的图形界面库,wxWidgets则是另一个使用广泛的图形库,此外使用Java中的Swing/AWT组件也可以用于开发Linux下的GUI应用。

20221026211151

GTK+

  • GTK+最初是为X Window系统开发的,但是目前已经发展成为一个跨平台的图形界面API,其支持的平台包括:Linux Unix Windows Mac OS X
  • GTK+基于LGPL协议发布,因此可以将GTK+的二进制动态链接库文件整合到私有软件中而无需额外授权。
  • GTK+本身是用C语言编写的,但是可以很方便地通过语言绑定(language binding)和其它语言协同工作
  • 与wxWidgets和Qt不同,GTK+支持使用纯C语言进行开发,此外还有一个基于C++的封装项目叫GTKMM。
  • GTK+是基于GLib构建的,其中GLib是一个通用的C语言库,类似于C++中的STL,提供了对动态数组、链表、队列、散列表、平衡二叉树、线程操作和XML解析等功能。

在所有的平台上,基于GTK+的应用都看起来完全一样,除非应用了主题。GTK+总是通过主题来模拟原生控件。在Windows平台下,可以通过使用Wimp主题来获得Windows的原生外观。

Qt

Qt是目前使用最广泛的跨平台应用程序框架(Application Framework).

和GTK+一样,Qt并不使用系统提供的控件,而是通过主题模拟这些控件。但是在一些特定的平台,比如Mac OS X和Windows上对于一些最基本的控件通过本地系统调用实现。

Qt通过MOC系统对C++语言进行了扩展,提供了所谓的“信号-槽”(signal-slot)机制。基于信号-槽的事件处理非常优雅,缺点则是是编译系统失去了通用性。

Qt的原生IDE是Qt Creater,同时兼容Qt的其它IDE也非常多,包括Visual Studio、Eclipse、XCode、Edyuk。

wxWidgets

wxWidgets是另一个非常流行的跨平台图形界面库。而GTK+ / Qt不同,wxWidgets并不是通过绘图来模拟控件,而是通过系统本地调用构建完全原生的图形界面。

wxWidgets支持的平台包括:

  • wxGTK: 使用Linux下的GTK+构建图形界面
  • wxMSW: 使用Win32 API构建图形界面
  • wxMac: 使用Mac OS下的Carbon构建图形界面
  • wxOSX/Carbon: 使用Mac OS下的Carbon构建图形界面
  • wxOSX/Cocoa: 使用Mac OS下的Cocoa构建图形界面
  • wxX11: 使用Linux下的X11的通用显示接口构建图形界面
  • wxMotif: 使用Linux下的OpenMotif和Lesstif构建图形界面

注意到这里的描述方式有所不同,因为wxWidgets是基于本地接口构建UI的。

在API和编程风格上,wxWidgets和MFC非常相似,但是封装的很多类比MFC更高级。很多知名的MFC程序都会选择用wxWidgets来改写,来快速移植到其它平台,如eMule用wxWidgets移植出aMule和xMule。

除了基本的图形界面、布局、事件系统外,wxWidgets还提供了很多其它的模块,包括:

  • wxHTML: 进行HTML渲染
  • wxMedia: 对各种多媒体操作提供支持
  • wxNet: Socket支持
  • wxXML: XML文件读写支持
  • wxWidgets集成的功能相对于Qt而言较少,但是足够完成绝大多数的常见任务。