宏与WINDOWS API简单使用

宏与WINDOWS API简单使用

#define定义宏

定义宏

语法: #define <宏名> <字符串>

#define PI 3.1415926

#define PI 3.1415926

int main() {
    cout << PI << endl;
}
//结果:3.1415926

定义一个H标识符。,代码中的H将会被删除。

#define H


int main() {


    int H a=1;

    cout << a << endl;

}

取消宏定义

语法:#undef H

定义复杂的宏表达式

#define MAX(a,b) (a)>(b)?(a):(b)

int main() {

    int a = 5;
    int b = 10;
    int c = MAX(a, b);
    cout << c << endl;
}

预编译指令

程序的编译过程可以分为预处理、编译、汇编三部分,其中预处理是首先执行的过程,预处理过程扫描程序源代码,对其进行初步的转换,产生新的源代码提供给编译器。
预处理过程读入源代码之后,会检查代码里包含的预处理指令,完成诸如包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码的工作

#指令

预处理指令以#号开头,并且#号必须是该行除了任何空白字符外的第一个字符。
#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
单纯一个#号表示空指令,没有任何作用。

#include指令

二 #include指令
#include预处理指令的作用是在指令处展开被包含的文件。展开被包含的文件之后,在代码就可以正常地调用该文件中所声明的变量和函数。#include指令有两种使用方法
#include <xxx.h>
#include "xxx.h"

#define、#undef指令

define指令定义了一个标识符及一个串,标识符称为宏名,源程序中宏名的每次出现都会用其定义的串进行替换,称为宏替换。

undef指令取消一个已定义的宏。

宏一般使用大写字母定义,其可以出现在程序的任意地方。宏替换仅仅是以文本串代替宏标识符的过程,该过程很容易出现一些逻辑上的错误,需要仔细处理一些关于括号的问题。

#if、#elif、#else、#endif指令

这几个指令称为条件编译指令,可对程序源代码的各部分有选择地进行编译。
跟一般的if、else if、else语句类似,如果一个条件上的值为真,则编译它对应的代码,否则提过这些代码,测试下一个条件上的值是否为真。注意,作为条件的表达式是在编译时求值的,它必须仅含常量及已定义过的标识符,不可使用变量,也不可以含有操作符sizeof(sizeof也是编译时求值)。
命令#endif标识一个#if块的结束。

#ifdef、#ifndef、#endif指令

这几个也是条件编译指令,其检查后面指定的宏是否已经定义,然后根据检查结果选择是否要编译后面语句。其中#ifdef表示”如果有定义“,#ifndef表示”如果没有定义“。

#line指令

C语言中可以使用__FILE__表示本行语句所在源文件的文件名,使用__LINE__表示本行语句在源文件中的位置信息。#line指令可以重新设定这两个变量的值,其语法格式为
#line number["filename"]

其中第二个参数文件名是可省略的,并且其指定的行号在实际的下一行语句才会发生作用。

#error指令

#error指令在编译时输出编译错误信息,可以方便程序员检查出现的错误


void test5()
{
#define OPTION 3
#if OPTION == 1
cout << "Option: 1" << endl;
#elif OPTION == 2
cout << "Option: 2" << endl;
#else
#error ILLEGAL OPTION! //fatal error C1189: #error :  ILLEGAL OPTION!
#endif

#pragma指令

该指令用来来设定编译器的状态或者是指示编译器完成一些特定的动作,它有许多不同的参数。

  1. pragma once

在头文件的最开始加入这条指令可以保证头文件只被编译一次。它可以实现上述使用#ifndef实现不重复包含头文件同样的功能,但可能会有部分编译系统不支持。

  1. pragma message

该指令能够让编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。其使用方法为:#pragma message(“消息文本”)
通过这条指令我们可以方便地记录在是否在源代码中定义过某个宏

  1. #pragma warning

该指令能够控制编译器发出警告的方式,其用法举例如:#pragma warning(disable : 4507 34; once : 4385; error : 164)
这个指令有三部分组成,其中disable部分表示忽略编号为4507和34的警告信息,once部分表示编号为4385的警告信息只显示一次,error部分表示把编号为164的警告信息当做错误。
另外,其还有两个用法

pragma warning(push [, n]):保存所有警告信息的现有的警告状态,后面n是可选的,表示把全局警告等级设为n。

pragma warning(pop):弹出最后一个警告信息,取消在入栈和出栈之间所作的一切改动。

  1. pragma comment

    该指令将一个注释记录放入一个对象文件或可执行文件中。其使用方法为:#pragma comment(comment-type ,["commentstring"])
    其中comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一。常用的是lib关键字,可以帮我们连入一个库文件。 如

    pragma comment(lib, "my.lib")

  2. pragma hdrstop

    该指令表示预编译头文件到此为止,后面的头文件不进行预编译。

  3. pragma resource

    该指令表示把指定文件中的资源加入工程,如

    pragma resource "*.dfm"

  4. pragma code_seg

该指令能够设置程序中函数代码存放的代码段,开发驱动程序的时候会使用到。使用方法为:#pragma code_seg(["section-name" [,"section-class"] ])。

  1. pragma data_seg

该指令建立一个新的数据段并定义共享数据。一般用于DLL中,在DLL中定义一个共享的有名字的数据段,这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量。

  1. #pragma pack

该指令规定数据在内存中的对齐长度

WINDOWS API数据类型

WINDOWS数据类型
BOOL int
BYTE unsingend char
INT int
CONST const
DWORD unsingend long
DWORD32 unsingend int
DWORD64 unsingend long long
FLOAT float
HANDLE void
HINSTANCE 指针
HKEK 指针
HWND 指针
UINT unsingend int
LPARAM long
WPARAM unsingend int
LPCSTR const char*
LPCWSTR const wchar_t*
VOID void
TCHAR 如果定义UNICODE宏为WCHAR,否则为CHAR
WINAPI __stdcall
LPCSTSTR TCHAR*
WORD unsingend char
LPDWORD DWORD*

image-20210811213228077

消息框

使用MessageBox来做案例

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);

