这里的变化太快了,一切都不稳定,
Bjarne Stroustrup 讲述语言的演变(转载)
<DIV class=FeatureSmallHead>采访++</DIV>
在VC2005 Express中使用WTL
废话不多说,搞了一上午了,
安装VC2005Express,先下PSDK,注意安装的时候要记得安那个Web WorkShop SDK,否则编译的时候会先提示Shlwapi.h这个文件,其他需要注意的就是CodeProject上那个日本哥们写的http://www.codeproject.com/KB/wtl/WTLExpress.aspx,写的不错,只要安这个步骤,应该不会错的,主要是那个在VC2005Express下设置PSDK,它妈的真服了MS,VC2005Express默认都不让生成Win32工程,还要按照那个挂掉的链接说明设置,找了半天没找到那个链接的相关内容,搞得累了一上午,最后终于google到了,竟然还要改工程默认配置文件,终于搞定了,为了以后设置方便这里把那篇E文Copy了:
http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/Using Visual C++ 2005 Express Edition with the Microsoft Platform SDK
By Brian Johnson,
Microsoft Corporation
You can use Visual C++ Express to build powerful .NET Framework applications immediately after installation. In order to use Visual C++ Express to build Win32 applications, you’ll need to take just a few more steps. I’ll list the steps necessary for building Win32 applications using Visual C++ Express.
Step 1: Install Visual C++ Express.
If you haven’t done so already, install Visual C++ Express.
Step 2: Install the Microsoft Platform SDK.
Install the Platform SDK over the Web from the Download Center. Follow the instructions and install the SDK for the x86 platform.
Step 3: Update the Visual C++ directories in the Projects and Solutions section in the Options dialog box.
Add the paths to the appropriate subsection:
Executable files: C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Bin
Include files: C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Include
Library files: C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Lib
Note: Alternatively, you can update the Visual C++ Directories by modifying the VCProjectEngine.dll.express.config file located in the /vc/vcpackages subdirectory of the Visual C++ Express install location. Please make sure that you also delete the file “vccomponents.dat” located in the “%USERPROFILE%/Local Settings/Application Data/Microsoft/VCExpress/8.0” if it exists before restarting Visual C++ Express Edition.
Step 4: Update the corewin_express.vsprops file.
One more step is needed to make the Win32 template work in Visual C++ Express. You need to edit the corewin_express.vsprops file (found in C:/Program Files/Microsoft Visual Studio 8/VC/VCProjectDefaults) and
Change the string that reads:
AdditionalDependencies=”kernel32.lib” to
AdditionalDependencies=”kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib”
Step 5: Generate and build a Win32 application to test your paths.
In Visual C++ Express, the Win32 Windows Application type is disabled in the Win32 Application Wizard. To enable that type, you need to edit the file AppSettings.htm file located in the folder “%ProgramFiles%/Microsoft Visual Studio 8/VC/VCWizards/AppWiz/Generic/Application/html/1033/“.
In a text editor comment out lines 441 - 444 by putting a // in front of them as shown here:
// WIN_APP.disabled = true;
// WIN_APP_LABEL.disabled = true;
// DLL_APP.disabled = true;
// DLL_APP_LABEL.disabled = true;
Save and close the file and open Visual C++ Express.
From the File menu, click New Project. In the New Project dialog box, expand the Visual C++ node in the Product Types tree and then click Win32. Click on the Win32 Console Application template and then give your project a name and click OK. In the Win32 Application Wizard dialog box, make sure that Windows application is selected as the Application type and the ATL is not selected. Click the Finish button to generate the project.
As a final step, test your project by clicking the Start button in the IDE or by pressing F5. Your Win32 application should build and run.
WM_NCHITTEST消息
以SDK为例:
case WM_LBUTTONDOWN :
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
wsprintf(mess,”pt.x=%d,pt.y=%d”,pt.x,pt.y);
MessageBox(hwnd,mess,”调试”,MB_OK);
/ScreenToClient(hwnd,&pt);/
if (PtInRect(&rcClose, pt))
{
MessageBox(hwnd,”点击了关闭按钮”,”调试”,MB_OK);
SendMessage(hwnd,WM_SYSCOMMAND,(WPARAM)SC_CLOSE,(LPARAM)MAKELPARAM(pt.x, pt.y));
}
if (PtInRect(&rcMin, pt))
{
SendMessage(hwnd,WM_SYSCOMMAND,(WPARAM)SC_MINIMIZE,(LPARAM)MAKELPARAM(pt.x, pt.y));
}
break;
case WM_NCHITTEST:
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
ScreenToClient(hwnd,&pt);
if(!PtInRect(&rcClose,pt) && !PtInRect(&rcMin,pt))
return HTCAPTION;
else
return HTCLIENT;
当你在消息函数中截获此消息时,你可以选择直接返回相应的值比如HTCAPTION给OS,这时经过我的测试发现OS就不会给你发送WM_LBUTTONDOWN消息了,而如上所示我需要响应WM_LBUTTONDOWN怎么办呢?这时可以通过判断相应点是否在某个区域内返回相应的值,经过测试可以运行。
映像模式
<TABLE style="TABLE-LAYOUT: fixed">
设备坐标就是你创建出来的窗口那个坐标,其原点始终在(0,0),即窗口左上角,
逻辑坐标就是你GDI函数(绝大部分)中使用的坐标,它是一个虚拟的”窗口”,仅仅为了让你画图方便
使用了GDI函数后这里指定的逻辑坐标Windows在实际输出到窗口上时将其转换为设备坐标,这时映像模式派上用途了,
设置”窗口”坐标原点(逻辑坐标)方法:
1
CRect rect;
GetClientRect (&rect);
dc.SetMapMode (MM_LOENGLISH);
dc.SetViewportOrg (rect.Width () / 2, rect.Height () / 2);
2
CRect rect;
GetClientRect (&rect);
CPoint point (rect.Width () / 2, rect.Height () / 2);
dc.SetMapMode (MM_LOENGLISH);
dc.DPtoLP (&point);
dc.SetWindowOrg (-point.x, -point.y);
两种方法都是将”窗口”坐标(逻辑坐标)原点设置为客户区中央,但是需要注意的是在使用SetWindowOrg时坐标值必须是逻辑坐标,因此需要dc.DPtoLP (&point),而在使用SetViewportOrg时则不需,因为这个函数使用的坐标值是设备坐标值。
总结:设置逻辑坐标原点用SetViewportOrg最好,其中指定的x,y即是最后逻辑坐标原点的值,在这个基础上使用GDI函数即是。
多线程学习-使用临界区进行线程同步
昨天那个火车站售票系统存在线程同步上的问题,这在实际应用中是不能存在的,否则后果不堪设想,估计上次那个ATM取款机的问题就是由于线程同步引起的。今天看了视频第16讲,知道了互斥,事件等同步对象以及相应的同步函数是可以跨进程使用的,而且一般也是作为不同进程中线程的同步用的,当然在同一个线程中也是可以用的,但是使用稍显麻烦,使用临界区进行同一个进程中的线程同步则简单直观。下面使用临界区来消除卖票系统中的同步问题:
多线程学习-线程基本概念及线程创建
<SPAN style="FONT-SIZE: 10.5pt; mso-spacerun: yes"><FONT face=宋体><FONT face="Times New Roman"> </FONT>以前上操作系统时学过一些关于进程,线程以及同步的理论,也在Linux做过一些多线程的实验,但是感觉那些都只是为了应付课程之类的东东,今天看了孙鑫老师的VC视频第15讲,主要讲的线程的创建以及使用互斥对象进行线程同步操作,感觉他这套视频确实是很经典。下面总结一下学习的东西:</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; mso-spacerun: yes"></SPAN>
程序是计算机指令的集合,它以文件的形式存储在磁盘上。
进程:通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。
进程由两个部分组成:
1、操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。
2、地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。
进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。单个进程可能包含若干个线程,这些线程都“同时” 执行进程地址空间中的代码。每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。
线程由两个部分组成:
1、线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
2、线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。
当创建线程时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。可以将线程内核对象视为由关于线程的统计信息组成的一个小型数据结构。
线程总是在某个进程环境中创建。系统从进程的地址空间中分配内存,供线程的堆栈使用。新线程运行的进程环境与创建线程的环境相同。因此,新线程可以访问进程的内核对象的所有句柄、进程中的所有内存和在这个相同的进程中的所有其他线程的堆栈。这使得单个进程中的多个线程确实能够非常容易地互相通信。
线程只有一个内核对象和一个堆栈,保留的记录很少,因此所需要的内存也很少。
因为线程需要的开销比进程少,因此在编程中经常采用多线程来解决编程问题,而尽量避免创建新的进程。
操作系统为每一个运行线程安排一定的CPU时间 —— 时间片。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样。如果计算机拥有多个CPU,线程就能真正意义上同时运行了。
创建线程主要使用API函数CreateThread,在MSDN中的原型声明为:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
第一个参数为线程安全属性,一般设置为NULL就可以了;第二个参数是线程使用的堆栈大小,设为0就可以了,表示使用默认的;第三个参数即是线程的起始地址,也就是线程函数;第四个参数是传递给线程函数的参数,一般都会经过case的;第五个参数是线程创建标志,若为CREATE_SUSPENDED表示线程创建后被挂起,需要通过ResumeThread唤醒之,一般设为0表示马上执行;最后一个参数是一个传出参数,表示线程ID。
可以通过ExitThread来显示终止线程,注意这个函数需要传递线程ID,所以在创建线程时就要记录线程ID,通过CloseHandle可以递减分配给线程的所有内核对象计数。
下面通过视频中的火车站售票系统来说明如何使用CreateThread创建线程,这个例子确实很实用:
代码很简单,需要注意的就是CloseHandle并没有终止该线程,只是使该线程的内核对象计数减少。 输入结果截图: 由结果可以看出输出是又问题的,这是因为线程在执行过程中没有进行同步而引起的,后面再介绍解决这个问题的方法。
两种获取本机IP地址的方法
int gethostname(
char* name, int namelen );
#### Parameters
- name
- [out] Pointer to a buffer that receives the local host name.
- namelen
- [in] Length of the buffer, in bytes
char hostname[50]; gethostname(hostname,50);即可得到主机名。有了主机名下一步就是获取IP地址,方法一是通过API函数 gethostbyname,需要注意的事这个函数仅仅适用于IPv4,该函数再MSDN中的注释如下: The gethostbyname function retrieves host information corresponding to a host name from a host database. Note The gethostbyname function has been deprecated by the introduction of the getaddrinfo function. Developers creating Windows Sockets 2 applications are urged to use the getaddrinfo function instead of gethostbyname.
struct hostent* FAR gethostbyname(通过传递主机名得到的返回值是一个hosten结构体指针,
const char* name
**);**
typedef struct hostent {
char FAR h_name;
char FAR FAR h_aliases;
short h_addrtype;
short h_length;
char FAR FAR h_addr_list;
} hostent;
我们需要使用其中的h_addr_list,通过
LPCSTR pszIP=inet_ntoa ((struct in_addr )pHost->h_addr_list[0);即可得到IP地址,
注意h_addr_list是主机地址列表,我们这里取的事第一个值,有的电脑装有多个网卡可能有多个IP地址。
方法二是通过API函数getaddrinfo,通过上面的注释也可以看出MS推荐我们使用这个函数代替
gethostbyname,因为这个函数既适用于IPv4也适用于IPv6,详见MSDN。
The getaddrinfo function provides protocol-independent translation from host name to address.
int getaddrinfo(
const TCHAR nodename, const TCHAR* servname, const struct addrinfo* hints, struct addrinfo** res
**);
#### Parameters
- nodename
- [in] Pointer to a null-terminated string containing a host (node) name or a numeric host address string. The numeric host address string is a dotted-decimal IPv4 address or an IPv6 hex address.
- servname
- [in] Pointer to a null-terminated string containing either a service name or port number.
- hints
- [in] Pointer to an [addrinfo](ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.WIN32COM.v10.en/winsock/winsock/addrinfo_2.htm) structure that provides hints about the type of socket the caller supports. See Remarks.
- res
- [out] Pointer to a linked list of one or more
addrinfo** structures containing response information about the host.
MSDN说这个函数是用来提供一种与协议无关的主机名转换为地址的方法,参照MSDN上面的例子经过试验得出下面获取IP的方法: gethostname(hostname,100); //获得主机的名称
getaddrinfo(hostname,NULL,&hints,&res); //利用主机名称获取本地地址
char buff[100];
DWORD bufflen=100;
//将本地地址转换成字符串显示
struct sockaddr_in pSockaddr=(sockaddr_in)res->ai_addr;
char *pIP=inet_ntoa(pSockaddr->sin_addr); 这里为什么说经过试验呢,MSDN上面的例子将getaddrinfo函数的前两个参数分别设置为主机IP和端口号,显然第一个参数不能设为主机IP了,根据其注释这个参数既可以为主机名也可以是IP,所以设为主机名,第二个参数说可以是一个服务名或者端口号,如果设为端口号,用WSAAddressToString函数获取的值中就包括该端口号,我也不太清楚这是什么原因,总之感觉这个Socket函数很诡异。