项目中遇到的一些知识问题等

MFC基础知识

1.命名规则

Dialog 中:

IDC_COMBOX_PORT

IDC_BTN_CONNECT

IDC_RICHEDIT_MSG

全部大写,前面是功能块的名字,用下划线连接,最后用该功能命名

2.test.cpp和testDlg.cpp有什么区别

一般后缀名有Dlg的,表示它是一个对话框类,从CDialog继承,用来实现用户界面,负责数据的输入输出而后缀名为App的类,是从CWinApp类继承过来的系统类,负责一些系统级的任务,比如系统初始化(例如注册表处理)等等。他们都有对应的头文件和实现文件,自己在类视图和解决方案视图来回切换切换,对照对照,应该很快就能明白的。

3.hInstance

实例就是一个程序。就像qq。你可以开同时开2个qq号,但是你电脑里的qq软件只有一份。这2个qq号就是qq的2个实例

4.#ifdef _DEBUG

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

这样就很清楚了,当在debug模式下时,我们分配内存时的new被替换成DEBUG_NEW,而这个DEBUG_NEW不仅要传入内存块的大小,还要传入源文件名和行号,这就有个好处,即当发生内存泄漏时,我们可以在调试模式下定位到该问题代码处。若删掉该句,就不能进行定位了。而在release版本下的new就是简单的new,并不会传入文件名和行号。因此,我们在开发代码阶段,保留上述代码是值得的。

#define

5.用于条件编译:(常用形式)

 #ifndef _AAA_H

 #define _AAA_H

 //c/c++代码

 #endif

在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。就是:

#ifdef WINDOWS
.....
#endif

#ifdef LINUX

......

#endif

可以在编译的时候通过#define设置编译环境

6.条件编译

#ifdef XXX…(#else) …#endif

例如 #ifdef DV22_AUX_INPUT

#define AUX_MODE 3 

#else

#define AUY_MODE 3

#endif

#ifndef XXX … (#else) … #endif

8.
可以只定义符号,不定义值.如#define AAA

5.BEGIN_MESSAGE_MAP

BEGIN_MESSAGE_MAP(CAutoMachApp, CWinApp)

    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)

END_MESSAGE_MAP()

使用BEGIN_MESSAGE_MAP宏开始你的消息映射的定义。

在你的类的成员函数的实现文件(.CPP)中,使用BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数加入一个入口,最后用END_MESSAGE_MAP宏结束消息映射

6.HANDLE

HANDLE:句柄,是Windows用来表示对象的(不是C++的对象),HWND是其中一种,HWND是HANDLE,但HANDLE不只是HWND,HANDLE是一个通用句柄表示HWND是一个专用表示窗口的句柄。更具体的可查找MSDN。包含在winnt.h头文件中。

ANDLE(句柄)是Windows操作系统中的一个概念。在Windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时会为它们分配内存,并返回标示这些资源的标示号,即句柄句柄指的是一个核心对象在某一个进程中的[唯一索引]。由于地址空间的限制,句柄所标识的内容对进程是不可见的,只能由操作系统通过进程句柄列表来进行维护。

句柄列表:每个进程都要创建一个句柄列表,这些句柄指向各种系统资源,比如信号量线程,和文件等,进程中的所有线程都可以访问这些资源。

7.LPVOID

LPVOID是一个没有类型的指针,也就是说你可以将LPVOID类型的变量赋值给任意类型的指针,比如在参数传递时就可以把任意类型传递给一个LPVOID类型为参数的方法,然后在方法内再将这个“任意类型”从传递时的“LPVOID类型”转换回来。具体请看下面的示例程序,其中LPVOID lParam即为空类型指针,而CMyClass即为任意类型指针。

UINT CMyClass::StartThread(LPVOID lParam)

{

CMyClass * pMyClass = (CMyClass*)lParam;

LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递),然后在使用的时候在转换回来。 例如:

class CMyClass
{
   void Start();
   static UINT StartThread(LPVOID lParam);
};

void CMyClass::Start()
{
    AfxBeginThread(StartThread, this);
}

UINT CMyClass::StartThread(LPVOID lParam)
{
   CMyClass * pMyClass = (CMyClass*)lParam;
   ...
   return 0;
}

8.DWORD

DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。

键值项窗口空白处单击右键,选择“新建”菜单项,可以看到这些键值被细分为:字符串值、二进制值、DWORD值、多字符串值、可扩充字符串值五种类型。.

9.WinAPI

视窗操作系统应用程序接口Windows API),有非正式的简称法为WinAPI,是微软对于Windows操作系统中可用的内核应用程序编程接口的称法。它设计为由C/C++程序调用,而且它也是应用软件与Windows系统最直接的交互方式。

10.afx_msg

应用程序框架产生的消息映射函数
例如:afx_msg void OnBnClickedButton1(); 其中 afx_msg为消息标志,它向系统声明:有消息映射到函数实现体;而在map宏定义中,就有具体消息和此函数的映射定义(可以是自定义,也可以是系统自动完成的)

11.include"stdafx.h"

因此,所有的MFC实现文件第一条语句都是:#include"stdafx.h"。在它前面的所有代码将被忽略,所以其他的头文件应该在这一行后面被包含。否则,你将会得到“Nosuchfileordirectory”这样让你百思不得其解的错误提示。

12.IDD

// 对话框数据 enum { IDD = IDD_AutoMACH_DIALOG };

IDD,是对话框资源编号的默认开头:IDD_DIALOG1,其中IDD表示Identify Dialog的意思

IDD_AutoMACH_DIALOG 在resource.h文件中

