多线程、互斥、进程、显示等

原文链接:笔记VC_xd_ladan的博客-CSDN博客

 

滚动条信息结构 LPSCROLLBARINFO 编译报错找不到时,在stdafx.h最前面加上#define WINVER 0x500


===============================================窗口==============================================
1 窗口的创建过程
1.1 定义入口函数(WinMain)
1.2 定义窗口的处理函数 (WindowProc)
1.3 注册窗口类 (RegisterClass)
1.4 创建窗口 (CreateWindow)
1.5 显示窗口 (ShowWindow、UpdateWindow)
1.6 消息循环 (GetMessage、TranslateMessage、DispatchMessage)
1.7 消息处理
RegisterClass/RegisterClassEx()
CreateWindow()/CreateWindowEx()
WNDCLASS/WNDCLASSEX //注册类的结构
GetClassInfo() // - 获取信息。
UnregisterClass() // - 卸载。
SetClassLong()/GetClassLong() //存入/读取类数据,该数据是所有窗口共享
SetWindowLong()/GetWindowLong() //存入/读取窗口属性数据,该数据是当前窗口独有


===============================================消息==============================================
MSG //消息结构
TranslateMessage() //将虚拟键消息转换为字符消息,字符消息被寄送到调用线程的消息队列。就是翻译按键消息,如果不是按键消息,就继续往下执行。
DispatchMessage() //派发一个消息给一个winodws窗口,通常由GetMessage()获得,消息被派发给回调函数(窗口处理函数),作用是传递给操作系统
WindowProc() //窗口处理函数
DefWindowProc() //缺省的窗口处理函数
CallWindowProc()
//当系统通知窗口时,会调用窗口处理函数同时,将消息ID和消息的参数传递给窗口处理函数。当窗口函数不处理的消息,使用缺省的窗口处理函数,例如:DefWindowProc


WM_QUIT //用于退出消息循环


GetMessage() //从系统获取消息,将消息从系统中移除,阻塞函数。当系统中无消息时,GetMessage会等候下一条消息。
PeekMessage() //以查看的方式从系统中获取消息,可以不将消息从系统中删除,非阻塞函数。当系统中无消息时,返回FALSE, 继续执行后续代码。
SendMessage //消息被执行后返回,一般发送非队列消息
PostMessage //消息发送后不论是否执行立即返回,发送队列消息
GetMessagePos() //消息产生时鼠标位置


ON_WM_CTLCOLOR //用来完成对EDIT, STATIC, BUTTON等控件设置背景和字体颜色
ON_WM_NCCALCSIZE //在需要计算窗口客户区的大小和位置时发送。通过处理这个消息,应用程序可以在窗口大小或位置改变时控制客户区的内容。(在改变编辑框文字输入位置时用过)

 


GetWindowLongPtr加上参数GWLP_WNDPROC,就可以获得窗口句柄所对应的窗口函数(窗口过程函数)指针,这一切在消息循环的 DispatchMessage(&msg)中完成


WM_SYSKEYDOWN: //ALT按键
EM_SETLIMITTEXT //编辑框字符限制
常数WM_KEYFIRST和WM_KEYLAST可作为过滤值取得所有键盘消息;
常数WM_MOUSEFIRST和WM_MOUSELAST可用来接收所有的鼠标消息。
SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMLEFT, MAKELPARAM(point.x, point.y)) //缩放消息


函数调用约定:
__stdcall :(标准调用方式)指明内存操作,函数的堆栈,在函数执行结束后,由函数自己负责回收。
__cdecl:在函数执行结束后,函数的堆栈,由函数的调用者来负责回收。
__pascal

 


回调函数:
typedef bool (WINAPI *fun)(int x);


void testcall(fun f)
{
f(10);
}


bool __stdcall CALLBACK funt(int x)
{
cout<<x<<endl;
getchar();
return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
testcall(funt);
return 0;
}
===============================================DLL==============================================
C++
导入: _declspec(dllimport) int Add(... ...); //隐式链接
导出: _declspec(dllexport) 导出函数


LoadLibrary() //显式链接
FreeLibrary() //卸载动态库
C
导入: extern "C" _declspec(dllimport) int Add(... ...);
导出: extern "C" _declspec(dllexport) int add(...);

 

