目录
基础
地址分配
1.栈的地址是由高向低增长的.
2.堆得地址增长方向是由低到高向上增长的
大小端
对于整数0x12345678:
获取进程信息
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <tlhelp32.h>
//进程结构体如下
/*
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;//结构大小
DWORD cntUsage;//此进程的引用计数
DWORD th32ProcessID;//进程ID
DWORD th32DefaultHeapID;//进程默认堆ID
DWORD th32ModuleID;//进程模块ID
DWORD cntThreads;//此进程开启的线程计数
DWORD th32ParentProcessID;//父进程ID
LONG pcPriClassBase;//线程优先权
DWORD dwFlags;//保留
char szExeFile[MAX_PATH];//进程名
} PROCESSENTRY32;
*/
int GetProcess()
{
//PROCESSENTRY32结构体,保存进程具体信息
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
//获得系统进程快照的句柄
//TH32CS_SNAPPROCESS:表示快照信息包含系统的所有进程的列表
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot error.\n");
return 0;
}
//首先获得第一个进程
BOOL bProcess = Process32First(hProcessSnap, &pe32);
//循环获得所有进程
while (bProcess)
{
//打印进程名和进程ID
printf("%ls----%d\n", pe32.szExeFile, pe32.th32ProcessID);
bProcess = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return 0;
}
int main()
{
GetProcess();
return 0;
}
当前运行目录
TCHAR szModuleNameBuff[MAX_PATH];
::GetModuleFileName(NULL, szModuleNameBuff, MAX_PATH);
TCHAR drive[_MAX_DRIVE];
TCHAR dir[_MAX_DIR];
TCHAR fname[_MAX_FNAME];
TCHAR ext[_MAX_EXT];
_tsplitpath_s(szModuleNameBuff, drive, dir, fname, ext);
CString str;
CString szFileName(_T("file.exe"));
str.Format(_T("%s%s%s"), drive, dir, szFileName);
MessageBox(str);
关闭一个进程
OpenProcess
OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。
HANDLE OpenProcess(
DWORD dwDesiredAccess, //想拥有的该进程访问权限,PROCESS_ALL_ACCESS 所有能获得的权限
BOOL bInheritHandle, //表示所得到的进程句柄是否可以被继承
DWORD dwProcessId //被打开进程的PID
);
返回值为指定进程的句柄。
通过一个进程关闭另外一个进程的时候,一般的做法就是枚举系统打开的所用进程的标识符(PID),使用OpenProcess函数获得进程的句柄,该函数可以通过第一个参数来设置句柄的新的访问权限。
TerminateProcess
这个函数可以用来终止或者说杀死一个进程,它不会留给进程及其所有线程清理的时间,系统会马上终止(杀死)这个进程的所有线程,致使进程终止。在使用此函数前我们必须要调用OpenProcess
函数来获得我们要终止(杀死)进程的句柄,并且要获得进程的PROCESS_TERMINATE
权限。
BOOL TerminateProcess(
HANDLE hProcess, //要终止(杀死)进程的句柄,需要有PROCESS_TERMINATE权限
UINT uExitCode //设置进程的退出值。可通过GetExitCodeProcess函数得到一个进程的退出值。
)
如果失败将返回FALSE(0),而成功将返回一个非零值。
HANDLE hCurrentProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessID);
TerminateProcess(hCurrentProcess,0);
SHELLEXECUTEINFO
typedef struct _SHELLEXECUTEINFO {
DWORD cbSize; //该结构大小,以字节为单位
ULONG fMask; //一个标志数组,用来设置其他成员的有效性
HWND hwnd; //可选。执行ShellExecuteEx的窗口句柄,可设为NULL
LPCTSTR lpVerb; //指定执行的动作,包括:edit ,explore ,find ,open,print, properties
LPCTSTR lpFile; //以\0 结尾的字符串,指出 lpVerb 的操作对象的路径
LPCTSTR lpParameters; //可选。运行/打开程序的参数,如果打开的是一个文档,则该项无效
LPCTSTR lpDirectory; //可选。指明工作目录的名字,默认为当前目录
int nShow; //必须。指定打开的程序的显示方式,为SW_值中的一个。
HINSTANCE hInstApp;//调用成功,则该项的值大于32,如果调用失败,则将设置为SE_ERR_XXX 的错误值。
LPVOID lpIDList; //一个ITEMIDLIST结构的地址,用来存储成员的特别标识符
LPCTSTR lpClass; //用以指明文件类别的名字或GUID
HKEY hkeyClass; //获得已在系统注册的文件类型的Handle,当fMask不包括SEE_MASK_HOTKEY时该项被忽略
DWORD dwHotKey; //程序的热键关联
union {
HANDLE hIcon; //取得对应文件类型的图标的Handle
HANDLE hMonitor; //将文档显示在显示器上的Handle
} DUMMYUNIONNAME;
HANDLE hProcess; //指向新启动的程序的句柄
} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
BOOL ShellExecuteEx(LPSHELLEXECUTEINFO lpExecInfo);
lpExecInfo:
[in, out] 一个指向 SHELLEXECUTEINFO 结构的指针,用来传递和保存应用程序执行相关的信息。
示例
CString lpDirectory = "\\file\\execfiles\\";
CString path = "\\file\\execfiles\\start.bat";
SHELLEXECUTEINFO si;
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
si.hwnd = NULL;
si.lpVerb = _T("open");
si.lpDirectory = lpDirectory;
si.lpFile = path;
si.lpParameters=NULL;
si.nShow = SW_HIDE;
si.fMask = SEE_MASK_NOCLOSEPROCESS;
if (!ShellExecuteEx(&si))
{
MessageBox(_T("启动失败"));
}
if (si.hProcess != NULL)
{
m_Handle = si.hProcess;
}
MFC设置程序以管理员权限运行
1.选择工程
2.右键工程属性
3.配置属性->链接器->清单文件
https://www.cnblogs.com/kingbin/p/4115034.html
运行其他应用
HINSTANCE ShellExecute(
HWND hwnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,
INT nShowCmd
);
hWnd:用于指定父窗口的句柄。当函数调用过程中出现错误时,它将作为Windows消息窗口的父窗口。
lpOperation:用于指定要进行的操作, 如: open、runas、print、edit、explore、find。当此参数为NULL时,默认执行open操作。(open:表示执行由lpFile参数指定的程序,或者打开由lpFile参数指定的文件或文件夹。explort:表示打开由lpFile参数指定的文件夹。print:表示打印由lpFile参数指定的文件。)
lpParameters:若lpFile参数是一个可执行文件,则此参数指定命令行参数,否则此参数应设为NULL。
lpDirectory:用于指定默认目录
nShowCmd:用于指定程序窗口初始显示方式。
原文链接:https://blog.csdn.net/jolin678/article/details/119300890
托盘图标操作
结构体
typedef struct _NOTIFYICONDATA {
DWORD cbSize; //以字节为单位的这个结构的大小
HWND hWnd; //接收托盘图标通知消息的窗体句柄
UINT uID; //应用程序定义的该图标的ID号
UINT uFlags; //设置该图标的属性
UINT uCallbackMessage; //应用程序定义的消息ID号,此消息传递给hWnd
HICON hIcon; //图标的句柄
char szTip[64]; //鼠标停留在图标上显示的提示信息
} NOTIFYICONDATA, *PNOTIFYICONDATA;
uFlags: 一个标志,用来表示结构体中剩下的字段哪些是可用的,或者提供提示条应当如何显示的 附加信息,能够使下列之中的一个或组合:
1. NIF_ICON 设置成员hIcon有效
2. NIF_MESSAGE 设置成员uCallbackMessage有效
3. NIF_TIP 设置成员szTip有效,一个标准提示字符串,以NULL结尾。包括结尾NULL字符,最多可以64个
uCallbackMessage:应用程序定义的消息ID。系统使用这个ID向由hWnd指定的窗口发送通知消息。当鼠标事件在图标区域发生、鼠标在图标的相关区域晃悠时,发送这个消息;当用鼠标或者键盘选中了图标、或者这些行为发生在气泡通知区域时,发送消息。
加载图标资源
LoadIcon 从与 hInstance 模块相关联的可执行文件中装入lpIconName指定的图标资源,仅当图标资源还没有被装入时该函数才执行装入操作,否则只获取装入的资源句柄,其原型:
HICON LoadIcon(
HINSTANCE hInstance,
LPCTSTR lpIconName
);
选择图标的做法是,即使用 MAKEINTRESOURCE
宏对一个十六位数的资源标识符(高 8 位为0,低 8 位为图标资源ID)进行转换。
使用该宏的时候,我们往往使用 Visual Studio 为我们自动产生资源头文件 resource.h 和 资源文件 OurProject.rc 。.rc 文件是个文本文件,我们可以在记事本里编辑它们,但一般不会直接去这么做。新建图标资源的操作结果会写到该文件中。
比如,可以通过 VS 里的“文件”–> “新建” --> “文件”–> “图标文件(.ico)” 来新添加一个图标,接着可以编辑该图标,最后保存。如果要添加该新建的图标,可以在“解决方案资源管理器”窗口底下的“资源文件”文件夹下双击打开 "OurProject.rc“ 文件,接着右键点击“Icon “文件夹图标,在弹出的菜单中选择“添加资源”,在弹出的对话框中,选择“Icon”,然后点击右边的“导入”按钮,将之前编辑好的图标资源添加进来。
参考
HINSTANCE AfxGetInstanceHandle( );
返回值:代表应用程序的当前实例的句柄HINSTANCE值。
这个函数使你能够获得当前应用程序的实例句柄。AfxGetInstanceHandle总是返回代表你的可执行文件(.EXE)的HINSTANCE值,除非它从与MFC的USRDLL版本连接的DLL内调用的。在这种情况下,它返回的是DLL的HINSTANCE值。
所以加载图标的方法可以使用:LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME))
其中IDR_MAINFRAME为图标资源的ID。
Shell_NotifyIcon
全局函数Shell_NotifyIcon() 用于在托盘上添加、删除或改动图标。其原型为:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage,PNOTIFYICONDATA pnid);
//Pnid 是上面的NOTIFYICONDATA结构的指针;
//dwMessage 是被传递的消息,能够是下面消息之中的一个:
// NIM_ADD 添加图标
// NIM_DELETE 删除图标
// NIM_MODIFY 改动图标
为使应用程序退出时图标消失,映射WM_DESTROY消息,在OnDestroy()函数中增加:
Shell_NotifyIcon(NIM_DELETE,&m_tnid);
NOTIFYICONDATA nid;
nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
nid.hWnd=this->m_hWnd;
nid.uID=IDR_MAINFRAME;
nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP ;
nid.uCallbackMessage=WM_SHOWTASK;//自定义的消息名称,如果没特殊用途用WM_COMMAND
nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
wcscpy(nid.szTip,L"MobilePublishRun");//信息提示条为“MobilePublishRun”
Shell_NotifyIcon(NIM_DELETE,&nid);//删除托盘区图标
windows共享内存
共享内存主要是通过映射机制实现的。
Windows 下进程的地址空间在逻辑上是相互隔离的,但在物理上却是重叠的。所谓的重叠是指同一块内存区域可能被多个进程同时使用。当调用 CreateFileMapping 创建命名的内存映射文件对象时,Windows 即在物理内存申请一块指定大小的内存区域,返回文件映射对象的句柄 hMap。为了能够访问这块内存区域必须调用 MapViewOfFile 函数,促使 Windows 将此内存空间映射到进程的地址空间中。当在其他进程访问这块内存区域时,则必须使用OpenFileMapping 函数取得对象句柄 hMap,并调用 MapViewOfFile 函数得到此内存空间的一个映射。这样一来,系统就把同一块内存区域映射到了不同进程的地址空间中,从而达到共享内存的目的。
下面举例说明如何将内存映射文件用于共享内存。
第一次运行这个例子时,它创建了共享内存,并写入数据“This is common data!” 。只要创建共享内存的进程没有关闭句柄hMap,以后运行的程序就会读出共享内存里面的数据,并打印出来。这就是使用共享内存在进程间通信的过程。程序代码如下。
#include <windows.h>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main()
{
wstring strMapName(TEXT("ShareMemory")); // 内存映射对象名称
wstring strComData(TEXT("This is common data!")); // 共享内存中的数据
LPVOID pBuffer; // 共享内存指针
// 首先试图打开一个命名的内存映射文件对象
HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, strMapName.c_str());
if (NULL == hMap)
{ // 打开失败,创建
hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
strComData.length() + 1,
strMapName.c_str());
// 映射对象的一个视图,得到指向共享内存的指针,设置里面的数据
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
//strcpy((char*)pBuffer, strComData.c_str());
wcscpy_s((wchar_t*)pBuffer,1024, strComData.c_str());
wcout << "写入共享内存数据:" << (wchar_t *)pBuffer << endl;
}
else
{ // 打开成功,映射对象的一个视图,得到指向共享内存的指针,显示出里面的数据
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
wcout << "读取共享内存数据:" << (wchar_t *)pBuffer << endl;
}
getchar(); // 注意,进程关闭后,所有句柄自动关闭,所以要在这里暂停
// 解除文件映射,关闭内存映射文件对象句柄
::UnmapViewOfFile(pBuffer);
::CloseHandle(hMap);
system("pause");
return 0;
}
CreateFileMapping
创建一个有名的共享内存
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全属性,一般设置NULL
DWORD flProtect, //保护方式,可以是PAGE_READONLY或是PAGE_READWRITE
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
)
-
物理文件句柄
任何可以获得的物理文件句柄, 如果你需要创建一个物理文件无关的内存映射也无妨, 将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了。
如果需要和物理文件关联, 要确保你的物理文件创建的时候的访问模式和"保护设置"匹配, 比如: 物理文件只读, 内存映射需要读写就会发生错误. 推荐你的物理文件使用独占方式创建。
如果使用INVALID_HANDLE_VALUE
, 也需要设置需要申请的内存空间的大小, 无论物理文件句柄参数是否有效, 这样 CreateFileMapping 就可以创建一个和物理文件大小无关的内存空间给你, 甚至超过实际文件大小, 如果你的物理文件有效, 而大小参数为0, 则返回给你的是一个和物理文件大小一样的内存空间地址范围. 返回给你的文件映射地址空间是可以通过复制, 集成或者命名得到, 初始内容为0。返回值是新创建的文件映射对象的句柄(Mapping 对象的句柄)
注意:
创建文件映射对象后,文件大小不得超过文件映射对象的大小;如果是,则并非所有文件内容都可用于共享。
如果应用程序指定文件映射对象的大小大于磁盘上实际命名文件的大小,并且页面保护允许写访问(即,flProtect参数指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE),则磁盘上的文件增加以匹配文件映射对象的指定大小。如果文件被扩展,则文件旧端与文件新端之间的文件内容不保证为零;行为由文件系统定义。如果磁盘上的文件无法增加,则CreateFileMapping将失败,GetLastError将返回ERROR_DISK_FULL。
多个进程可以通过使用单个共享文件映射对象或创建由同一文件支持的单独文件映射对象来共享同一文件的视图。单个文件映射对象可以由多个进程共享,方法是在创建进程时继承句柄,复制句柄或按名称打开文件映射对象。
创建文件映射对象实际上并不将视图映射到进程地址空间。 MapViewOfFile和MapViewOfFileEx函数将文件视图映射到进程地址空间。
文件映射对象的映射视图维护对象的内部引用,并且文件映射对象在释放对它的所有引用之前不会关闭。因此,要完全关闭文件映射对象,应用程序必须通过调用UnmapViewOfFile取消映射文件映射对象的所有映射视图,并通过调用CloseHandle来关闭文件映射对象句柄。可以按任何顺序调用这些函数。
通过映射视图修改文件时,可能不会自动更新上次修改时间戳。如果需要,调用者应使用SetFileTime设置时间戳。
参考原文
参考
OpenFileMapping
HANDLE WINAPI OpenFileMapping(
__in DWORD dwDesiredAccess,//映射(Mapping)对象的存取权限,同 CreateFileMapping() 中的 flProtect 参数
__in BOOL bInheritHandle,//如果设为 TRUE ,那么可以继承进程句柄,否则不能继承,一般可设为 FALSE 。
__in LPCTSTR lpName //打开的 Mapping 对象的名字,由 CreateFileMapping 的 lpName 参数指定
);
该函数用来打开已经存在的并且有名的文件映射。
函数若执行成功则返回一个 Mapping 对象的句柄,失败返回 NULL 。
MapViewOfFile
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject, //共享文件对象句柄
__in DWORD dwDesiredAccess, //文件共享属性
__in DWORD dwFileOffsetHigh, //文件共享区的偏移地址
__in DWORD dwFileOffsetLow, //文件共享区的偏移地址
__in SIZE_T dwNumberOfBytesToMap //共享数据长度
);
翻译后的描述是 将文件映射的视图映射到调用进程的地址空间。也就是 将共享内存的文件内容映射到调用进程中的地址上。
释放映射内存
使用了MapViewOfFile将文件映射到内存
使用完 必须 使用UnmapViewOfFile 取消映射,否则报内存泄露
UnmapViewOfFile(lpBuffer);
关闭文件映射
CloseHandle(hMap);
示例2
// 发送端
#include "stdafx.h"
#include <WINDOWS.H>
BOOL Send()
{
//创建FileMapping对象
HANDLE hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared"));
if (NULL == hMapObject)
{
printf("创建文件映像失败\n");
return FALSE;
}
//将FileMapping对象映射到自己的进程
HANDLE hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0);
if (NULL == hMapView)
{
printf("内存映射失败\n");
return FALSE;
}
//写入数据
memset((char*)hMapView,0,0x1000);
strcpy((char*)hMapView,"Test shared memory.");
return TRUE;
}
int main(int argc, char* argv[])
{
Send();
return 0;
}
//接收端
// ShareMemory_Recv.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
BOOL Recv()
{
//创建FileMapping对象
HANDLE hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared"));
if (NULL == hMapObject)
{
printf("共享内存失败\n");
return FALSE;
}
//将FileMapping对象映射到自己的进程
HANDLE hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0);
if (NULL == hMapView)
{
printf("内存映射失败\n");
return FALSE;
}
//读取数据
char szBuffer[0x1000] = {0};
memcpy(szBuffer,hMapView,0x1000);
printf("%s\n",szBuffer);
return TRUE;
}
int main(int argc, char* argv[])
{
Recv();
return 0;
}
ini文件操作
写入ini
BOOL WritePrivateProfileString(
LPCTSTR lpAppName, //INI文件中的一个字段名
LPCTSTR lpKeyName, //lpAppName下的一个键名,通俗讲就是变量名
LPCTSTR lpString, //键值,也就是变量的值,不过必须为LPCTSTR型或CString型的
LPCTSTR lpFileName //完整的INI文件名,即包括路径
);
读ini
读取字符串值
DWORD GetPrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
LPCTSTR lpDefault, // 如果INI文件中没有前两个参数指定的字段名或键名,则将此值赋给变量.
LPTSTR lpReturnedString,//接收INI文件中的值的对象,即目的缓存器
DWORD nSize, //目的缓存器的大小
LPCTSTR lpFileName //完整的INI文件名
);
//使用
CString strStudName;
int nStudAge;
GetPrivateProfileString("StudentInfo","Name","默认姓名",strStudName.GetBuffer(MAX_PATH),MAX_PATH,"c:\\stud\\student.ini");
// 执行后 strStudName 的值为:"张三",若前两个参数有误,其值为:"默认姓名"
读取整型
UINT GetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
INT nDefault,
LPCTSTR lpFileName
);
返回读取的结果。
注意:
- INI文件的路径必须完整,文件名前面的各级目录必须存在,否则写入不成功,该函数返回 FALSE 值.
- 文件名的路径中必须为 \ ,因为在VC++中, \ 才表示一个 \ .
- 也可将INI文件放在程序所在目录,此时 lpFileName 参数为: “.\student.ini”.
ini文件结构
[小节名]
关键字=值
INI文件允许有多个小节,每个小节又允许有多个关键字, "="后面是该关键字的值。
值的类型有三种:字符串、整型数值和布尔值。其中字符串存贮在INI文件中时没有引号。
布尔真值用1表示,布尔假值用0表示。
一般文件操作
CopyFile
5–拒绝访问,一般可能是格式错误,比如目标文件没有写文件名字
123–文件名、目录名或卷标语法不正确,比如路径格式写错了
错误连接:https://www.cnblogs.com/MakeView660/p/10249697.html
char exitfile[20] = { "G://test//hello.txt" };
char newfile[40] = { "G://target//hello.txt" };
if (!CopyFile(exitfile, newfile, true))
{
printf("%d\n",GetLastError());
if (GetLastError() == 80)
{
TCHAR a;
printf("%s文件已存在,是否进行替换?Y|N:", newfile);
scanf_s("%c", &a);
if ((a == 'Y') || (a == 'y'))
{
CopyFile(exitfile, newfile, false);
}
}
}
CFile
MFC提供了CFile类方便文件的读写,首先要知道,文件的数据读取、数据写入与文件指针的操作都是以字节为单位的,数据的读取和写入都是从文件指针的位置开始的,当打开一个文件的时候,文件指针总是在文件的开头。
CFile file;
file.open(
LPCTSTR lpszFileName, //文件名,可包含文件路径,若只有文件名,则默认路径为工程路径
UINT nOpenFlags, //文件打开模式
CFileException* pError = NULL //打开失败时用来接收失败信息,一般设置为NULL。
);
nOpenFlags的常用模式有:
CFile::modeCreate
若打开文件不存在,则创建一个新文件,如果该文件存在,则清空它的数据。
CFile::modeNoTruncate
与CFile::modeCreate 组合使用。如果文件不存在,则创建一个新文件,如果文件存在,则保留他原本的数据。
CFile::modeRead
打开文件用于读取数据。
CFile::modeWrite
打开文件用于写入数据。
CFile::modeReadWrite
打开文件用于读取和写入数据。
CFile::typeBinary
使用二进制文件模式。此模式仅用于CFile的派生类。
CFile::typeText
使用文本文件模式。此模式仅用于CFile的派生类。
使用CFile操作文件的流程如下:
- 构造一个CFile对象。
- 调用CFile::Open()函数创建、打开指定的文件。
- 调用CFile::Read()和CFile::Write ()进行文件操作。
- 调用CFile::Close()关闭文件句柄。
文件指针的位置设置可以使用:
- Seek( LONG lOff, UINT nFrom ) 把文件指针移动到指定位置
lOff :是指针偏移字节数,若向后偏移则为正数,若向前偏移,则为负数。
nFrom :MSDN上有三种取值:
CFile::begin 从文件开头开始算起,lOff为正数;
CFile::current 当前位置开始算起;
CFile::end 从文件结尾开始算起,lOff为负数; - SeekToBegin( ) 把文件指针移到文件开头
- SeekToEnd( ) 把文件指针移到文件末尾
- GetPosition( ) 返回当前文件指针的位置
获取文件的字节数可用 GetLength( ) 此函数的返回值为DWORD,但可直接用来分配数组元素数目
DOWRD len=file.GetLength();
char *pBuf=new char[len+1
相反的,SetLength(0)
清空文件。
读取
//写入文件:
CFile file;
file.Open("E:\\VC\\1.txt",CFile::modeCreate|CFile::modeWrite|CFile::modeNoTruncate,NULL);
file.Write("HelloWorld",strlen("HelloWorld"));
//Write( const void* lpBuf, UINT nCount ) lpBuf是写入数据的Buf指针,nCount是Buf里需要写入文件的字节数
file.close( );
//读取文件:
CFile file;
file.Open("E:\\VC\\1.txt",CFile::modeRead,NULL);
DWORD len=file.GetLength();
char Buf[len+1];
Buf[len]=0; //0终止字符串,用于输出。
file.Read(Buf,len); //Read( void* lpBuf, UINT nCount ) lpBuf是用于接收读取到的数据的Buf指针nCount是从文件读取的字节数
MessageBox(Buf);
GetPosition
文档描述函数的作用是,返回的文件指针的位置,一直以为是个指针,其实类型都不一样。这里返回的应该是偏移量。如:
CFile File(TEXT("test2.my"), CFile::modeCreate | CFile::modeWrite | CFile::typeBinary);
DWORD lActual = File.Seek(5, CFile::begin);//lActual 的值是5
DWORD pos = File.GetPosition();//pos 的值是5
注意事项
- 在CFile 调用modeCreate标志时(无论有无modeNoTruncate标志)构造时,当前位置是文件起始位,也就同
seektobegin()/seek(0,CFile::begin)
;Open
同样.每次用write
写完后,会自动定位到文件结束位,同seektoend/seek(0,CFile::end)
;如果你不记得这些规定,那么每次都先调用Seek函数定下位. - 覆盖文件中间的内容:用Seek定位写入位置Pos,再write写入N个定节,会将Pos后的N个字节覆盖,其他内容不变.
windows 信号量
创建信号量
HANDLE WINAPI CreateSemaphore(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全属性。如果是NULL,就表示使用默认属性
_In_ LONG lInitialCount,//信号量的初始数值,必须大于或等于0,并且小于或等于lMaximumCount
_In_ LONG lMaximumCount,//信号量的最大值,即最大并发数
_In_opt_ LPCWSTR lpName//信号量的名字,是一个字符串,任何线程(或进程)都可以根据这一名称引用到这个信号量,这个值可以是NULL,表示产生一个匿名信号量。
);
如果成功就返回一个handle,否则传回NULL
打开信号量
HANDLE WINAPI OpenSemaphore(
_In_ DWORD dwDesiredAccess,//访问权限,一般传入SEMAPHORE_ALL_ACCESS。
_In_ BOOL bInheritHandle,//信号量句柄继承性,一般传入True。
_In_ LPCSTR lpName//需要打开的信号量的名称
);
如果成功就返回信号量handle,否则传回NULL
解除锁定信号量
实现信号量计数器增加一个值,该值通常是1,但不会超过创建信号量时指定的lMaximumCount
BOOL WINAPI ReleaseSemaphore(
_In_ HANDLE hSemaphore,//信号量的句柄
_In_ LONG lReleaseCount,//信号量值增加的个数,必须大于0且不超过最大资源数,一般为1
_Out_opt_ LPLONG lpPreviousCount//传出先前信号量的计数值,设置为NULL表示不需要传出
);
如果成功就返回True,否则传回False
关闭信号量
由于信号量是一个内核对象,关闭时直接调用CloseHandle()
就可以。
示例
#include<iostream>
#include <windows.h>
using namespace std;
const int THREAD_NUM = 10;
int g_Num = 0;
CRITICAL_SECTION g_csVar; //创建关键段cs
HANDLE g_ThreadSema; //创建内核对象,用来初始化信号量
DWORD WINAPI Func(LPVOID);
int main()
{
InitializeCriticalSection(&g_csVar);
g_ThreadSema = CreateSemaphore(NULL, 0, 1, NULL); //创建匿名信号量,初始资源为零,最大并发数为1,
HANDLE handle[THREAD_NUM];
DWORD ThreadId[THREAD_NUM];
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = CreateThread(NULL, 0, Func, &i, 0, &ThreadId[i]);
WaitForSingleObject(g_ThreadSema, INFINITE); //等待信号量资源数>0
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, true, INFINITE);
CloseHandle(g_ThreadSema); //销毁信号量
DeleteCriticalSection(&g_csVar);//销毁关键段cs
for (i = 0; i < THREAD_NUM; i++)
{
CloseHandle(handle[i]);
}
return 0;
}
DWORD WINAPI Func(LPVOID p)
{
int nThreadNum = *(int*)p;
EnterCriticalSection(&g_csVar);
cout << "线程编号为: " << nThreadNum << " 全局资源值为:" << ++g_Num << endl;
LeaveCriticalSection(&g_csVar);
ReleaseSemaphore(g_ThreadSema, 1, NULL); //信号量资源数加一
return 0;
}
队列queue
直接copy代码,哈哈
/*
queue
Author:YuBo
Date:2018/2/5
*/
#include<iostream>
#include<queue>
using namespace std;
queue<int> q;
void menu()
{
cout<<"******1.入队 2.出队**********"<<endl;
cout<<"******3.读取队顶 4.读取队尾******"<<endl;
cout<<"******5.队中元素个数 6.退出**********"<<endl;
}
void PUSH()
{
int i;int n;int N;
cout<<"请输入你要输入的整数个数:"<<endl;
cin>>N;
cout<<"请输入"<<N<<"个整数:"<<endl;
for(i=0;i<N;i++)
{
cin>>n;
q.push(n);
}
}
void POP()
{
int i,N;
cout<<"请输入你要删除的整数个数:"<<endl;
cin>>N;
if(N>q.size())cout<<"里面没有这么多元素。。。"<<endl;
else{
for(i=0;i<N;i++)
{
q.pop();
}
}
}
void Getfront()
{
if(q.empty())cout<<"队已经空了!"<<endl;
else cout<<"队顶元素为:"<<q.front()<<endl;
}
void Getback()
{
if(q.empty())cout<<"队已经空了!"<<endl;
else cout<<"队尾元素为:"<<q.back()<<endl;
}
void Getsize()
{
cout<<"队的大小为:"<<q.size()<<endl;
}
int main()
{
int i;
while(1)
{
menu();
cout<<"请输入菜单号:"<<endl;
cin>>i;
if(i==6)break;
switch(i)
{
case 1:PUSH();break;
case 2:POP();break;
case 3:Getfront();break;
case 4:Getback();break;
case 5:Getsize();break;
default:cout<<"输入错误!";break;
}
}
return 0;
}
参考:https://blog.csdn.net/lady_killer9/article/details/79261798
string 函数
find_first_of
CString方法
CString 对象的 AllocSysString 方法将 CStr
ing 转化成 BSTR:
字符串转换
sstream
<sstream> 主要用来进行数据类型转换,由于 <sstream> 使用 string 对象来代替字符数组(snprintf 方式),避免了缓冲区溢出的危险;而且,因为传入参数和目标对象的类型会被自动推导出来,所以不存在错误的格式化符号的问题。简单说,相比 C 编程语言库的数据类型转换,<sstream> 更加安全、自动和直接。
#include <string>
#include <sstream>
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
stringstream sstream;
string strResult;
int nValue = 1000;
// 将int类型的值放入输入流中
sstream << nValue;
// 从sstream中抽取前面插入的int类型的值,赋给string类型
sstream >> strResult;
cout << "[cout]strResult is: " << strResult << endl;
printf("[printf]strResult is: %s\n", strResult.c_str());
return 0;
}
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
stringstream sstream;
int first, second;
// 插入字符串
sstream << "456";
// 转换为int类型
sstream >> first;
cout << first << endl;
// 在进行多次类型转换前,必须先运行clear()
sstream.clear();
// 插入bool值
sstream << true;
// 转换为int类型
sstream >> second;
cout << second << endl;
return 0;
}
CString -> LPTSTR
CString::GetBuffer
LPTSTR GetBuffer( int nMinBufLength )
ReleaseBuffer();
返回值:一个指向对象的(以空字符结尾的)字符缓冲区的LPTSTR指针。
参数:nMinBufLength 字符缓冲区的以字符数表示的最小容量。这个值不包括一个结尾的空字符的空间。
及时调用ReleaseBuffer,在调用ReleaseBuffer前使用原cstring的其他方法可能产生错误,比如先修改了LPTSTR的内容,导致长度变化,在获取原cstring的长度,GetLength,返回的长度信息可能有误。
在之后操作LPTSTR,同样会影响cstring的内容。
上面的可能有内存泄漏的风险,可以参考如下方式
TCHAR* CString2TCHAR(CString &str)
{
int iLen = str.GetLength();
TCHAR* szRs = new TCHAR[iLen];
lstrcpy(szRs, str.GetBuffer(iLen));
str.ReleaseBuffer();
return szRs;
}
或者
CString str("Hockey is Best!");
BSTR bstr = str.AllocSysString();
wchar_t* temp0 = (LPWSTR)bstr4;
//使用完成以后:
SysFreeString(bstr);
多字节转换为宽字节,string->wstring
bool Utf8ToUnicode( std::string &utf8_string,std::wstring &unicode_string)
{
unicode_string = L"";
if (utf8_string.compare("") == 0 )
{
return false;
}
const char* temp_utf8_string = utf8_string.c_str();
int unicode_string_len = ::MultiByteToWideChar(CP_UTF8, NULL, temp_utf8_string, strlen(temp_utf8_string), NULL, 0);
if (0 == unicode_string_len )
{
return false;
}
wchar_t* temp_unicode_string = new wchar_t[unicode_string_len+1];
memset(temp_unicode_string,0,sizeof(wchar_t)*(unicode_string_len+1));
if (0 == ::MultiByteToWideChar(CP_UTF8, NULL, temp_utf8_string, strlen(temp_utf8_string),temp_unicode_string, unicode_string_len))
{
delete[] temp_unicode_string;
temp_unicode_string = NULL;
return false;
}
unicode_string = temp_unicode_string;
delete[] temp_unicode_string;
temp_unicode_string = NULL;
return true;
}
宽字节转换为多字节,wstring->string
bool UnicodeToUtf8( std::wstring &unicode_string,std::string &utf8_string)
{
utf8_string = "";
if (_wcsicmp(unicode_string.c_str(),L"") == 0 )
{
return false;
}
DWORD utf8_string_len = WideCharToMultiByte(CP_UTF8,NULL,unicode_string.c_str(),-1,NULL,0,NULL,FALSE);// WideCharToMultiByte的运用
if (0 == utf8_string_len)
{
return false;
}
char *temp_utf8_string = new char[utf8_string_len];
memset(temp_utf8_string,0,sizeof(char)*utf8_string_len);
if (0 == WideCharToMultiByte (CP_UTF8,NULL,unicode_string.c_str(),-1,temp_utf8_string,utf8_string_len,NULL,FALSE))
{
delete []temp_utf8_string;// aKmtempTagName的清除
temp_utf8_string = NULL;
return false;
}
utf8_string = (std::string)temp_utf8_string;
delete []temp_utf8_string;// aKmtempTagName的清除
temp_utf8_string = NULL;
return true;
}
CString->string
//CA2T含义
//C:convert,转换的意思
//A:ANSI字符串,也就是Muti-Byte
//2:to
//T:中间类型,如果定义了_UNICODE,则T表示W;如果定义了_MBCS,则T表示A
//W:宽字符串,也就是UNICODE
std::string name = "li";
CString c_name = CA2T(name.c_str()); //所以CA2T也就是CA2W就是将多字符集转换为宽字符UNICODE,也可写成CA2W
原文链接:https://blog.csdn.net/dycljj/article/details/110529484
但是有的机器还是会中文乱码,可以先将string转换为wstring,然后再赋值cstring
string2wstring(std::string str)
{
std::wstring result;
//获取缓冲区大小,并申请空间,缓冲区大小按字符计算
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
buffer[len] = '\0'; //添加字符串结尾
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
return result;
}
原文:https://www.cnblogs.com/babietongtianta/p/3143900.html
UTF-8到GB2312的转换
//UTF-8到GB2312的转换
char* U2G(const char* utf8)
{
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len+1];
memset(wstr, 0, len+1);
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len+1];
memset(str, 0, len+1);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
if(wstr) delete[] wstr;
return str;
}
GB2312到UTF-8的转换
//GB2312到UTF-8的转换
char* G2U(const char* gb2312)
{
int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len+1];
memset(wstr, 0, len+1);
MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len+1];
memset(str, 0, len+1);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
if(wstr) delete[] wstr;
return str;
}
函数
WaitForSingleObject
Windows提供了许多内核对象来实现线程的同步。对于线程同步而言,这些内核对象有两个非常重要的状态:“已通知”状态,“未通知”状态(也有翻译为:受信状态,未受信状态)。Windows提供了几种内核对象可以处于已通知状态和未通知状态:进程、线程、作业、文件、控制台输入/输出/错误流、事件、等待定时器、信号量、互斥对象(Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer
)。
你可以通知一个内核对象,使之处于“已通知状态”,然后让其他等待在该内核对象上的线程继续执行。你可以使用Windows提供的API函数,等待函数来等待某一个或某些内核对象变为已通知状态。
你可以使用WaitForSingleObject函数来等待一个内核对象变为已通知状态:
DWORD WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
该函数需要传递一个内核对象句柄,该句柄标识一个内核对象,如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0
。第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE
指明要无限期等待下去,如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。如果等待超时,该函数返回WAIT_TIMEOUT
。如果该函数失败,返回WAIT_FAILED
。
- WAIT_ABANDONED
0x00000080
:当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值。 - WAIT_OBJECT_0
0x00000000
:指定的对象出有信号状态。 - WAIT_TIMEOUT
0x0000010
:等待超时。 - WAIT_FAILED
0xFFFFFFFF
:出现错误,可通过GetLastError得到错误代码
原文:https://blog.csdn.net/u011555996/article/details/90604923
CreateEvent
CreateEvent 用来创建或打开一个命名的或无名的事件对象
HANDLE WINAPI CreateEvent(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
_In_ BOOL bManualReset, //设置信号复位方式为自动恢复为无信号状态(FALSE)还是手动恢复为无信号状态(TRUE)
_In_ BOOL bInitialState, //初始状态
_In_opt_ LPCTSTR lpName //信号名称,可以为Null
)
-
lpEventAttributes:[输入]一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
-
bManualReset: [输入]指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。
-
bInitialState: [输入]指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
-
lpName: [输入]指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
-
如果
lpName
指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS
来访问存在的对象。这时候,由于bManualReset
和bInitialState
参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes
是 参数不是NULL
,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。 -
如果
lpName
为NULL
,将创建一个无名的事件对象。 -
如果
lpName
和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError
函数中将返回ERROR_INVALID_HANDLE
。造成这种现象的原因是这些对象共享同一个命名空间。
返回值:
-
如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在
GetLastError
函数中返回ERROR_ALREADY_EXISTS
。 -
如果函数失败,函数返回值为
NULL
,如果需要获得详细的错误信息,需要调用GetLastError
。
原文链接:https://blog.csdn.net/u011394598/article/details/82981399
参考:https://blog.csdn.net/mpp_king/article/details/88999224
参考:https://www.baidu.com/link?url=Trf3FVCL-IrvLNW7HgnO2I1VUr21vF5ROIQ88zrxrPnsyF2abg7Si7eHNIlGatPU4mUl7xz123TDdViM7z_f8_&wd=&eqid=8e026f6d00084ffd00000003625fd315
//综上,一般创建方式如下,哈哈
HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
//复位方式为自动恢复到无信号状态,且初始状态为有信号.
DWORD dReturn = WaitForSingleObject(hEvent, 等待时间); //有信号状态,直接执行,不阻塞
// 即调用完该方法后,hEvent 就变为无信号状态,
// 需要调用setEvent使其为有信号状态,否则其他调用WaitForSingleObject阻塞等待的地方会一直卡住直到 设置等待的时间为止
SetEvent(hEvent);//设置为有信号状态(或者叫做发信号)
ResetEvent(hEvent);//设置为无信号状态(或者叫做不发信号)
例子:
#include <iostream>
#include<Windows.h>
#include<process.h>
#define THREADNUM 2
#define SINGLEFUN
unsigned int WINAPI ThreadFunc1(LPVOID pParam);
unsigned int WINAPI ThreadFunc2(LPVOID pParam);
HANDLE gHandle;
int count = 0;//全局变量
HANDLE threadHandle[THREADNUM];//线程句柄
typedef struct _INFO {
int index;
std::string desc;
}INFO;//传递给线程的参数
int main()
{
gHandle = CreateEvent(NULL, FALSE, TRUE, NULL);
INFO info[THREADNUM];
for (size_t i = 0; i < THREADNUM; i++)
{
info[i].index = i;
info[i].desc = "desc";
unsigned int threadID = i;
#ifdef SINGLEFUN
threadHandle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc2, info + i, 0, &threadID);
#else
if( i < 1 )
threadHandle[i] = (HANDLE)_beginthreadex(NULL,0, ThreadFunc1, info+i,0, &threadID);
else
threadHandle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc2, info + i, 0, &threadID);
#endif
}
std::cout << "Hello World!\n" << "begin\n";
int count = 20;
while (count) {
count--;
Sleep(1000);
}
//等待线程结束
for (size_t i = 0; i < THREADNUM; i++) {
DWORD res = WaitForSingleObject(threadHandle[i], INFINITE);
if (res == WAIT_OBJECT_0) {
std::cout << "ok " << i << std::endl;
}
else {
std::cout << "over " << i <<std::endl;
}
CloseHandle(threadHandle[i]);
}
std::cout << "the end" << std::endl;
system("PAUSE");
}
unsigned int WINAPI ThreadFunc1(LPVOID pParam)
{
INFO *info = (INFO*)pParam;
//DWORD ThreadID = ::GetCurrentThreadId();
while (count < 10)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(gHandle, INFINITE)) {
count++;
printf("ThreadFunc%d: -- %d\n",info->index, count);
SetEvent(gHandle);
}
Sleep(1000);
}
_endthreadex(info->index);
return 0;
}
unsigned int WINAPI ThreadFunc2(LPVOID pParam)
{
INFO *info = (INFO*)pParam;
//DWORD ThreadID = ::GetCurrentThreadId();
while (count < 20)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(gHandle, INFINITE)) {
count++;
printf("ThreadFunc%d: -- %d\n", info->index, count);
SetEvent(gHandle);
}
Sleep(1000);
}
_endthreadex(info->index);
return 0;
}
/*
unsigned long _beginthread(
void(_cdecl *start_address)(void *), //声明为void (*start_address)(void *)形式
unsigned stack_size, //是线程堆栈大小,一般默认为0
void *arglist //向线程传递的参数,一般为结构体
);
unsigned long _beginthreadex( //推荐使用
void *security, //安全属性,NULL表示默认安全性
unsigned stack_size, //是线程堆栈大小,一般默认为0
unsigned(_stdcall *start_address)(void *), //声明为unsigned(*start_address)(void *)形式
void *argilist, //向线程传递的参数,一般为结构体
unsigned initflag, //新线程的初始状态,0表示立即执行,CREATE_SUSPEND表示创建后挂起。
unsigned *thrdaddr //该变量存放线程标识符,它是CreateThread函数中的线程ID。
); //创建成功条件下的将线程句柄转化为unsigned long型返回,创建失败条件下返回0
*/
线程函数都为 ThreadFunc2
不同线程函数
AfxGetApp
CWinApp* AfxGetApp( )
获取指向当前应用程序对象的指针
HINSTANCE AfxGetInstanceHandle( )
返回当前exe实例的句柄,如果是dll的话返回dll的句柄
HINSTANCE GetModuleHandle(NULL)
返回当前exe实例的句柄,不过如果是在dll的话,返回的也是exe的实例句柄,想要返回dll的句柄就必须GetModuleHandle(dll的名字)。
使用
CWnd* pWnd = AfxGetApp()->GetMainWnd();//获取窗口
//窗口指针,句柄,ID的相关转换
指针->句柄: hWnd = pWnd->GetSafeHwnd();
ID->句柄: hWnd = ::GetDlgItem(hParentWnd, ID);
句柄->指针: pWnd = CWnd::FromHandle(hWnd);
ID->指针: pWnd = Cwnd::GetDlgItem();
指针->ID: ID = GetWindowLong(pWnd->GetSafeHwnd(), GWL_ID);
句柄->ID: ID = GetWindowLong(hWnd, GWL_ID);
GetModuleFileName
TCHAR szPath[ MAX_PATH ] = {0};
GetModuleFileName( NULL, szPath, MAX_PATH );
GetModuleFileName函数为windows的API函数,使用的时候需要包含windows.h的头文件;
MAX_PATH是一个宏定义,值为260。执行完GetModuleFileName函数之后,szPath数组中保存的就是执行程序当前的绝对路径。
PathRemoveFileSpec
将路径末尾的文件名和反斜杠去掉
//获取EXE文件自身所在的文件夹
TCHAR szPath[MAX_PATH];
//获取应用程序或者DLL的完整路径
::GetModuleFileName(NULL, szPath, MAX_PATH);
//去掉路径末尾的文件名和反斜杠
::PathRemoveFileSpec(szPath);
ShowWindow
BOOL ShowWindow( int nCmdShow );
如果窗口原来可见,则返回非零值;如果CWnd原来是隐藏的,则返回0
参数:
Parameters nCmdShow
指定了CWnd应如何被显示。
它必须是下列值之一:
SW_HIDE
隐藏窗口并将活动状态传递给其它窗口。
SW_MINIMIZE
最小化窗口并激活系统列表中的顶层窗口。
SW_RESTORE
激活并显示窗口。如果窗口是最小化或最大化的,Windows恢复其原来的大小和位置。
SW_SHOW
激活窗口并以其当前的大小和位置显示。
SW_SHOWMAXIMIZED
激活窗口并显示为最大化窗口。
SW_SHOWMINIMIZED
激活窗口并显示为图标。
SW_SHOWMINNOACTIVE
将窗口显示为图标。当前活动的窗口将保持活动状态。
SW_SHOWNA
按照当前状态显示窗口。当前活动的窗口将保持活动状态。
SW_SHOWNOACTIVATE
按窗口最近的大小和位置显示。当前活动的窗口将保持活动状态。
SW_SHOWNORMAL
激活并显示窗口。如果窗口是最小化或最大化的,则Windows恢复它原来的大小和位置。
这个函数设置窗口的可视状态。每个应用程序只应用CWinApp::m_nCmdShow为主窗口调用一次ShowWindow。以后调用ShowWindow应该用上面列出的值来代替CWinApp::m_nCmdShow指定的值。
MFC - 树形控件(tree-view control)
修改选中节点 高亮颜色。
void ClistDlg::OnNMCustomdrawTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
}
在控件的NM_CUSTOMDRAW
事件中,对于函数的第一个参数pNMHDR
进行转换,有很多其他控件也有此事件,其中转换的类型如下:
Control | Structure |
---|---|
List view | NMLVCUSTOMDRAW |
ToolTips | NMTTCUSTOMDRAW |
Tree | view NMTVCUSTOMDRAW |
All other supported controls | NMCUSTOMDRAW |
树控件对应的是NMTVCUSTOMDRAW
结构:
typedef struct tagNMTVCUSTOMDRAW {
NMCUSTOMDRAW nmcd;//一个包含控件各项信息的结构,如设备上下方句柄,项状态,大小。
COLORREF clrText;//如果项有文本,这个指明文本颜色
COLORREF clrTextBk;//文本背景色
}NMTVCUSTOMDRAW, *LPNMTVCUSTOMDRAW;
//NMCUSTOMDRAW结构定义:
typedef struct tagNMCUSTOMDRAWINFO {
NMHDR hdr;//这个结构变量跟OnCustomDraw函数的第一个参数是一样的,它们是一块内存的,也就是说
//(DWORD)&pTreeCtrl->nmcd.hdr和(DWORD)pNMHDR的值是相等的,转来转去又转回来了。
DWORD dwDrawStage;//绘画段,这个要着重了解,篇幅较大
HDC hdc;//控件的设备上下文句柄
RECT rc;//要绘制的区域,大小,一般是一个项的区域,如果是大循环则是整个控件的大小。
DWORD dwItemSpec;//项索引,依据控件而来(如CListCtrl),树控件不需要这个变量
UINT uItemState;//项的状态
LPARAM lItemlParam //项关联的数据,通过SetItemData函数设置的。
}NMCUSTOMDRAW, FAR* LPNMCUSTOMDRAW;
先来看看关于dwDrawState
在MSDN的取值:
The following table shows the global Drawstage values.
Value | Description |
---|---|
CDDS_POSTERASE | After the erasing cycle is complete. 擦除控件后 |
CDDS_POSTPAINT | After the painting cycle is complete. 绘制控件后 |
CDDS_PREERASE | Before the erasing cycle begins. 擦除控件前 |
CDDS_PREPAINT | Before the painting cycle begins. 绘制控件前 |
CDDS_ITEM Indicates | that the dwItemSpec, uItemState, and lItemParam members are valid. |
CDDS_ITEMPOSTERASE | After an item has been erased. 一个项被擦除后 |
CDDS_ITEMPOSTPAINT | After an item has been drawn. 一个项被绘制好后 |
CDDS_ITEMPREERASE | Before an item is erased. 一个项被擦除前 |
CDDS_ITEMPREPAINT | Before an item is drawn. 一个项被绘制前 |
LRESULT参数:该参数相当于函数的返回值函数执行以后系统可以根据该结果作出相应的操作。
函数执行过程:
该处理函数将控件的绘制分为两部分:擦除和绘画。Windows在每部分的开始和结束都会发送NM_CUSTOMDRAW消息。所以总共就有4个消息。但是 实际上你的程序所收到消息可能就只有1个或者多于四个,这取决于你想要让WINDOWS怎么做。每次发送消息的时段被称作为一个“绘画段”。你必须紧紧抓 住这个概念,因为它贯穿于整个“重绘”的过程。
所以,你将会在以下的时间点收到通知:
- 一个item被画之前——“绘画前”段
- 一个item被画之后——“绘画后”段
- 一个item被擦除之前——“擦除前”段
- 一个item被擦除之后——“擦除后”段
并不是所有的消息都是一样有用的,实际上,我们不需要处理所有的消息。
-
当
nmcd.dwDrawStage= CDDS_PREPAINT
时即绘画周期开始前:
CDRF_DODEFAULT: 返回该值之后该控件自己绘制,整个绘画周期内他不会发送任何其他的NM_CUSTOMDRAW
消息,
CDRF_NOTIFYITEMDRAW: 该返回值返回以后会通知父窗口相关项的绘画操作,并且在项的绘画开始前和开始后都会发送NM_CUSTOMDRAW
; -
当
nmcd.dwDrawStage=CDDS_ITEMPREPAINT
时即在该项绘画开始前
CDRF_NOTIFYSUBITEMDRAW: 在视图项被绘制之前,你的应用程序会收到一个NM_CUSTOMDRAW
消息,该消息中的dwDrawStage
为CDDS_ITEMPREPAINT | CDDS_SUBITEM
,这时你可以指定画笔和颜色对每个子项进行修改,否则返回CDRF_DODEFAULT
默认处理。
CDRF_NEWFONT: 你的应用程序使用指定的画笔,然后控件将会调用。
CDRF_SKIPDEFAULT: 应用程序绘制这个项,控件不绘制
例子:
void ClistDlg::OnNMCustomdrawTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMTVCUSTOMDRAW *pLVCD = (NMTVCUSTOMDRAW *)pNMHDR;
NMCUSTOMDRAW nmCustomDraw = pLVCD->nmcd;
*pResult |= CDRF_NOTIFYITEMDRAW;
switch (nmCustomDraw.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
{
if (nmCustomDraw.uItemState & CDIS_SELECTED)
pLVCD->clrTextBk = RGB(230, 0, 0);
}
default:
break;
}
}
参考https://blog.csdn.net/dengtangxie6311/article/details/101952053/?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-0–blog-4511500.pc_relevant_eslanding_v1&spm=1001.2101.3001.4242.1&utm_relevant_index=3
setitemstate
BOOL CTreeCtrl::SetItemState(int nItem, UINT uState, UINT uMask);
返回值:非0表示成功,0表示失败。
nItem:表示要被设置新状态的项的在列表控件中的索引值。
uState:表示要设置的新状态,如果传入0值,表示去除当前被设置项的,屏蔽位所标明的那几种状态,如果传入的与uMask(屏蔽位)的值相同,则表示要设置在uMask处所标明的那几种状态。
uMask:指明了那几种状态要设置,或要被去除。
例如:
ctlList.SetItemState(nItem,0,LVIS_SELECTED|LVIS_FOCUSED);
ctlList.SetItemState(nItem,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
参考:https://blog.csdn.net/junbincc02/article/details/53128369