第一个参数类型为HWND,转到定义

DECLARE_HANDLE            (HWND);

...
    
#define DECLARE_HANDLE(name) struct name##__{int unused;};
    typedef struct name##__ *name

实际上这里就是一个结构体,定意为 *name传入name##__即HWND,而name##__{int unused;}调用结构体并且传入int类型信息。

看到第二和第三个参数LPCTSTR

typedef LPCWSTR PCTSTR, LPCTSTR;
...
typedef _Null_terminated_ CONST WCHAR UNALIGNED *LPCUWSTR, *PCUWSTR;

是一个LPCWSTRPCTSTR类型,是一个WCHAR类型参数

_NullNull_terminated 宏的含义 宏是一个头部注解,藐视了一些类型.如何用作函数的参数以及返回值.

第三个参数实际上不用看了在这里简单写一下。

typedef unsigned int        UINT;

案例:

int main(){
    
MessageBox(0, L"hhh", L"hhh", MB_OK);
    
}

注意点

在这里面多字符集的编码和unicode编码还不太一样。看到MessageBox中定义的代码。

#ifdef UNICODE
    return MessageBoxW(
#else
    return MessageBoxA(
#endif
        hWnd,
    lpText,
    lpCaption,
    uType
        );
}

预编译指令中,如果为UNICODE编码则返回MessageBoxW,否则返回MessageBoxA

对比一下MessageBoxA和MessageBoxW

MessageBoxW(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType);


MessageBoxA(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCSTR lpText,
    _In_opt_ LPCSTR lpCaption,
    _In_ UINT uType);

第二、三使用的数据类型不一致。

Link窗口案例

GetStdHandle

首先需要获取输入输出,则需要调用GetStdHandle来实现。先来看看api文档。

HANDLE WINAPI GetStdHandle(
  _In_ DWORD nStdHandle
);

_In_其实没有什么实际一样,只是标识这里是一个接受输入的。

DWORD类型的nStdHandle参数进行传入。返回值为HANDLE

int main() {
    HANDLE in;
    HANDLE out;
    HANDLE error;
    in = GetStdHandle(STD_INPUT_HANDLE);
    out = GetStdHandle(STD_OUTPUT_HANDLE);
    error = GetStdHandle(STD_ERROR_HANDLE);
    if (in != INVALID_HANDLE_VALUE) {
        cout << "打开输入句柄成功" << endl;
    
    }
    
}

SetConsoleWindowInfo

设置控制台屏幕缓冲区窗口的当前大小和位置

参数:

hConsoleOutput [in]
控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。有关更多信息,请参阅控制台缓冲区安全和访问权限。

bAbsolute [in]
如果此参数为TRUE,则坐标指定窗口的新左上角和右下角。如果为FALSE,则坐标是相对于当前窗口角坐标的。

lpConsoleWindow [in]
指向SMALL_RECT结构的指针,该结构指定窗口的新左上角和右下角。
BOOL WINAPI SetConsoleWindowInfo(
  _In_       HANDLE     hConsoleOutput,
  _In_       BOOL       bAbsolute,
  _In_ const SMALL_RECT *lpConsoleWindow
);

SMALL_RECT类型为一个结构体

typedef struct _SMALL_RECT {
    SHORT Left;
    SHORT Top;
    SHORT Right;
    SHORT Bottom;
} SMALL_RECT, *PSMALL_RECT;

实现代码:

int main() {



    HANDLE in;
    in = GetStdHandle(STD_INPUT_HANDLE);
    
    SMALL_RECT small_rect{ 0,0,30,30 };


    SetConsoleWindowInfo(in, TRUE, &small_rect);
    SetConsoleTitle(L"aaaa");

    while (true);

    return 0;
}
posted @ 2021-08-13 16:24  nice_0e3  阅读(349)  评论(0编辑  收藏  举报