这是为了好写对话框的架构. 因为不同的对话框可以使用相同的类, 不同的对话框资源.而不同的资源又要是相同的代码.
enum { IDD = IDD_DIALOG1 }这个就能起作用了.
只要基类代码中使用IDD就能得到对话框资源的ID号,而不用写其它的代码要设置.如果你删除掉这句, 你就可以看到使用IDD的原因了.你要定义成变量的话, 你就需要自己修改代码了. 
: CDialog(CTestDlg::IDD, pParent) //基类构造函数的调用需要修改.
IDD是变量的话, 就需要调用这个基类构造前赋值, 并且修改成不要CTestDlg:: 类似于这样:
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/) :IDD(IDD_TEST_DIALOG) //变量初始化, 
CDialog(IDD, pParent) //构造函数 
个人感觉使用常数在效率上会好 点,因为常熟是包含在指令中,变量要获得值需要进行内存访问
enum { IDD = IDD_TESTDEMO_DIALOG };

CtestdemoDlg::CtestdemoDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_TESTDEMO_DIALOG, pParent)
或者
enum { IDD = IDD_AutoMACH_DIALOG };

CAutoMachDlg::CAutoMachDlg(CWnd* pParent /*=NULL*/)
    : CImageDlg(CAutoMachDlg::IDD, pParent)
        

13.undef 标识符

1、在一个程序块中用完宏定义后,为防止后面标识符冲突需要取消其宏定义:

#define MAX 200
printf("MAX = %d\n", MAX);
#undef MAX

{
int MAX = 10;
printf("MAX = %d\n", MAX);
}

return 0;
}

2、在同一个头文件中定义结构类型相似的对象,根据宏定义不同获取不同的对象,主要用于增强代码的可读性:

例如:在头文件student.h中定义两个学生对象(小明和小红),两个对象互不干涉:

#ifdef MING
#define MING_AGE 20
#define MING_HEIGHT 175
#endif

#ifdef HONG
#define HONG_AGE 19
#define HONG_HEIGHT 165
#endif
#define MING
#include "student.h"
#undef MING
#define HONG
#include "student.h"
#undef HONG

int main()
{
printf("Xiao Ming's age is %d.\n", MING_AGE);
printf("Xiao Hong's age is %d.\n", HONG_AGE);

return 0;
}

3、将某个库函数包装成自定义接口,而只允许用户调用自定义接口,禁止直接调用库函数:

/*
** 定义一个不易发生错误的内存分配器
*/
#include <stdlib.h>

#define malloc                         //防止直接调用malloc!
#define MALLOC(num, type)   (type *)alloc((num) * sizeof(type))
extern void *alloc(size_t size);

/***********不易发生错误的内存分配器接口:alloc.h***********/

其中“#define malloc”是为了防止用户直接调用库函数malloc,只要包含了这个头文件alloc.h,就不能直接调用库函数malloc,而只能调用自定义函数MALLOC,如果用户要调用库函数malloc编译器会发生错误
#include <stdio.h>
#include "alloc.h"
#undef malloc

void *alloc(size_t size)
{
void *new_mem;
new_mem = malloc(size);
if(new_mem == NULL)
{
printf("Out of memory!\n");
exit(1);
}
return new_mem;
}

4、用于调试头文件中,偶然看到这样一个代码用到了#undef,写于此作为记录:

#ifdef DEBUG#undef THIS_FILEstatic char THIS_FILE[] = FILE;#define new DEBUG_NEW#endif

/***调试代码头文件:debug.h***/

14.DoDataExchange

DoDataExchange函数其实是一项数据动态绑定技术。比如你在写动态按钮过程中须对按钮添加变量时,怎么添加?控件类已经写好了,其变量是已经固定的。你要添加新的变量就要用到DoDataExchange函数。

15.WPARAM和LPARAM的含义

lParam 和 wParam 是宏定义,一般在消息函数中带这两个类型的参数,通常用来存储窗口消息的参数。

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); wParam 通常用来存储小段信息,如,标志 lParam 通常用于存储消息所需的对象

16.L

在字符串前加一个L,如 L”我的字符串” 表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。 strlen(“asd”) = 3; strlen(L”asd”) = 6;

17. _T

_T宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的(Unicode还是ANSI)字符处理方式 如果你定义了UNICODE,那么_T宏会把字符串前面加一个L。这时 _T(“ABCD”) 相当于 L”ABCD” ,这是宽字符串。 如果没有定义,那么T宏不会在字符串前面加那个L,T(“ABCD”) 就等价于 “ABCD”

18.WM_USER

为了防止用户定义的消息ID与系统的消息ID冲突,MS(Microsoft)定义了一个宏WM_USER,小于WM_USER的ID被系统使用,大于WM_USER的ID被用户使用。 #define MYWM_NOTIFYICON (WM_USER + )

19.HICON

HICON 是微软平台下的图标句柄。

类似的定义还有HANDLE、HWND、HDC、HBITMAP等。遵循的命名规则为大写的H加上大写的句柄类型(其中HANDLE是通用句柄,没有类型)。

在VC/MFC下,句柄其实就是一个用于兼容windows平台下其它编程语言的地址标记定义,其本质,是一个无类型指针:

HICON这类句柄,主要作用是为了使用windows平台的API函数,这类函数是多编程语言通用的,很多语言没有指针,因此定义了这样一个局部类型。(注意:上面的代码只是句柄在C/C++下的定义,在SDK和其它语言环境中,定义不同但兼容)。

20.段错误:操作常量指针

char *c="hello"; c[0] = 'a';printf("%s",c);

这样就会段错误了

21.删除.vs隐藏文件,且使用了静态库,就会报前端与后端不统一的错误

22.更换托盘图标

在源文件文件中res中,将icon 直接改成同名的就能替换,不需要导入之类的

需要将图片在格式工厂里转换,图标选64*64为宜

23.输出格式

%04d  表示:在输出整数x的时候  按照4个位子的空间左对齐  多余的位子用0代替
(例如:x=3  -->  输出:0003  x=33  -->  输出:0033)
这里x=7  就是0007

