上一篇《Windows界面编程第三篇 异形窗体 普通版》介绍了异形窗口(异形窗体)的创建,其主要步骤为——先通过创建位图画刷来做窗口的背景画刷,再通过SetWindowLong为窗体加上WS_EX_LAYERED属性,然后使用SetLayeredWindowAttributes指定窗口的透明色来完成窗口形状的调整。并且为了使异形窗口支持鼠标的拖曳,在WM_LBUTTONDOWN消息中作了特殊处理。
然后在下图中有非常相似的两个异形窗体,只不过,左边的异形窗体小,右边的异形窗体大。这个可以怎么实现了?
先通过其它软件来缩放位图,然后再让程序加载这种方式来指定异形窗口的大小。这种方法虽然可以完成任务,但毕竟太OUT了。
由《Windows界面编程第一篇位图背景与位图画刷》可以想到不用位图画刷,而直接在窗口背景绘制时使用StretchBlt来缩放位图至窗口大小,这样就可以达到指定窗口大小的功能。
由于异形窗口运行后无法通过鼠标来动态调整窗口大小,因此可以窗口初始化时就可以先缩放位图并加载到一个缓冲HDC中,然后再在窗口背景绘制时使用BitBlt来贴图。这种做法只需要缩放位图一次,在每次背景绘制时只须拷贝位图,对程序的效率会有提高。下面给出完整源代码(下载地址:http://download.csdn.net/download/morewindows/4966819)
// 异形窗口2 在WM_ERASEBKGND消息中自贴图 //By MoreWindows-(http://blog.csdn.net/MoreWindows) #include <windows.h> const char szAppName[] = "异形窗口2 MoreWindows-(http://blog.csdn.net/MoreWindows)"; /* * 函数名称: GetWindowSize * 函数功能: 得到窗口的宽高 * hwnd 窗口句柄 * pnWidth 窗口宽 * pnHeight 窗口高 */ void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight); /* * 函数名称: InitBitmapWindow * 函数功能: 位图窗口初始化 * hinstance 进程实例 * nWidth 窗口宽 * nHeight 窗口高 * nCmdshow 显示方式-与ShowWindow函数的第二个参数相同 */ BOOL InitBitmapWindow(HINSTANCE hinstance, int nWidth, int nHeight, int nCmdshow); // 位图窗口消息处理函数 LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm); HBITMAP g_hBitmap; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //先创建一个无背影画刷窗口, //然后在WM_CREATE中并指定透明颜色, 缩放位图后加载至s_hdcMem中. //最后在WM_ERASEBKGND中用s_hdcMem贴图即可 g_hBitmap = (HBITMAP)LoadImage(NULL, "Kitty.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); if (g_hBitmap == NULL) { MessageBox(NULL, "位图加载失败", "Error", MB_ICONERROR); return 0; } // 设置异形窗口大小 BITMAP bm; GetObject(g_hBitmap, sizeof(bm), &bm); int nWindowWidth = bm.bmWidth; int nWindowHeight = bm.bmHeight + 100; //拉高100高度 if (!InitBitmapWindow(hInstance, nWindowWidth, nWindowHeight, nCmdShow)) return 0; MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } DeleteObject(g_hBitmap); return msg.wParam; } BOOL InitBitmapWindow(HINSTANCE hinstance, int nWidth, int nHeight, int nCmdshow) { HWND hwnd; WNDCLASS wndclass; wndclass.style = CS_VREDRAW | CS_HREDRAW; wndclass.lpfnWndProc = BitmapWindowWndPrco; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hinstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);//窗口背影画刷为空 wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, "Program Need Windows NT!", "Error", MB_ICONERROR); return FALSE; } hwnd = CreateWindowEx(WS_EX_TOPMOST, szAppName, szAppName, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, nWidth, nHeight, NULL, NULL, hinstance, NULL); if (hwnd == NULL) return FALSE; ShowWindow(hwnd, nCmdshow); UpdateWindow(hwnd); return TRUE; } LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm) { static HDC s_hdcMem; //放置缩放后的位图 switch (message) { case WM_CREATE: { // 设置分层属性 SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); // 设置透明色 COLORREF clTransparent = RGB(0, 0, 0); SetLayeredWindowAttributes(hwnd, clTransparent, 0, LWA_COLORKEY); // 缩放位图 // 加载位图到hdcTemp中 HDC hdc = GetDC(hwnd); HDC hdcTemp = CreateCompatibleDC(hdc); SelectObject(hdcTemp, g_hBitmap); // 得到窗口大小 int nWidth, nHeight; GetWindowSize(hwnd, &nWidth, &nHeight); // 创建与窗口大小相等且能容纳位图的HDC - s_hdcMem s_hdcMem = CreateCompatibleDC(hdc); HBITMAP hbmp = CreateCompatibleBitmap(hdc, nWidth, nHeight); SelectObject(s_hdcMem, hbmp); // 将原位图缩放到窗口大小 BITMAP bm; GetObject(g_hBitmap, sizeof(bm), &bm); StretchBlt(s_hdcMem, 0, 0, nWidth, nHeight, hdcTemp, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); // 释放资源 DeleteDC(hdcTemp); ReleaseDC(hwnd, hdc); } return 0; case WM_KEYDOWN: switch (wParam) { case VK_ESCAPE: //按下Esc键时退出 SendMessage(hwnd, WM_DESTROY, 0, 0); return TRUE; } break; case WM_LBUTTONDOWN: //当鼠标左键点击时可以拖曳窗口 PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0); return TRUE; case WM_ERASEBKGND: //在窗口背景中直接贴图 { HDC hdc = (HDC)wParam; int nWidth, nHeight; GetWindowSize(hwnd, &nWidth, &nHeight); BitBlt(hdc, 0, 0, nWidth, nHeight, s_hdcMem, 0, 0, SRCCOPY); return TRUE; } case WM_DESTROY: DeleteDC(s_hdcMem); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParm); } void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight) { RECT rc; GetWindowRect(hwnd, &rc); *pnWidth = rc.right - rc.left; *pnHeight = rc.bottom - rc.top; }
运行程序将得到如文章中每一张图右边所示的异形窗口。
最后总结一下异形窗口的“三要素”:
1.WS_EX_LAYERED属性
2.以位图为窗口背景(自贴图或位图画刷)
3.指定透明色
本文配套程序下载地址为:http://download.csdn.net/download/morewindows/4966819
转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8451638
欢迎关注微博:http://weibo.com/MoreWindows
由于最近需要研究JVM,所以打算先自己build一个openjdk试试,hllvm群组里主要提供的都是基于Xubuntu环境,没有RedHat系的,与我的需求不尽相符,只好自己摸索,好在有RednaxelaFX等大神提供的诸多资料,确实提供了许多帮助,下述如有错误纰漏还望各位大神指正。
废话不多说,环境选择了Fedora 16 (64-bit),OpenJDK的版本是openjdk-7-fcs-src-b147-27_jun_2011.zip,源码下载地址http://download.java.net/openjdk/jdk7/
从一个干净的Fedora 16开始build需要不少准备工作,这里强烈建议直接查看OpenJDK Build README,网上有些帖子说的乱七八糟的而且来源还都是同一篇,十分误事,我接下来就把我的经验share一下,操作基本来自于这篇build README,我对linux不是很熟,过程中还是废了不少事儿,另外下面所有的都是在root权限下操作的。
- 首先当然必须有最基本的GNU make,最低版本要求是3.81以上,Fedora 16自带的是3.82的版本,所以这个无需操心。
- 我在安装fedora的时候,选择了安装Java开发工具,因此系统默认给我装好了openjdk 1.6.0_24版本,如果没有的话用yum-builddep java-1.6.0-openjdk,用网上帖子的话说:“这是安装一个 jdk 6 在 /usr/lib/jvm/java-openjdk 这个位置。这个JDK 就是所谓的bootstrap jdk了. 因为在编译openjdk7 的时候java 部分的代码就是这个来编译的了。”
- 修改 /root/openjdk/hotspot/make/linux下的Makefile文件,注释掉以下内容
check_os_version: #ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),) # $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1; #endif
这一部分我是在最开始的时候就按照这里说的把这几行给注释掉了,没有亲测,所以也不知道不注释的话到底会不会发生问题。
- 设置环境变量,进入你下载的openjdk7解压后的文件夹,在shell里输入. jdk/make/jdk_generic_profile.sh,这个是你的bash/sh/ksh的setup文件,执行这条命令之后那些LANG、ALT_BOOTDIR等环境变量都会为你设置好,就不用再自己一条一条地export了。
- 接下来按照Basic Linux System Setup部分给出的Basic Linux Check List
Basic Linux Check List 1. Install the Bootstrap JDK, set ALT_BOOTDIR. 2. Optional Import JDK, set ALT_JDK_IMPORT_PATH. 3. Install or upgrade the FreeType development package. 4. Install Ant 1.7.1 or newer, make sure it is in your PATH.
第一条是指构建openjdk的工作需要一个预先安装好的JDK环境用来做引导,由于我们已经的系统里已经装好了openjdk 6,而且环境变量已设置好,所以就不用管了。
第二条里的ALT_JDK_IMPORT_PATH环境变量是在你不需要build整个JDK的时候用到,比如你之前编过整个JDK,这次你不想再build Hotspot虚拟机了,那么你就可以通过这个来设置。
第三条是要求安装FreeType,README里提供的是源码安装的方法,但是通过yum提供了FreeType软件包,所以用yum安装会更方便一些,安装命令:yum install freetype-devel.x86_64
第四条是要求安装Apache Ant,README给的方法是下载源码安装,具体的可以到Ant的官网http://ant.apache.org/去看,源码和安装方法都能找到,我由于之前安装系统是勾选了Java开发那栏,因此Ant貌似已经有了,我没有再手动安装。
- 弄完了这些你以为就已经完成了吗?No,太naïve了!我以为这些就是Linux Fedora需要的全部dependency了,于是进入到openjdk目录下,执行命令make sanity检查build dependency,结果报出的错误是少了ALSA、CUPS的一些头文件。我擦,CUPS不是明明列在了Solaris的Basic Check List下吗!好吧,那就一个个解决吧。
前面一篇文章线程同步之信号量同步 讲的是线程之间的信号量,这篇讲的更加具有通用性,能够实现进程之间的同步。
它是一个特殊变量,只允许对它进行等待和发送信号这两种操作。
- P(信号量变量):等待
- V(信号量变量):发送信号
semaphore sem_id = 1; loop{ P(sem_id); 临界区代码; V(sem_id); 非临界区代码; }
信号量机制及相关接口
Linux系统中的信号量接口经过了精心设计,提供了比通常所需更多的机制。所有的Linux信号量函数都是针对成组的通用信号量进行操作,而不只是针对一个二进制信号量。但是在绝大多数情况下,使用一个单个信号量就足够了,所以在这里只讨论单个信号量的使用。
用于创建一个新的信号量或者是取得一个已有的信号量的键。
所需包含的头文件:
#include <sys/sem.h>它通常依赖于另两个头文件:
#include <sys/types.h> #include <sys/ipc.h>一般情况下,这两个头文件都会被它自动包含。
功能描述
函数原型:int semget(key_t key,int nsems,int semflg);
功能描述
获取与某个键关联的信号量集标识。信号量集被建立的情况有两种:
1.如果键的值是IPC_PRIVATE。
2.或者键的值不是IPC_PRIVATE,并且键所对应的信号量集不存在,同时标志中指定IPC_CREAT。
当调用semget创建一个信号量时,他的相应的semid_ds结构被初始化。ipc_perm中各个量被设置为相应
值:
sem_nsems被设置为nsems所示的值;
sem_otime被设置为0;
sem_ctime被设置为当前时间
参数解释:
key:所创建或打开信号量集的键值。需要是唯一的非零整数。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。几乎总是取值为1.
flag:调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过or表示
返回值说明:
如果成功,则返回信号量集的IPC标识符(一个正数)。
如果失败,则返回-1,errno被设定成以下的某个值
EACCES:没有访问该信号量集的权限
EEXIST:信号量集已经存在,无法创建
EINVAL:参数nsems的值小于0或者大于该信号量集的限制;或者是该key关联的信号量集已存在,并且nsems
大于该信号量集的信号量数
ENOENT:信号量集不存在,同时没有使用IPC_CREAT
ENOMEM :没有足够的内存创建新的信号量集
ENOSPC:超出系统限制
用于改变信号量的值。
#include <sys/sem.h> int semop( int semid, struct sembuf semoparray[], size_t nops );
参数解释:
参数semid是一个通过semget函数返回的一个信号量标识符
参数nops标明了参数semoparray所指向数组中的元素个数
参数semoparray是一个struct sembuf结构类型的数组指针,
结构sembuf来说明所要执行的操作,其定义如下:
struct sembuf{ unsigned short sem_num; short sem_op; short sem_flg; }
在sembuf结构中,
sem_num是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。除非使用一组讯号了,否则它的取值一般为0.
sem_op的值是一个整数,是信号量在一次操作中需要改变的数值(可以是非1的数值)。通常只会用到两个值:1----P操作,-1---V操作。
sem_flg说明函数semop的行为。通常被设置为SEM_UNDO。它将使得操作系统跟着当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。
注意:
semop调用的一切动作都是一次性完成的,这是为了避免出现因使用多个信号量而可能发生的竞争现象。
原型:
int semctl(int semid,int semnum,int cmd,union semun);
返回值:
如果成功,则为一个正数。
如果失败,则为-1:
errno=EACCESS(权限不够)
EFAULT(arg指向的地址无效)
EIDRM(信号量集已经删除)
EINVAL(信号量集不存在,或者semid无效)
EPERM(EUID没有cmd的权利)
ERANGE(信号量值超出范围)
参数解释:
sem_id是由semget返回的信号量标识符。
sem_num与前面一个函数相同。
cnd:表示将要采取的动作。最常用的两个值如下:
- SETVAL:用来把信号量初始化为一个已知的值。这个值通过union semun中的val成员设置。其作用是在信号量第一次使用之前对它进行设置。
- IPC_RMID:用于删除一个无需继续使用的信号量标志符。
semun联合结构的定义:
semun是在linux/sem.h中定义的: /*arg for semctl systemcalls.*/ union semun{ int val;/*value for SETVAL*/ struct semid_ds *buf;/*buffer for IPC_STAT&IPC_SET*/ ushort *array;/*array for GETALL&SETALL*/ struct seminfo *__buf;/*buffer for IPC_INFO*/ void *__pad;
信号量的使用
虽然上述函数调用看似很复杂,但是我们可以用这些接口来创建一个简单的PV类型的接口,然后用这个简单的接口来进行信号量相关操作。
下面的程序使用上述接口实现了P、V操组以及设置信号量、删除信号量的操作。
然后利用这些新的函数接口实现了两个程序实例访问临界区的功能。
在这里同时访问临界区的是一个程序的两个不同实例,并且使用参数个数的不同来进行区别。其中一个需要完成信号量的创建及其删除的额外操作。
两个程序在进入临界区和离开临界区的时候分别都会输出两个不同的字符,以此来进行区分。可以发现,两个不同的字符是成对出现的。因为同一时刻只有一个进程可以进入临界区。
完整代码:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/sem.h>//包含信号量定义的头文件 //联合类型semun定义 union semun{ int val; struct semid_ds *buf; unsigned short *array; }; //函数声明 //函数:设置信号量的值 static int set_semvalue(void); //函数:删除信号量 static void del_semvalue(void); //函数:信号量P操作 static int semaphore_p(void); //函数:信号量V操作 static int semaphore_v(void); static int sem_id;//信号量ID int main(int argc,char *argv[]) { int i; int pause_time; char op_char = 'O'; srand((unsigned int)getpid()); //创建一个新的信号量或者是取得一个已有信号量的键 sem_id = semget((key_t)1234,1,0666 | IPC_CREAT); //如果参数数量大于1,则这个程序负责创建信号和删除信号量 if(argc > 1) { if(!set_semvalue()) { fprintf(stderr,"failed to initialize semaphore\n"); exit(EXIT_FAILURE); } op_char = 'X';//对进程进行标记 sleep(5); } //循环:访问临界区 for(i = 0;i < 10;++i) { //P操作,尝试进入缓冲区 if(!semaphore_p()) exit(EXIT_FAILURE); printf("%c",op_char); fflush(stdout);//刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上 pause_time = rand() % 3; sleep(pause_time); printf("%c",op_char); fflush(stdout); //V操作,