常用结构:
UNDONAMEID //编辑框撤销、复制、粘贴
LVITEM //List Control内容结构

 


字符串(char string CString):
GetBuffer() //将CString转换为string
c_str() //string转换为char


计算字符串长度:
char s1[] = "中文ABC";
wchar_t s2[] = L"中文ABC";
sizeof s1: 8
sizeof s2: 12
strlen(s1): 7
wcslen(s2): 5
sa.GetLength(): 7
sw.GetLength(): 5
ss1.size(): 7
ss2.size(): 5
bs1.length(): 5
bs2.length(): 5


比如想查看shell32.dll文件被哪些进程调用:


C:\>tasklist /M shell32.dll

 

 


===============================================HTTP==============================================


===============================================Win32_API==============================================
文件夹和文件的相关操作:
UIUE设置模块类视图NS_CBB/DelFolderFiles
CreateFolder //创建文件夹
CreateDirectory //创建文件夹
FolderExist //文件夹是否存在
FileExist //文件是否存在
GetFileExt //获取文件扩张名
GetAbsolutePath //获取当前exe文件绝对路径
GetRelativePath //获取当前exe文件相对路径
GetModuleFileName //装载一个程序实例的句柄或返回该函数返回该当前应用程序全路径,可以获取自身程序名
GetFileTitle //和GetModuleFileName 搭配可以得到文件名
GetCurrentDirectory //获取当前exe路径
DeleteFile //删除文件
RemoveDirectory //删除文件夹
DeleteFolder //删除文件夹
CreateFileShortcut //创建快捷方式
FindFirstFile //查找文件
FindNextFile //查找下一个文件
FindClose // 关闭文件句柄
GetPrivateProfileString //从文件中取字符串,一般处理config.ini
CopyFile() //拷贝文件
INVALID_HANDLE_VALUE //FindFirstFile未找到文件
WIN32_FIND_DATA //文件全部属性信息结构,使用FindFirstFile进行取值


AfxGetApp()->m_pszExeName //本程序名


求随机数:
srand((time_t)time(NULL));
rand();


打开网页:
ShellExecute() //运行一个外部程序(打开一个已注册的文件、打开一个目录、打印文件等等),并对外部程序有一定的控制


AfxGetApp()->m_hInstance // 获得应用程序当前实例句柄


//隐藏控制台
#pragma comment (linker, "/ENTRY:mainCRTStartup")
#pragma comment (linker, "/subsystem:windows")


获取系统路径(CSIDL):
SHGetSpecialFolderLocation() //
SHGetSpecialFolderPath() //
SHGetPathFromIDList()
CSIDL_FLAG_CREATE
CSIDL_ADMINTOOLS
CSIDL_ALTSTARTUP
CSIDL_APPDATA
CSIDL_BITBUCKET
CSIDL_COMMON_ADMINTOOLS
CSIDL_COMMON_ALTSTARTUP
CSIDL_COMMON_APPDATA
CSIDL_COMMON_DESKTOPDIRECTORY
CSIDL_COMMON_DOCUMENTS
CSIDL_COMMON_FAVORITES
CSIDL_COMMON_PROGRAMS
CSIDL_COMMON_STARTMENU //开始菜单
CSIDL_COMMON_STARTUP
CSIDL_COMMON_TEMPLATES //临时文件夹
CSIDL_CONTROLS
CSIDL_COOKIES
CSIDL_DESKTOP
CSIDL_DESKTOPDIRECTORY
CSIDL_DRIVES
CSIDL_FAVORITES
CSIDL_FONTS
CSIDL_HISTORY
CSIDL_INTERNET
CSIDL_INTERNET_CACHE
CSIDL_LOCAL_APPDATA
CSIDL_MYPICTURES
CSIDL_NETHOOD
CSIDL_NETWORK
CSIDL_PERSONAL
CSIDL_PRINTERS
CSIDL_PRINTHOOD
CSIDL_PROFILE
CSIDL_PROGRAM_FILES
CSIDL_PROGRAM_FILES_COMMON
CSIDL_PROGRAMS
CSIDL_RECENT
CSIDL_SENDTO
CSIDL_STARTMENU
CSIDL_STARTUP
CSIDL_SYSTEM
CSIDL_TEMPLATES
CSIDL_WINDOWS


TRACE() //打印输出:
FAST_TRACE()