%4.4  表示:输出的数的格式为:整数部分为4位  小数部分为4位(多余的位子用0代替)
(例如:3.24  -->  输出:0003.2400)
这里x=7  没有小数部分  就只有:0007

24.常见词翻译

轮询(Polling)

proc process 过程

interface 界面; <计>接口; 交界面;

buffer 缓存区

缺省=默认

MFC常用方法

1.CreateEvent的用法

事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们称其为”有信号”否则称为”无信号”。可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的CPU时间就比较少。

HANDLE     CreateEvent(

 LPSECURITY_ATTRIBUTES     lpEventAttributes,                           //     SD   
BOOL     bManualReset,                         
                    //     reset     type   
 BOOL     bInitialState,                                                //     initial     state   
LPCTSTR     lpName                                                       //     object     name   
    );   

该函数创建一个Event同步对象,如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。

  1. lpEventAttributes 一般为NULL

  1. bManualReset 创建的Event是自动复位还是人工复位.如果true,人工复位, 一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复 为无信号. 如果为false,Event被设置为有信号,则当有一个wait到它的Thread时, 该Event就会自动复位,变成无信号. 如果想 在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件 的信号。

  2. bInitialState 初始状态,true,有信号,false无信号

  3. lpName 事件对象的名称。您在OpenEvent函数中可能使用。

2.CreateThread

CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。

步骤:

CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:

1.在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回

2.把线程退出码置为STILL_ACTIVE,把线程挂起计数置1

3.分配context结构

4.分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD

5.lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数

6.把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数

MSDN中CreateThread原型:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
SIZE_T dwStackSize,//initialstacksize
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
LPVOID lpParameter,//threadargument
DWORD dwCreationFlags,//creationoption
LPDWORD lpThreadId//threadidentifier
)

1.lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE

2.dwStackSize,设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。

3.lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:

4.DWORD WINAPI 函数名 (LPVOID lpParam) ,格式不正确将无法调用成功。

HANDLE CreateThread(
LPSECURITY_ATTRIBUTESlpThreadAttributes,//线程安全属性
DWORDdwStackSize,//堆栈大小
LPTHREAD_START_ROUTINElpStartAddress,//线程函数
LPVOIDlpParameter,//线程参数
DWORDdwCreationFlags,//线程创建属性
LPDWORDlpThreadId//线程ID
);

m_hHeartBeatPolling = CreateThread(NULL, 0, HeartBeatPollingThreadProc, this, 0, &dwThreadId);  

3.WaitForSingleObject

WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。

DWORD WINAPI WaitForSingleObject(

__in HANDLE hHandle,

__in DWORD dwMilliseconds

);

hHandle[in]对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

dwMilliseconds[in]定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。

如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。

如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

SetEvent,触发事件

ResetEvent,使事件状态设为未触发,如在创建事件时第二个参数为TRUE手动设置,则需要该函数去恢复事件为未触发状态。

BOOL CMainDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    m_handle = CreateEvent(NULL,FALSE,FALSE,NULL);  //自动复位事件状态,初始状态未触发

    return TRUE;  
}
//线程1函数
{
   ....
   SetEvent(m_handle);       //触发事件
}
//线程2函数
{
    ......
   WaitForSingleObject(m_handle,INFINITE);  //无限等待 事件的触发才会执行后续程序
   ...........
}

创建线程以及事件

testDlg.h

private:

HANDLE m_hTestPolling;

private:
static DWORD WINAPI TestPollingTreadProc(LPVOID pParam);
DWORD WINAPI TestPollingTreadContent(LPVOID pParam);

private:
HANDEL m_hTestEvent;

testDlg.cpp

1.在构造函数中
CtestdemoDlg::CtestdemoDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_TESTDEMO_DIALOG, pParent)
{
   m_hTestPolling=NULL;
   m_hTestEvent=CreateEvent(NULL,TRUE,FALSE,NULL);             
}

2.在BOOL CtestdemoDlg::OnInitDialog() 
//创建线程
DWORD dwTreadId;
m_hTestPolling=CreateTread(NULL,0,TestPollingTreadProc,this,0,&dwTreadId);//第三个参数好像要求是静态函数,如果不是好像会报错,具体原因还未知


3.初始化函数 
//创建一个对象指针来接受this指针,在这里操作类成员需要加Dlg->,并且直接在里面操作控件可能会崩溃
DWORD CTestDlg:: TestPollingTreadProc(LPVOID pParam)
{
   CtestdemoDlg *pDlg = (CtestdemoDlg*)pParam;
    return pDlg->TestPollingThreadContent(pParam);

}

4.//传pParam 是this指针,但传下去后就相当于在类中直接操作,
//可以直接m_editmsg.setwindowTest();
DWORD CTestDlg:: TestPollingTreadContent(LPVOID pParam)
{
    while (true)
    {
        DWORD dwResult = WaitForSingleObject(m_hTsetEvent, INFINITE);
        fun();
        ResetEvent(m_hTsetEvent);//关闭信号
        SetEvent(m_hCloseTsetEvent);//开启另外一个事件,比如清除事件
    }
    
}

4.SetTimer

SetTimer是一种API函数,位于user32.dll中。你想每隔一段时间执行一件事的的时候,你可以使用它。 使用定时器的方法比较简单,通常告诉Windows一个时间间隔,然后Windows以此时间间隔周期性触发程序。通常有两种方法来实现:发送WM_TIMER消息和调用应用程序定义的回调函数。不需要指定定时器时,可以调用对应KillTimer 的函数销毁指定的时钟。

KillTimer :在调用一次ontime函数,杀死指定ID的timer线程

5.AfxGetApp

