宏与WINDOWS API简单使用

宏与WINDOWSAPI简单使用#define定义宏定义宏语法:#define<宏名><字符串>如#definePI3.1415926#defineP

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

宏与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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/119858.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • python面试常问问题_面试的十个问题及回答

    python面试常问问题_面试的十个问题及回答整理了关于人工智能pytorch、python基础、图像处理opencv/自然语言处理、机器学习、数学基础等工具型资料,对于想学习人工智能或者转行到高薪行业的打工人或大学生都非常实用,无任何套路免费提供,进群966367816免费领取哦~1、到底什么是Python?你可以在回答中与其他技术进行对比下面是一些关键点:Python是一种解释型语言。这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需要编译。其他解释型语言还包括PHP和Ruby。Python是动态类型语言,指的是你在声明变

  • python激活_在线激活「建议收藏」

    (python激活)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • linux apache2配置_虚拟主机开启ssl

    linux apache2配置_虚拟主机开启ssl测试环境*操作系统:Ubuntu5.10*测试机地址:10.39.6.59*测试机域名:*.firehare.com基本配置我们都知道,如果我们想在单台机器上设置多个域名或主机名时,我们就要用到基于名称的虚拟主机了。那么要如何进行设置呢?这就是本指南想解决的问题了。在Ubuntu的/etc/apache2/目录下有个Ap

  • Android移动开发-Android数据加密与解密的实现「建议收藏」

    Android移动开发-Android数据加密与解密的实现「建议收藏」数据的安全是非常重要的,现在无论干什么都要账号和密码,一旦账号与密码泄露出去必将造成财产的损失,所以做好数据保密是非常重要的。Android加密算法有多种多样,常见的有MD5、RSA、AES、3DES四种。

  • DOM手术台

    DOM手术台

  • springboot整合mybatis实现增删改查

    springboot整合mybatis实现增删改查立志存高远,笃行践初心三更灯火五更鸡,正是男儿读书时。黑发不知勤学早,白首方悔读书迟。立志,标定人生方向;奋斗,创造人生价值,二者相辅相成,互相促进。大部分程序员的「目标」都是成为一名优秀的工程师,一名可以统览全局的「架构师」。千里之行始于足下对于大部分普通人而言,成为一名优秀的架构师还是有一定难度的,「千里之行始于足下,一步一个脚印,慢慢来」。????粉丝专属福利:包邮送书3本,如下书单四选一。????获取方式:1、参与文末投票,点赞,收藏即有机会获得精

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号