配置文件:
GetPrivateProfileString() //从文件中读值


WritePrivateProfileString() //写入配置文件


原来iostream是C++的头文件,iostream.h是C的头文件

 


//判断是否有admin权限,vs2008以上才能运行
BOOL IsRunAsAdmin()
{
BOOL fIsRunAsAdmin = FALSE;
DWORD dwError = ERROR_SUCCESS;
PSID pAdministratorsGroup = NULL;


// Allocate and initialize a SID of the administrators group.
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdministratorsGroup))
{
dwError = GetLastError();
goto Cleanup;
}


// Determine whether the SID of administrators group is enabled in
// the primary access token of the process.
if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
{
dwError = GetLastError();
goto Cleanup;
}


Cleanup:
// Centralized cleanup for all allocated resources.
if (pAdministratorsGroup)
{
FreeSid(pAdministratorsGroup);
pAdministratorsGroup = NULL;
}


// Throw the error if something failed in the function.
if (ERROR_SUCCESS != dwError)
{
throw dwError;
}


return fIsRunAsAdmin;
}


===============================================线程==============================================
创建线程:
_beginthread, _beginthreadex ,_endthread //获取线程句柄:对返回值强转(HANDLE)
CreateThread //win32创建线程 不推荐用,容易内存泄露
AfxBeginThread //用户界面线程和工作者线程,MFC可重载函数
GetExitCodeThread //获得线程退出码
SetThreadPriority //为线程指定优先级
ResumeThread //激活、恢复线程运行


多线程:
临界区:
InitializeCriticalSection //初始化临界区
EnterCriticalSection //进入临界区
LeaveCriticalSection //离开临界区
DeleteCriticalSection //删除临界区
事件:
CreateEvent //创建事件
SetEvent //将事件置为有信号状态
ResetEvent //将事件置为无信号状态
原子锁:
互斥Mutex:
CreateMutex //创建互斥
ReleaseMutex //释放互斥
信号量:
CreateSemphore //创建信号量
ReleaseSemphore //释放信号量
可等候定时器:


WaitForSingleObject //等候函数
WaitForMultipleObject //等候函数
CloseHandle //关闭线程句柄
临界区 - 用户态,执行效率高。只能在一个进程内的多个线程使用。
互斥 - 内核态,执行效率低 可以通过命名的方式的进程使用。
原子锁 - 单条指令。
临界区 - 单条或多条指令。
多线程内部一般使用临界区,多进程之间使用互斥。


若要终止线程的运行,可以使用下面的方法:
  · 线程函数返回(最好使用这种方法)。
  · 通过调用ExitThread函数,线程将自行撤消(最好不要使用这种方法)。
  · 同一个进程或另一个进程中的线程调用TerminateThread函数(应该避免使用这种方法)。
  · 包含线程的进程终止运行(应该避免使用这种方法)。

 

 

 

 

 


===============================================进程==============================================
CreateProcess() //创建进程
ExitProcess() //强制执行本进程的退出
TerminateProcess() //在一个进程中强制结束其他进程
GetCurrentProcess() //获取到本进程句柄
WaitForSingleObject() //TerminateProcess()是异步执行的,在调用返回后并不能确定被终止进程是否已经真的退出,可以通过WaitForSingleObject()来等待进程的真正结束。
GetWindowThreadProcessId() //获取进程ID号
OpenProcess() //打开进程
GetModuleHandle() //根据模块名获得进程句柄
GetModuleHandleEx() //
GetExitCodeProcess //获得子进程的退出代码


创建子进城后,一般都会将子进程分离出去,分离的方法:
CloseHandle(pi.hThread); //主线程内核对象使用计数减1
CloseHandle(pi.hProcess); //子进程内核对象使用计数减1


进程可以通过以下4种方式终止:
主线程的入口函数返回 (强烈推荐的方式)。
进程中的一个线程调用ExitProcess函数 (要避免这个方式)
另一个进程中的线程调用TerminateProcess函数 (要避免这个方式)
进程中的所有线程都“自然死亡” (这是很难发生的)