AfxGetApp( )这个函数可以得到当前应用进程的指针,是CWinApp*类型的,通过这个指针可以访问到这个进程中的对象。

GetMainWnd是应用程序为一个OLE服务器,调用该函数得到应用程序活动主窗口的指针。

CAutoMachDlg* pDlg = (CAutoMachDlg*) AfxGetApp()->GetMainWnd();

AfxGetApp( )是全局的。AfxGetApp( )这个函数可以得到当前应用进程的指针,是CWinApp*类型的,通过这个指针可以访问到这个进程中的对象。

AfxGetApp( )是全局的。

比如在全局函数中要向对话框中的列表写数据。

void writeString(char* pString)

{

CWnd* pWnd = AfxGetApp()->GetMainWnd();

CMyDlg * pDlg;

pDlg=(CMyDlg *) pWnd;

pDlg->ShowMsg(pString);

}

AfxGetApp()得到进程指针CWinApp*,通过这个指针可以得到pWnd。

要不在全局函数里你怎么对已存在的对话框操作呢?

这是静态的函数,所以不能直接调用类成员

静态成员函数不属于任何一个类对象,没有this指针,而非静态成员必须随类对象的产生而产生,所以静态成员函数"看不见"非静态成员,自然也就不能访问了
静态成员变量,和类绑定,    而普通的成员变量与对象绑

CAutoMachDlg* pDlg = (CAutoMachDlg*) AfxGetApp()->GetMainWnd();

pDlg->m_strTRACKCardID = pCardNum;

createThread(NULL, 0, UploadCardRecordPollingThreadProc, this, 0, &dwThreadId); 

不同于创建线程,因为线程的函数,那会已经将this指针传入了,所以不需要再获取当前应用进程的指针

afxgetapp -- 取应用程序实例指针 getmainwnd -- 取主窗口对象指针

AfxGetMainWnd()

AfxGetMainWnd() 就是获得应用程序主窗口的指针,

AfxGetMainWnd()->m_hWnd是主窗口的句柄,

AfxGetMainWnd()->GetSafeHwnd() 返回主窗口的句柄。

AfxGetApp()->GetMainWnd()取得的是主窗口句柄,无论在那个线程里调用都是没有问题的,因为它先取得主线程句柄,再取得主线程的活动窗口(如视图切换可能导致的替代,这种情况我也不大清楚),如果没有活动窗口则取主窗口,任何程序总要有一个主窗口,所以它的调用不会出现问题,如果想取得程序的主窗口建议用AfxGetApp()->GetMainWnd().

6.GetBuffer

该函数功能:获取指定缓冲区里的内容大小的指针,从而进行修改,

例如:char *p=NULL; p=variable.GetBuffer(m_strWrite.GetLength());

这个函数是为一个CString对象重新获取其内部字符缓冲区的指针,返回的LPTSTR为非const的,从而允许直接修改CString中的内容!

  CString   s(   "abcd"   );   
  int   len=s.GetLength();   
  LPTSTR   p   =   s.GetBuffer(   5   );   
  strcpy(   p,   "Hello"   );  

如果你需要修改 CString 中的内容,它有一个特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一个可写的缓冲指针。

如果仅仅是读出CString中的内容,那么只需要用GetBuffer(0)即可。如果后面对CString还有其他操作,那么立刻ReleaseBuffer。

这是 GetBuffer 的第一种用法,也是最简单的一种,不用给它传递参数,它使用默认值 0,意思是:“给我这个字符串的指针,我保证不加长它”。当你调用 ReleaseBuffer 时,字符串的实际长度会被重新计算,然后存入 CString 对象中。

7.releaseBuffer

GetBuffer()主要作用是将字符串的缓冲区长度锁定,releaseBuffer则是解除锁定,使得CString对象在以后的代码中继续可以实现长度自适应增长的功能。

是否需要在GetBufer后面调用ReleaseBuffer(),是根据你的后面的程序是否需要继续使用该字符串变量,并且是否动态改变其长度而定的。如果你GetBuffer以后程序自函数就退出,局部变量都不存在了,调用不调用ReleaseBuffer没什么意义了。

GetBuffer(int size)是用来返回一个你所指定大小可写内存的成员方法。它和被重载的操作符LPCTSTR还是有点本质区别的,LPCTSTR是直接返回一个只读内存的指针,而GetBuffer则是返回一个可以供调用者写入的内存,并且,你可以给定大小。

 int readFile(CString& str, const CString& strPathName)
    {
        FILE* fp = fopen(strPathName, "r"); // 打开文件
        fseek(fp, 0, SEEK_END);
        int nLen = ftell(fp); // 获得文件长度
        fseek(fp, 0, SEEK_SET); // 重置读指针
        char* psz = str.GetBuffer(nLen);
        fread(psz, sizeof(char), nLen, fp); //读文件内容
        str.ReleaseBuffer(); //千万不能缺少
        fclose(fp);
    }
    上面的函数是GetBuffer函数最典型的用法了,其实它就相当于申请一块nLen大小的内存,只不过,这块内存是被引用在CString对象的内部而已,这是非常有效的一种用法,如果不直接用GetBuffer函数来申请的话,那么你必须用new操作符(或者malloc()函数)在CString的外部申请,然后再将申请的内存拷贝到CString对象中,显然这是一个非常冗余的操作,会使你函数的效率大大下降。
    ReleaseBuffer函数是用来告诉CString对象,你的GetBuffer所引用的内存已经使用完毕,现在必须对它进行封口,否则 CString将不会知道它现在所包含的字符串的长度,所以在使用完GetBuffer之后,必须立即调用ReleaseBuffer函数重置 CString的内部属性,其实也就是头部信息。

8.strcpy_s:

语法:

#include<string.h>
errno_t __cdecl strcpy_s(char*_Destination,rsize_t _SizeInBytes,char const* _Source);

功能:复制字符串Source中的字符到字符串Destination,其中限制了大小为_SizeInBytes,这是为了防止字符串过长超出缓存区内存引发问题而要求的。

9.GetSystemMetrics

GetSystemMetrics是一个计算机函数,该函数只有一个参数,称之为「索引」,这个索引有75个标识符,通过设置不同的标识符就可以获取系统分辨率、窗体显示区域的宽度和高度、滚动条的宽度和高度。

SM_CYSCREEN 以像素为单位计算的屏幕尺寸。

m_rc = CRect(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));// 创建一个矩阵,分别左上,右下,两个角

10.CRect

CRect 实际上是一个矩形的类,数据成员是,(矩形左上角的x的坐标,矩形左上角Y的坐标,矩形右下角x的坐标,矩形右下角Y的坐标)

这个类是从tagRECT结构派生而来的。(tagRECT是RECT结构的不太常用的别名。)这意味着RECT结构的数据成员(left,top,right,和bottom)也是CRect的可访问数据成员。left左上角X坐标,top左上角Y坐标,right右下角X坐标,bottom右下角Y坐标。

一个CRect包含用于定义矩形的左上角和右下角点的成员变量

11.GetPrivateProfileString

GetPrivateProfileString是一个计算机函数,功能是为初始化文件中指定的条目取得字串,是编辑语言中的一种函数结构。

lpApplicationName String,欲在其中查找条目的小节名称。这个字串不区分大小写。如设为vbNullString,就在lpReturnedString缓冲区内装载这个ini文件所有小节的列表。
lpKeyName String,欲获取的项名或条目名。这个字串不区分大小写。如设为vbNullString,就在lpReturnedString缓冲区内装载指定小节所有项的列表
lpDefault String,指定的条目没有找到时返回的默认值。可设为空(""

lpReturnedString String,指定一个字串缓冲区,长度至少为nSize
nSize Long,指定装载到lpReturnedString缓冲区的最大字符数量

lpFileName String,初始化文件的名字。如没有指定一个完整路径名,windows就在Windows目录中查找文件

GetPrivateProfileString("DEVICECONFIG", "ICCARD", "", strICCard.GetBuffer(KILOBYTE), KILOBYTE, m_strPathConfigDev.GetBuffer(0));

12.MoveWindow

是一种函数。功能是改变指定窗口的位置和大小。

当你设计一个对话框的窗口时,就需要布局好所有按钮、文本显示框等等,由于每个按钮都是一个窗口,那么就需要移动这些窗口到合适的位置,这时就需要使用到MoveWindow函数。或者当你的界面需要动态地修改按钮位置,比如窗口放大了,按钮就需要跟着移动,否则按钮还在原来的位置,放大也不会移动按钮的位置,这时也需要使用MoveWindow函数重新设置按钮的位置。只要你想移动窗口,就可以考虑使用这个函数来实现。

MoveWindow(m_rc);

13.LOGFONT

在Windows内部,字体是以一个名为LOGFONT的结构来表示。

LOGFONT lfont;

14.SetWindowPos

SetWindowPos函数改变一个子窗口,弹出式窗口或顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。

SWP_NOACTIVATE:不激活窗口。如果未设置标志,则窗口被激活,并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)。

SWP_NOZORDER:维持当前Z序(忽略hWndlnsertAfter参数)。

::SetWindowPos(GetSafeHwnd(),HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);//最前端

15.memset

将s所指向的某一块内存中的后n个 字节的内容全部设置为ch指定的ASCII值, 第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为s。

void *memset(void *s, int ch, size_t n);

0x00

1,memset函数在内存中填充数据的时候是以字节为单位的。一个字符在计算机中占一个字节。一个字节在计算机中是8位的。2,'\0'是一个字符,作为字符串结尾字符。它是一个字节大小,占8位。0x00是16进制表示,因为它以0x开头,0x00转换为2进制为00000000,正好八位,在计算机内存中其实和'\0'的表示是一样的。因此,上面的两种写法形式不同,但是实际效果是一样的。只不过,第2种表示方式把内存的2进制数据写成了16进制数据。

memset(this, 0, sizeof *this)

有时候里面定义了很多int,char,struct等C语言里的那些类型的变量,我习惯在构造函数中将它们初始化为0,但是一句句的写太麻烦,所以直接就memset(this, 0, sizeof *this);将整个对象的内存全部置为0。对于这种情形可以很好的工作,但是下面几种情形是不可以这么使用的:

1.类含有虚函数表:这么做会破坏虚函数表,后续对虚函数的调用都将出现异常

2.类中含有C++类型的对象:例如,类中定义了一个list的对象,由于在构造函数体的代码执行之前就对list对象完成了初始化,假设list在它的构造函数里分配了内存,那么我们这么一做就破坏了list对象的内存。

16.Format

Format是CString类的一个成员函数,它通过格式操作使任意类型的数据转换成一个字符串。Format参数也是一个格式化字符串。DateTime是时间类型。返回值是一种格式化后的字符串。

Format("my name is %6s","wind");

Format("%d is %s",a,m_strMsg);

if(NULL == pStr || 0 == strlen(pStr)) return FALSE;//判断是否是空指针或者是

char ss[20]="";

strlen(ss)=NULL;

NULL=0;

sprintf

sprintf指的是字符串格式化命令,主要功能是把格式化的数据写入某个字符串中。sprintf 是个变参函数。使用sprintf 对于写入buffer的字符数是没有限制的,这就存在了buffer溢出的可能性。

sprintf(szBufKey,"0x%X",pVirKey->vkCode);

原型

int sprintf( char *buffer, const char *format, [ argument] … );

参数列表

buffer:char型指针,指向将要写入的字符串的缓冲区。

*format*:格式化字符串。