在VC程序中如何结束系统正在运行的其他进程(该进程必须有窗口界面),其实很简单,按如下步骤进行即可:
1.取得进程的句柄(利用FindWindow函数得到);
2.获取进程ID号(用GetWindowThreadProcessId函数获取);
3.打开进程,OpenProcess函数中的第一个参数设为PROCESS_TERMINATE,就可以获取处理该进程的句柄;
4.利用TerminateProcess函数结束进程,将该函数的第二个参数设为4。
代码如下:
//结束进程
int CStaticFunc::KillProcess(LPCSTR pszClassName, LPCSTR
pszWindowTitle)
{
HANDLE hProcessHandle;
ULONG nProcessID;
HWND TheWindow;
TheWindow = ::FindWindow( NULL, pszWindowTitle );
::GetWindowThreadProcessId( TheWindow, &nProcessID );
hProcessHandle = ::OpenProcess( PROCESS_TERMINATE, FALSE,
nProcessID );
return ::TerminateProcess( hProcessHandle, 4 );
}
而启动进程则只需要CreateProcess函数就可完成,需要注意的是这个函数的几个输入参数,第一个参数是
//启动新进程
int CStaticFunc::CreateNewProcess(LPCSTR pszExeName)
{
PROCESS_INFORMATION piProcInfoGPS;
STARTUPINFO siStartupInfo;
SECURITY_ATTRIBUTES saProcess, saThread;
ZeroMemory( &siStartupInfo, sizeof(siStartupInfo) );
siStartupInfo.cb = sizeof(siStartupInfo);
saProcess.nLength = sizeof(saProcess);
saProcess.lpSecurityDescriptor = NULL;
saProcess.bInheritHandle = true;
saThread.nLength = sizeof(saThread);
saThread.lpSecurityDescriptor = NULL;
saThread.bInheritHandle = true;
return ::CreateProcess( NULL, (LPTSTR)pszExeName, &saProcess,
&saThread, false,
CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&siStartupInfo, &piProcInfoGPS );

 

 

 


进程间通信:
http://wenku.baidu.com/view/b0db919b51e79b89680226aa.html
http://www.cnblogs.com/mydomain/archive/2010/09/23/1833369.html

 


1.文件映射(Memory-Mapped Files)
2.共享内存(Shared Memory)
3.匿名管道(Pipe)
4.命名管道(Named Pipe)
5.邮件糟(Mailslots)
6.剪贴板(Clipped Board)
7.动态数据交换(DDE)
8.对象链接与嵌入(OLE)
9.动态链接库(Dll)
10.远程过程调用(RPC)
11.NetBios函数
12.Sockets
13.WM_COPYDATA
14.信号量(semophore)
15.消息队列(不知道和13有没有联系,或者是一样的)
16.信号(sinal)

 

 

 


混音器设备:
mixerGetNumDevs() //获取系统中的混音器设备的数量
mixerOpen() //打开指定的混音设备获取句柄并且确保它不能被删除直到应用程序关闭了句柄
mixerGetDevCaps() //查询指定的混音设备来确定性能
mixerGetLineInfo //获得一条指定的音频线的数据
mixerGetLineControls //获得一个或多个音频线的控制
mixerGetControlDetails //获得一条音频线的单一控制的详细数据
HMIXER //混音器句柄
MIXERCAPS //一个结构,描述了混音器的功能的选项
DECLARE_HANDLE(HMIXER) //http://www.cnblogs.com/BeyondTechnology/archive/2010/08/29/1811915.html

 

 


===============================================作业===============================================
CreateJobObject() //创建作业对象

 

 


===============================================注册表==============================================
HKEY hKey
::RegCreateKey //创建项
::RegOpenKeyEx //打开
::RegSetValueEx //设置值和数据
::RegQueryValueEx
::RegCloseKey //关闭注册表句柄
::RegDeleteValue //删除


HKEY_CLASSES_ROOT\Unknown\shell\openas\command //打开方式提示框位置

 


===============================================网络抓包==============================================
tcp contains version


ip.addr ==

 

 


设置鼠标手型:
PtInRect(POINT pt) //判断point是否在rect区域
GetCursorPos() //获取鼠标坐标
SetCursor(LoadCursor( NULL, MAKEINTRESOURCE(32649) ) ) //设置手型


CToolTipCtrl //提示框类


SetForegroundWindow() //该函数将创建指定窗口的线程设置到前台,并且激活该窗口。(主窗口最小化时创建了子窗口,主窗口无法最大化问题)


==============================================WinDbg=================================================


抓dump文件命令: .dump /ma 路劲\filename.dmp


设WinDbg为默认调试器:
\\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
添加或修改Auto键,类型设为REG_SZ,数值为0. //如果值为1,则崩溃口自动弹出WinDbg.
添加或修改Debugger键,类型设为REG_SZ,数值为""C:/ProgramFiles\Debugging Tools for Windows\cdb.exe" -p %ld -e %ld -g".
添加或修改PreVisualStudio7Debugger键,类型设为REG_SZ,数值为"drwtsn32 -p %ld -e %ld -g".

 


==============================================linux=================================================
find / -name "要找的文件名"

 


===============================================inno脚本==============================================


{group} 开始程序中程序的文件夹
{userappdata}\Microsoft\Internet Explorer\Quick Launch\ 快速启动目录
{usertemplates}
ShellExec('open', 'http://www.google.com', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);//打开网页


//win7超级任务栏
[Icons]
Name: "{userdesktop}\D8Test"; Filename: "{win}\notepad.exe"


[code]
var
ErrorCode: Integer;

procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep=ssDone then
ShellExec('taskbarpin', ExpandConstant('{userdesktop}\D8Test.lnk'), '', '', SW_SHOWNORMAL, ewNoWait,ErrorCode);
end;


procedure DeinitializeUninstall();
begin
ShellExec('taskbarunpin', ExpandConstant('{userdesktop}\D8Test.lnk'), '', '', SW_SHOWNORMAL, ewNoWait,ErrorCode);
end;

 

// 多显示器的相关问题 引用《Programming for Multiple Monitors in Windows 98》的原文说明

1.数据类型
HMONITOR 显示器的句柄
MONITORINFO 显示器信息
MONITORINFOEX 显示器信息(上面结构的扩展)
typedef struct tagMONITORINFO
{
DWORD cbSize;
RECT rcMonitor;
RECT rcWork;
DWORD dwFlags;
} MONITORINFO, *LPMONITORINFO;
typedef struct tagMONITORINFOEXA
{
MONITORINFO;
TCHAR szDevice[CCHDEVICENAME];
} MONITORINFOEX, *LPMONITORINFOEX;

2.获得某点所在的屏幕
HMONITOR MonitorFromPoint(POINT pt,DWORD dwFlags);
Pt为点坐标
dwFlags可取下面的值,表示没有任何显示器包含该点时,返回什么值MONITOR_DEFAULTTONULL(返回NULL)
MONITOR_DEFAULTTOPRIMARY(返回主屏)
MONITOR_DEFAULTTONEAREST(返回最靠近该目标的屏幕)
3.获取某矩形区域,窗口所在屏幕
HMONITOR MonitorFromRect(LPRECT lprc,DWORD dwFlags);
lprc为指定矩形区域
dwFlags含义与2相同

HMONITOR MonitorFromWindow(HWND hWnd,DWORD dwFlags);
hWnd为指定窗口
dwFlags含义同2
这两个接口返回包含目标最多的屏幕,如果没有任何屏幕包含目标,那么根据第二个参数返回具体的值。
4.获取显示器信息
BOOL GetMonitorInfo( HMONITOR hmonitor, LPMONITORINFO lpmi);
hmonitor 显示器句柄
lpmi MONITORINFO或MONITORINFOEX结构指针

注意MONITORINFOEX是MONITORINFO的超集,作为参数时需先设置它的cbSize属性,使它等于自身的大小。

可以这样使用
MONITORINFOEX mix;
mix.cbSize = sizeof(mix);
GetMonitorInfo(hMonitor, (LPMONITORINFO)&mix);
5.获取显示器的宽和高
GetSystemMetrics(int nIndex);
使用SM_CXSCREEN,SM_CYSCREEN可以获取主屏大小(800, 600)
如果要获得虚拟桌面的大小,需要使用SM_CXVIRTUALSCREEN,SM_CYVIRTUALSCREEN,此时获取的大小为(1824, 768)
6.获取工作区大小
BOOL SystemParametersInfo(
UINT uiAction, // system parameter to retrieve or set
UINT uiParam, // depends on action to be taken
PVOID pvParam, // depends on action to be taken
UINT fWinIni // user profile update option
);

上 面函数对使用uiAction为SPI_GETWORKAREA,SPI_SETWORKAREA时,做了改进,以前总是对主屏进行操作。 现在要获取其他屏幕的属性,可以使用GetMonitorInfo,要设置其他屏幕可以在uiAction为SPI_SETWORKAREA时,将 RECT作为pvParam,这样就可以设置包含该RECT的显示器。
7.便利相关显示器
BOOL WINAPI EnumDisplayMonitors(
HDC hdc,
LPCRECT lprcClip,
MONITORENUMPROC lpfnEnum,
LPARAM dwData);

hdc 设备环境
lprcClip 矩形区域
lpfnEnum 回调函数
dwData 传给回调函数的参数

回调函数需要有下面的形式
BOOL CALLBACK MonitorEnumProc(
HMONITOR hmonitor,
HDC hdcMonitor,
LPRC lprcMonitor,
DWORD dwData);

 

8.枚举显示器设备
BOOL WINAPI EnumDisplayDevices(
PVOID Unused, //Reversed, Set to NULL
DWORD iDevNum, //index of monitor to query information
PDISPLAY_DEVICE lpDisplayDevice, //output information
DWORD dwFlags); //set to 0
其中iDevNum是指查询设备的索引,以0为基数,如果有该设备存在则填充lpDisplayDevice,返回TRUE;如果没有该设备,则返回FALSE。如果要枚举所有的设备,那么需要让iDevNum从0开始一直调用该函数,知道返回FALSE为止。
第三个参数的结构定义如下:
typedef struct _DISPLAY_DEVICE {
DWORD cb;
BYTE DeviceName[32]; //name of the device. Same as value get by GetMonitorInfo
BYTE DeviceString[128]; //Firendly name of the device
DWORD StateFlags; //不详
} DISPLAY_DEVICE, *PDISPLAY_DEVICE,
*LPDISPLAY_DEVICE;
StateFlags参数含义不详,但是可以通过它的取值看出些汉以来:
#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
0x00000001
#define DISPLAY_DEVICE_MULTI_DRIVER
0x00000002
#define DISPLAY_DEVICE_PRIMARY_DEVICE
0x00000004
#define DISPLAY_DEVICE_MIRRORING_DRIVER
0x00000008
#define DISPLAY_DEVICE_VGA_COMPATIBLE
0x00000010

引用《Programming for Multiple Monitors in Windows 98》的原文说明

GUI编程注意事项
GUI编程使用了显示器,此时如果不考虑 多显示器情况,可能出现显示问题。例如用户当前将任务条拖放到副屏上,在副屏上进行操作,运行程序后程序出现在主屏上,这会让用户感觉程序没有运行,但这 并不是主要问题。 当用户将主屏上的窗口拖放到副屏后,点击某个按钮或菜单,此时程序进行了动态的位置调整,坐标设想为(0,0),那么窗口又从副屏跑回主屏了,此时我想用 户会觉得恼火。 具体的做法可以在窗口创建的时候和窗口移动的时候,获取窗口所处屏幕信息,并记录下当前显示屏的原点。例如:
CPOINT m_ ptOrg; //在.h文件中声明变量

//在WM_MOVE或者窗口初始化时调用, 保存原点信息
HMONITOR hWndMonitor = MonitorFromWindow(GetSafeHwnd(), MONITOR_DEFAULTTONULL);
MONITORINFOEX monitorInfo;
if (hWndMonitor == NULL) return;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hWndMonitor, &monitorInfo);

m_ptOrg.x = monitorInfo.left
m_ptOrg.y = monitorInfo.top

当调用SetWindowPos时,需要注意此时传递的RECT的原点应该以哪个计算。


多窗口的一些有趣的事情
一般第二个窗口的原点横坐标是第一个窗口的宽,任务栏和桌面显示在主屏上。默认情况下,副屏在主屏的右边,鼠标可以向左移动出主屏外。并且可以将任务栏拖到副屏中。

多窗口时,Control Panel会记住上次的位置,下次打开的时候还在原来的屏幕上出现。例如打开Control Panel,此时显示在主屏上,如果将它移动到副屏上,然后关闭,再次打开的时候会出现在副屏上。

 


__FUNCTION__: //函数名(string)%s
————————————————
版权声明:本文为CSDN博主「xd_ladan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xd_ladan/article/details/8499694

posted @ 2023-02-03 11:52  IceArrow  阅读(97)  评论(0编辑  收藏  举报