*[argument]..*.:可选参数,可以是任何类型的数据。

17.SplitString

comfun.h 的库函数

std::vector<CString> SplitString(CString sData, const char* sFlag)

std::vector<CString> vct= SplitString(pStr,"|");

//磁卡读卡器|2|COM,COM1,9600|0
将字符串或者,依照后面的字符串进行拆分,装到容器中

18.Vector

size: 表示实际容器中保存元素的个数

if(vct.size() > 1)

if(vct.size() > 2)

根据vec的容器大小进行赋值的一种判断方式

19.Compare

if(0 == vctsub[0].Compare("COM"))

比对字符串,真就为0

20.MakeUpper

MakeUpper()函数,用于将CString字符转化为一个大写的字符串。

21.MakeLower

此成员函数将此CString对象转换为一个小写字符串

22.repalce

用后面的参数替换前一个参数,当后面是空时,即去掉这个对象前面参数的数据

vctsub[1].MakeUpper();//vctsub[1]是COM1
strcpy_s(szCom,vctsub[1].GetBuffer(0));//szCom=COM1

vctsub[1].Replace("COM","");//获得COM1 的1, /vctsub[1]是1了
bCom = atoi(vctsub[1].GetBuffer(0));//string 转成int 

23.NOTIFYICONDATA

NOTIFYICONDATA是一个函数公式主要含义和作用是以此函数用来向任务栏托盘区域发送消息。

函数格式

BOOL Shell_NotifyIcon( DWORD dwMessage,PNOTIFYICONDATA lpdata);

24.CenterWindow

居中窗口

CenterWindow(GetDesktopWindow());

25.GetDesktopWindow

GetDesktopWindow,该函数返回桌面窗口的句柄。桌面窗口覆盖整个屏幕。桌面窗口是一个要在其上绘制所有的图标和其他窗口的区域。

26.WindowProc

该函数是一个应用程序定义的函数。它处理发送给窗口的消息。WINDPROC类型定义了一个指向该回调函数的指针。WindowProc是用于应用程序定义函数的占位符。

27.DefWindowProc

DefWindowProc函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。

WindowProc是你给自己的窗口定义的窗口处理函数DefWindowProc是windows平台提供的默认窗口处理函数

如果某些消息你不需要做特别的处理,调用DefWindowProc进行处理就可以了,不需要你自己再去些那些windows的"标准动作"

看看MFC的CWnd源码就一目了然了
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult)) // 如果消息是需要自己处理的,
  //处理后就不必让windows系统进行默认处理了
lResult = DefWindowProc(message, wParam, lParam); // 自己没有处理的就交给windows去做

return lResult;
}

28.IsIconic

IsIconic()作用是判断窗口是否处于最小化状态(点击了最小化按钮之后)

29.PreTranslateMessage

在MFC中,PreTranslateMessage是虚函数,是用来截获消息的。

PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当你需要在MFC之前处理某些消息时,常常要在这里添加代码.。

BOOL CPrinterSharedDlg::PreTranslateMessage(MSG* pMsg)//在唤出输入界面可以使用,回车或者esc来实现简单的控制
{
    // TODO: 在此添加专用代码和/或调用基类
    if (pMsg->message == WM_KEYDOWN)
    {
        switch (pMsg->wParam)
        {
        case VK_RETURN:
            return TRUE;
        case VK_ESCAPE:
            return TRUE;
        default:
            break;
        }
    }
    return CImageDlg::PreTranslateMessage(pMsg);
}

30.GetStyle,ModifyStyle,GetExStyle,ModifyStyleEx

要添加或除去窗口的普通风格,可以使用GetStyle()和ModifyStyle()方法来实现要设置或去除窗口的扩展风格,可以使用GetExStyle()和ModifyStyleEx() 方法实现

​ ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_LAYERED| WS_EX_TOOLWINDOW);//变透明窗口

31.GetSubMenu

GetSubMenu函数功能:该函数取得被指定菜单激活的下拉式菜单或子菜单的句柄。

CMenu* pMenu = menu.GetSubMenu(0);

32.LoadMenu

LoadMenu函数从与应用程序实例相联系的可执行文件(.EXE)中加载指定的菜单资源。

BOOL LoadMenu( LPCTSTR lpszResourceName );

BOOL LoadMenu( UINT nIDResource );

CMenu menu;

menu.LoadMenu(IDR_MENU1); //载入事先定义的选单

33.GetCursorPos

GetCursorPos,函数名。该函数检取光标的位置,以屏幕坐标表示。

CPoint pos;
GetCursorPos(&pos);

___ShowCursor

ShowCursor函数功能:该函数显示或隐藏光标。

34.SetForegroundWindow

函数将创建指定的窗口,并激活到前台窗口的线程 。键盘输入窗口,并为用户更改不同的视觉线索。该系统分配一个优先略高前景的窗口,比它其他线程创建的线程。

::SetForegroundWindow(this->m_hWnd);// 解决托盘图标菜单弹出后,点击其它地方,菜单不消失的问题  

先看看官方描述:

SetForegroundWindow:函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。

SetWindowPos:改变一个子窗口,弹出式窗口或顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。

Z序:就是窗口层叠的顺序阿, 从你看到的是最顶层,一次往下,Z越大,显示在屏幕越靠前的位置.Z小的窗口会被遮挡

什么意思呢,就是说SetForegroundWindow这个函数抢焦点,如果焦点被别的窗口抢回去,置顶就可能无效了。而SetWindowPos不会管焦点在哪。

我在程序中内嵌别的程序时,用定时器把要内嵌的程序一直置顶。先用了前者发现置顶可能无效并且一直抢走焦点也不是办法,换了后者才可行。

hWndad=::FindWindow(NULL,ExeTitle );

if ( hWndad!=NULL )

if (GetWindowLong(hWndad, GWL_EXSTYLE) & WS_EX_TOPMOST)
{
//::SetForegroundWindow(hWndad);
CWnd::FromHandle(hWndad)->SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE );

}

35.TrackPopupMenu

函数功能:该函数在指定位置显示快捷菜单,并跟踪菜单项的选择。快捷菜单可出现在屏幕上的任何位置。

pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pos.x, pos.y,AfxGetMainWnd());

当右键的时候,上面调用GetCursorPos来获得鼠标位置,在指定位置显示菜单

TPM_RIGHTBUTTON:若设置此标志,用户能用鼠标右键选择菜单项。

TPM_LEFTALIGN:若设置此标志,函数使快捷菜单的左边界与由参数X指定的坐标对齐。(显示在鼠标右边)

TPM_RIGHTALIGN:若设置此标志,函数使快捷菜单的右边界与由参数X指定的坐标对齐。(显示在鼠标左边)

36.互斥锁

EnterCriticalSection

多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,

就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。

LeaveCriticalSection

InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
//操作数据
MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区

37.GetModuleFileName

函数获取当前程序所在目录

GetModuleFileName() 函数返回当前进程已加载可执行或DLL文件的完整路径名(以'\0'终止),该模块必须由当前进程地址空间加载。如果想要获取另一个已加载模块的文件路径,可以使用GetModuleFileNameEx()函数。

CString strExePath;  
CString strPath;  
GetModuleFileName(NULL,strPath.GetBufferSetLength(MAX_PATH+1),MAX_PATH+1);  
AfxMessageBox(strPath);//"d:\我的文档\Visual Studio 2005\Projects\test\Debug\test.exe"  
int nPos = strPath.ReverseFind(_T('\\'));  
strExePath = strPath.Left(nPos+1);  
AfxMessageBox(strExePath);//"d:\我的文档\Visual Studio 2005\Projects\test\Debug\"  

38.GetBufferSetLength

申请一个指定长度的空间

39.CInternetSession

使用类CInternetSession 创建并初始化一个或多个同时的Internet 会话。如果需要,还可描述与代理服务器的连接

39.try 和catch的用法

#include<iostream.h>                            //包含头文件
#include<stdlib.h>
double fuc(double x, double y)                        //定义函数
{
    if(y==0)
    {
        throw y;                                    //除数为0,抛出异常
    }
    return x/y;                                    //否则返回两个数的商
}

void main()
{
    double res;
    try                                            //定义异常
    {
        res = fuc(2,3);
        cout << "The result of x/y is : " << res << endl;
        res = fuc(4,0);                                //出现异常
    }
    catch(double)                                    //捕获并处理异常
    {
        cerr<<"error of dividing zero.\n";
        exit(1);                                    //异常退出程序
    }
}
//catch(...) 就是抓取所以的异常

40.PathIsRelative

检查 路径 是否 是 相对的(路径)

41.PtInRect

类CRect的成员函数,其作用是判断一个点是否在CRect中

CRect rect1(0,0, nFalg, nFalg);

CPoint point

if(rect2.PtInRect(point))

42.OnCommnd ,pretranslatemessage ,windowproc

1.OnCommnd 处理消息,可以通过BEGIN_MESSAGE_MAP来处理消息

也可以通过postmessage来抛

postMessage是Windows API(应用程序接口) 中的一个常用函数,用于将一条消息放入到消息队列中。消息队列里的消息通过调用GetMessage和PeekMessage取得。

if(m_strAdminEnter.Find("12342314") >= 0) ::PostMessage(m_hWnd,WM_COMMAND,BTN_ADMIN_LOGOUT,NULL);

配合虚函数的OnCommand来使用
case BTN_ADMIN_LOGOUT:
在多线程应用中,PostMessage的用法还是一样,但SendMessage则不同了。
如果在线程A中向线程B所创建的一个窗口hWndB发送消息SendMessage(hWndB,WM_MSG,0,0),那么系统将会立即将执行权从线程A  切换到线程B ,然后在线程B中调用hWndB的窗口过程来处理消息,并且在处理完该消息后,                    执行权仍然在B手中!
        s这个时候,线程A则暂停在SendMessage处,
等待下次线程A获得执行权后才继续执行,并且仍然可以获得消息处理的结果(返回值)。

一般,为了避免死锁,在B中对WM_MSG做出处理之前,要加上:     if(InSendMessage())

2.pretranslatemessage 处理键盘鼠标

在MFC中,PreTranslateMessage是虚函数,是用来截获消息的。我们可以通过重载它来处理键盘和鼠标消息。在sdk中,这有所不同,我们必须在回调函数LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)中处理消息。

{
    switch(pMsg->message){
    case WM_LBUTTONDOWN:
        {
            CPoint point;
            GetCursorPos(&point);
            OnAdminEnter(point);
        }   

3.windowproc 获取类似整个框架的,例如窗口创建WM_CREATE

43.HHOOK

HHOOK 钩子,比如在创建窗口的时候下钩子

g_HookHwnd = SetWindowsHookEx(WH_KEYBOARD_LL, MyHookFun, g_hInst, 0);

捕捉一些键盘事件,比如屏蔽按键WM_SYSKEYUP

44.SetWindowText函数

设置对话框标题或者对话框控件文本的内容例子

作用 向对话框中的控件发送消息

45.增加滚动字条

CRect rect(0,m_rc.Height()-70,m_rc.Width()-120,m_rc.Height()); AddScrollText(rect,g_Config.GetSubPagePath("ADTXT").GetBuffer(0),60,"微软雅黑",RGB(255,0,0),TXT_SCRO,1);

46.trace

宏仅仅在程序的DEBUG版本中出现,当RELEASE的时候该宏就完全消失了,从而帮助你调试也在RELEASE的时候减少代码量。

使用非常简单,格式如下: TRACE("DDDDDDDDDDD"); TRACE("wewe%d",333); 同样还存在TRACE0,TRACE1,TRACE2。。。分别对应0,1,2。。个参数 TRACE信息输出到VC IDE环境的输出窗口(该窗口是你编译项目出错提示的那个窗口),但仅限于你在VC中运行你的DEBUG版本的程序。

TRACE("屏幕密码是:%s\n",m_strAdminEnter);

47.WM_CHAR、WM_KEYDOWN和WM_SYSKEYDOWN消息

WM_KEYDOWN和WM_CHAR都是键盘消息。TranslateMessage函数已经将按键消息转换成字符消息了,那么WndProc函数中需要对事件进行选择。如:键入“D”键,就应该选择WM_CHAR,因为WM_CHAR 只是字母,不包含特殊字符如Ctrl等。

如果键盘键入的是“Ctrl+D”,则应该选择WM_KEYDOWN,因为WM_KEYDOWN既包含字母也包含特殊字符。

​ WM_CHAR是由WM_KEYDOWN消息Translate()之后产生的,然后再发送给窗口过程。例如按下“D”键,产生WM_KEYDOWN消息,此消息经过Translate()处理后变成了WM_KEYDOW、WM_CHAR两个消息传递给窗口过程。

​ 而WM_SYSKEYDOWN是接受快捷键或系统命令按键的,像Alt键就是。所以捕获Alt键时,在WM_KEYDOWN下是无效的,要在WM_SYSKEYDOWN中。Ctrl和shift不属于WM_SYSKEYDOWN。

48.Navigate

m_ctrNet.Navigate(bstrUrl,&vInfo,&vInfo,&vInfo,&vInfo);

在ActivteX加载网页

49.Domodal

Windows对话框分为两类:模态对话框和非模态对话框。

​ 模态对话框是这样的对话框,当它弹出后,本应用程序其他窗口将不再接受用户输入,只有该对话框响应用户输入,在对它进行相应操作退出后,其他窗口才能继续与用户交互。

​ 非模态对话框则是,它弹出后,本程序其他窗口仍能响应用户输入。非模态对话框一般用来显示提示信息等。

50.GetFullPath

默认是deBug路径下的文件名

CString BmpPath = GetFullPath("photo.bmp");

CString picpath = "DATA\\pic\\bg.jpg";//直接写要写成这种格式

加载图片1:

CStatic m_Bitmap;//picture Control控件

CString BmpPath = GetFullPath("photo.bmp");//getfullpath默认路径是debug
    CImage bitmap;  // CBitmap对象,用于加载位图 
    m_Bitmap.ShowWindow(SW_SHOW);

    CDC* pDC = m_Bitmap.GetDC();
    HRESULT hResult = bitmap.Load(BmpPath);
    if (hResult == S_OK)
    {
        m_Bitmap.SetWindowPos(NULL, 0, 0, bitmap.GetWidth(), bitmap.GetHeight(), SWP_NOMOVE | SWP_SHOWWINDOW);
        Sleep(0.5);
        bitmap.Draw(pDC->m_hDC, 0, 0, bitmap.GetWidth(), bitmap.GetHeight());
    }

加载图片2:

CRect m_rc;
构造中:
m_rc = CRect(0, 0, 900,600);

Oninit中:

CString picpath = "DATA\\pic\\bg.jpg";
MoveWindow(m_rc);
AddImage(0, 0, picpath);

53.right

函数的功能是从字符串右端取指定个数字符

dim txt
txt="This is a beautiful day!"
document.write(Right(txt,11))
//输出:
//utiful day!

54.ReverseFind

返回此CString对象中与要求的字符匹配的最后一个字符的索引;如果没有找到需要的字符则返回-1。

CString s( "abcabc" );
s.ReverseFind( 'b' ) == 4 // true;//从0开始0,1,2,3,4

55.typedef

非MFC DLL学习中出现了一个不太好懂的语句:

typedef int(*lpAddFun)(int, int); //宏定义函数指针类型 
这句宏定义函数指针类型是什么意思呢?  
我们知道typedef 是宏定义,一般的语法是这样:
typedef unsigned long ulong; 
这个定义是用ulong作unsigned long 的别名
而在这里的意思是定义一种指针类型lpAddFun,它是一种指向函数int (int,int)的指针,也就是说lpAddFun表示的是这种类型的函数的地址。
换句话说,lpAddFun是typedef定义的一个名称,可以用来定义变量。
比如  lpAddFun p;
那p就是  int(*p)(int, int);
 首先(*p)说明p是一个指针,(*p)();说明p指向函数,
 (*p)(int, int)说明p指向的函数有两个int类型参数,
 最后  int(*p)(int, int);说明p指向的函数返回值类型是int。
举个例子说明:和调用动态库时的那个typedef一致
#include <stdio.h>
#include <windows.h> 
typedef int (*ADD)(int, int);
typedef int (*SUB)(int, int);

int main(int argc, char* argv[])
{    
    HMODULE hmodule11 = ::LoadLibrary("DynamicDLL.dll");           ADD Add= (ADD) GetProcAddress(hmodule11,"GetAdd");     
    SUB Sub= (SUB) GetProcAddress(hmodule11,"GetSub");             int x=10,y=5;      
    int nRes = Add(x,y);       
    int nRes2= Sub(x,y);        
    FreeLibrary(hmodule11);      
    printf("%d,%d\n",nRes,nRes2);    
    return 0;
}
〉其中ADD 和SUB 是两个函数指针类型。
首先你要明白函数指针的概念
int *p(int ,int );//声明一个函数
int (*p)(int ,int);//声明一个函数指针
typedef int(*lpAddFun)(int, int);
就是把这个类型的函数指针的声明变为lpAddfun;

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页