|
Hook(鉤子)是一種在消息到達目標窗口前進行截獲的技術。使用鉤子主要使用以下三個函數SetWindowsHookEx:創建鉤子 CallNextHookEx:將消息傳給鉤子鏈中的下一個鉤子 UnhookWindowsHookEx:釋放鉤子 對于創建鉤子的函數SetWindowsHookEx,MSDN給出其原形如下:
HHOOK SetWindowsHookEx( int idHook, // type of hook to install HOOKPROC lpfn, // address of hook procedure HINSTANCE hMod, // handle to application instance DWORD dwThreadId // identity of thread to install hook for );
這些在windows上面使用沒有問題,但是在說明的最后,關于平臺限制的地方,可以清楚的看到以下文字:Windows CE: Unsupported. 也就是說,wince并不支持鉤子。 但是是不是不支持呢?只能說不直接支持鉤子,用別的方法也是可以使用鉤子函數的,那就是直接獲取鉤子函數地址,然后調用的方法。 g_hHookApiDLL = LoadLibrary(_T("coredll.dll")); SetWindowsHookEx = (_SetWindowsHookExW)GetProcAddress(g_hHookApiDLL, _T("SetWindowsHookExW")); 如法炮制,可以獲得其他兩個函數的地址,有了這三個函數的地址,就可以類似這樣使用了: g_hInstalledLLKBDhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardProc, hInstance, 0); 關于wince的鉤子,有以下總結,不盡不對之處,請給飛狐指正: 1 參看WinCE的winbase.h,wince下可以使用以下三種: #define WH_JOURNALRECORD 0 #define WH_JOURNALPLAYBACK 1 #define WH_KEYBOARD_LL 20 其中最有用的就是鍵盤鉤子了。Wince里面定義其為20,而不是windows里面的14,因此調用時要注意。 2 關于鍵盤鉤子回調函數keyboardProc,它里面的幾個參數并不像MSDN里面提到的KeyboardProc那樣: LRESULT CALLBACK KeyboardProc( int code, // hook code WPARAM wParam, // virtual-key code LPARAM lParam // keystroke-message information 按照說明,wParam應該存的是虛擬鍵信息。然而事實上,這三個函數中,第二個是用來指示是鍵按下還是彈起,第三個參數lParam才是真正存儲的按鍵信息數據。它存儲的是一個KBDLLHOOKSTRUCT結構體指針。這個結構體定義如下: typedef struct { DWORD vkCode; DWORD scanCode; DWORD flags; DWORD time; ULONG_PTR dwExtraInfo; } KBDLLHOOKSTRUCT; 結構體里面才是真正的按鍵信息。 3對于鍵盤鉤子,我只能使用一個,如果創建二個鉤子來檢測(不管是使用同一個dll,還是兩個不同的dll),則第一個可以正常工作,但是第二個會報錯,錯誤id是31 ERROR_GEN_FAILURE即 A device attached to the system is not functioning. 不知道哪位高手有解決方案? 4 鉤子有線程級和全局鉤子,但是我只試驗成功了全局鉤子,工作很好,但是線程級鉤子還沒有成功。 5 鉤子用途很多,我們就用它和驅動打交道,具體也不多說了。 鉤子函數源代碼在采用codeproject網站上面Prathamesh S Kulkarni的源代碼基礎上,增加了處理按鍵消息的部分,代碼比較長,就不貼了,有需要的可以交流,或者參看Prathamesh S Kulkarni的文章。
Windows 95進程間數據通訊的實現技術 1、引言 在Windows程序中,各個進程之間常常需要交換數據,進行數據通訊。WIN32 API提供了 許多函數使我們能夠方便高效的進行進程間的通訊,通過這些函數我們可以控制不同進 程間的數據交換,就如同在WIN16中對本地進程進行讀寫操作一樣。 典型的WIN16兩進程可以通過共享內存來進行數據交換:(1)進程A將GlobalAlloc(GM EM_SHARE...)API分配一定長度的內存;(2)進程A將GlobalAlloc函數返回的句柄傳遞 給進程B(通過一個登錄消息);(3)進程B對這個句柄調用GlobalLock函數,并利用G lobalLock函數返回的指針訪問數據。這種方法在WIN32中可能失敗,這是因為GlobalLo ck函數返回指向的是進程A的內存,由于進程使用的是虛擬地址而非實際物理地址,因此 這一指針僅與A進程有關,而于B進程無關。 本文探討了幾種WIN32下進程之間通訊的幾種實現方法,讀者可以使用不同的方法以達到 程序運行高效可靠的目的。 2、Windows95中進程的內存空間管理 WIN32進程間通訊與Windows95的內存管理有密切關系,理解Windows95的內存管理對我們 如下的程序設計將會有很大的幫助,下面我們討論以下Windows95中進程的內存空間管理 。 在WIN16下,所有Windows應用程序共享單一地址,任何進程都能夠對這一空間中屬于共 享單一的地址空間,任何進程都能夠對這一空間中屬于其他進程的內存進行讀寫操作, 甚至可以存取操作系統本身的數據,這樣就可能破壞其他程序的數據段代碼。 在WIN32下,每個進程都有自己的地址空間,一個WIN32進程不能存取另一個地址的私有 數據,兩個進程可以用具有相同值的指針尋址,但所讀寫的只是它們各自的數據,這樣 就減少了進程之間的相互干擾。另一方面,每個WIN32進程擁有4GB的地址空間,但并不 代表它真正擁有4GB的實際物理內存,而只是操作系統利用CPU的內存分配功能提供的虛 擬地址空間。在一般情況下,絕大多數虛擬地址并沒有物理內存于它對應,在真正可以 使用這些地址空間之前,還要由操作系統提供實際的物理內存(這個過程叫“提交”co mmit)。在不同的情況下,系統提交的物理內存是不同的,可能是RAM,也可能是硬盤模 擬的虛擬內存。 3、WIN32中進程間的通訊 在Windows 95中,為實現進程間平等的數據交換,用戶可以有如下幾種選擇: * 使用內存映射文件 * 通過共享內存DLL共享內存 * 向另一進程發送WM_COPYDATA消息 * 調用ReadProcessMemory以及WriteProcessMemory函數,用戶可以發送由GlobalLock( GMEM_SHARE,...)函數調用提取的句柄、GlobalLock函數返回的指針以及VirtualAlloc函 數返回的指針。 3.1、利用內存映射文件實現WIN32進程間的通訊 Windows95中的內存映射文件的機制為我們高效地操作文件提供了一種途徑,它允許我們 在WIN32進程中保留一段內存區域,把目標文件映射到這段虛擬內存中。在程序實現中必 須考慮各進程之間的同步。具體實現步驟如下: 首先我們在發送數據的進程中需要通過調用內存映射API函數CreateFileMapping創建一 個有名的共享內存: HANDLE CreateFileMapping( HANDLE hFile, // 映射文件的句柄, //設為0xFFFFFFFF以創建一個進程間共享的對象 LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全屬性 DWORD flProtect, // 保護方式 DWORD dwMaximumSizeHigh, //對象的大小 DWORD dwMaximumSizeLow, LPCTSTR lpName // 必須為映射文件命名 ); 與虛擬內存類似,保護方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多進程都對 同一共享內存進行寫訪問,則必須保持相互間同步。映射文件還可以指定PAGE_WRITECO PY標志,可以保證其原始數據不會遭到破壞,同時允許其他進程在必要時自由的操作數 據的拷貝。 在創建文件映射對象后使用可以調用MapViewOfFile函數映射到本進程的地址空間內。 下面說明創建一個名為MySharedMem的長度為4096字節的有名映射文件: HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF), NULL,PAGE_READWRITE,0,0x1000,"MySharedMem"); 并映射緩存區視圖: LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile, FILE_MAP_READ|FILE_MAP_WRITE,0,0,0); 其他進程訪問共享對象,需要獲得對象名并調用OpenFileMapping函數。 HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE, FALSE,"MySharedMem"); 一旦其他進程獲得映射對象的句柄,可以象創建進程那樣調用MapViewOfFile函數來映射 對象視圖。用戶可以使用該對象視圖來進行數據讀寫操作,以達到數據通訊的目的。 當用戶進程結束使用共享內存后,調用UnmapViewOfFile函數以取消其地址空間內的視圖 : if (!UnmapViewOfFile(pszMySharedMapView)) { AfxMessageBox("could not unmap view of file"); } 3.2、利用共享內存DLL 共享數據DLL允許進程以類似于Windows 3.1 DLL共享數據的方式訪問讀寫數據,多個進 程都可以對該共享數據DLL進行數據操作,達到共享數據的目的。在WIN32中為建立共享 內存,必須執行以下步驟: 首先創建一個有名的數據區。這在Visual C++中是使用data_seg pragma宏。使用data_ seg pragma宏必須注意數據的初始化: #pragma data_seg("MYSEC") char MySharedData[4096]={0}; #pragma data_seg() 然后在用戶的DEF文件中為有名的數據區設定共享屬性。 LIBRARY TEST DATA READ WRITE SECTIONS .MYSEC READ WRITE SHARED 這樣每個附屬于DLL的進程都將接受到屬于自己的數據拷貝,一個進程的數據變化并不會 反映到其他進程的數據中。 在DEF文件中適當地輸出數據。以下的DEF文件項說明了如何以常數變量的形式輸出MySh aredData。 EXPORTS MySharedData CONSTANT 最后在應用程序(進程)按外部變量引用共享數據。 extern _export"C"{char * MySharedData[];} 進程中使用該變量應注意間接引用。 m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED); m_pStatic->GetLine(0,*MySharedData,80); 3.3、用于傳輸只讀數據的WM_COPYDATA 傳輸只讀數據可以使用Win32中的WM_COPYDATA消息。該消息的主要目的是允許在進程間 傳遞只讀數據。Windows95在通過WM_COPYDATA消息傳遞期間,不提供繼承同步方式。SD K文檔推薦用戶使用SendMessage函數,接受方在數據拷貝完成前不返回,這樣發送方就 不可能刪除和修改數據: SendMessage(hwnd,WM_COPYDATA,wParam,lParam); 其中wParam設置為包含數據的窗口的句柄。lParam指向一個COPYDATASTRUCT的結構: typedef struct tagCOPYDATASTRUCT{ DWORD dwData;//用戶定義數據 DWORD cbData;//數據大小 PVOID lpData;//指向數據的指針 }COPYDATASTRUCT; 該結構用來定義用戶數據。 3.4、直接調用ReadProcessMemory和WriteProcessMemory函數實現進程間通訊 通過調用ReadProcessMemory以及WriteProcessMemory函數用戶可以按類似與Windows3. 1的方法實現進程間通訊,在發送進程中分配一塊內存存放數據,可以調用GlobalAlloc 或者VirtualAlloc函數實現: pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024); 可以得到指針地址: pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock (pApp->m_hGlobalHandle); 在接收進程中要用到用戶希望影響的進程的打開句柄。為了讀寫另一進程,應按如下方 式調用OpenProcess函數: HANDLE hTargetProcess=OpenProcess( STANDARD_RIGHTS_REQUIRED| PROCESS_VM_REDA| PROCESS_VM_WRITE| PROCESS_VM_OPERATION,//訪問權限 FALSE,//繼承關系 dwProcessID);//進程ID 為保證OpenProcess函數調用成功,用戶所影響的進程必須由上述標志創建。 一旦用戶獲得一個進程的有效句柄,就可以調用ReadProcessMemory函數讀取該進程的內 存: BOOL ReadProcessMemory( HANDLE hProcess, // 進程指針 LPCVOID lpBaseAddress, // 數據塊的首地址 LPVOID lpBuffer, // 讀取數據所需緩沖區 DWORD cbRead, // 要讀取的字節數 LPDWORD lpNumberOfBytesRead ); 使用同樣的句柄也可以寫入該進程的內存: BOOL WriteProcessMemory( HANDLE hProcess, // 進程指針 LPVOID lpBaseAddress, // 要寫入的首地址 LPVOID lpBuffer, // 緩沖區地址 DWORD cbWrite, // 要寫的字節數 LPDWORD lpNumberOfBytesWritten ); 如下所示是讀寫另一進程的共享內存中的數據: ReadProcessMemory((HANDLE)hTargetProcess, (LPSTR)lpsz,m_strGlobal.GetBuffer(_MAX_FIELD), _MAX_FIELD,&cb); WriteProcessMemory((HANDLE)hTargetProcess, (LPSTR)lpsz,(LPSTR)STARS, m_strGlobal.GetLength(),&cb); 4、進程之間的消息發送與接收 在實際應用中進程之間需要發送和接收Windows消息來通知進程間相互通訊,發送方發送 通訊的消息以通知接收方,接收方在收到發送方的消息后就可以對內存進行讀寫操作。
我們在程序設計中采用Windows注冊消息進行消息傳遞,首先在發送進程初始化過程中進 行消息注冊: m_nMsgMapped=::RegisterWindowsMessage("Mapped"); m_nMsgHandle=::RegisterWindowsMessage("Handle"); m_nMsgShared=::RegisterWindowsMessage("Shared"); 在程序運行中向接收進程發送消息: CWnd* pWndRecv=FindWindow(lpClassName,"Receive"); pWndRecv->SendMessage(m_MsgMapped,0,0); pWndRecv->SendMessage(m_nMsgHandle, (UINT)GetCurrentProcessID(),(LONG)pApp->m_hGlobalHandle); pWndRecv->SendMessage(m_nMsgShared,0,0); 可以按如下方式發送WM_COPYDATA消息: static COPYDATASTRUCT cds;//用戶存放數據 pWnd->SendMessage(WM_COPYDATA,NULL,(LONG)&cds); 接收方進程初始化也必須進行消息注冊: UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage("Mapped"); UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage("Handle"); UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage("Shared"); 同時映射消息函數如下: ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped) ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle) ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared) 在這些消息函數我們就可以采用上述技術實現接收進程中數據的讀寫操作了。 5、結束語 從以上分析中我們可以看出Windows95的內存管理與Windows 3.x相比有很多的不同,對 進程之間的通訊有較為嚴格的限制。這就確保了任何故障程序無法意外地寫入用戶的地 址空間,而用戶則可根據實際情況靈活地進行進程間的數據通訊,從這一點上來講Wind ows95增強應用程序的強壯性。 參考文獻: 1、 David J.Kruglinski, Visual C++技術內幕, 北京:清華大學出版社,1995. 2、 Microsoft Co. Visual C++ 5.0 On Line Help.
client: #include "Winsock2.h" #include "stdafx.h" #pragma comment(lib,"Ws2.lib") // TODO: 在 STDAFX.H 中 // 引用任何所需的附加頭文件,而不是在此文件中引用 void SOCKETRACE(char *buf,int len) { WSADATA wsadata; WSAStartup(MAKEWORD(2,0),&wsadata); struct sockaddr_in ipaddr; ipaddr.sin_family=AF_INET; ipaddr.sin_port=htons(11000); ipaddr.sin_addr.s_addr=inet_addr("169.254.2.2");
int sk=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); int c=connect(sk,(sockaddr*)&ipaddr,sizeof(ipaddr)); send(sk,buf,len,0); closesocket(sk); WSACleanup(); }
void OEMTRACEW(BOOL cond, LPCWSTR fmt, ...) { if(cond) {
int n, size = 100; wchar_t* p ; va_list ap ;
p = (wchar_t*)malloc(size * sizeof(wchar_t)) ;
while( 1 ) { /* Try to print in the allocated space. */ va_start( ap, fmt ) ; n = _vsnwprintf( p, size, fmt, ap ) ; va_end( ap ) ; /* If that worked, return the string. */ if( n > -1 && n < size ) break ;
/* Else try again with more space. */ if( n > -1 ) /* C99 conform vsnprintf() */ size = n+1 ; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2 ; /* twice the old size */
p = (wchar_t*)realloc( p, size * sizeof(wchar_t) ) ; } char nstring[200]={0}; wcstombs( nstring,p,200); free( p ) ; SOCKETRACE(nstring,strlen(nstring));
}
}
void OEMTRACE(BOOL cond, const char * fmt, ...) { if(cond) {
int n, size = 100; char* p ; va_list ap ;
p = (char*)malloc(size) ;
while( 1 ) { /* Try to print in the allocated space. */ va_start( ap, fmt ) ; n = _vsnprintf( p, size, fmt, ap ) ; va_end( ap ) ; /* If that worked, return the string. */ if( n > -1 && n < size ) break ;
/* Else try again with more space. */ if( n > -1 ) /* C99 conform vsnprintf() */ size = n+1 ; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2 ; /* twice the old size */
p = (char*)realloc( p, size ) ; }
SOCKETRACE(p,strlen(p)); free( p ) ;
}
} client c#: udpClient = new UdpClient(); Byte[] bytes = Encoding.Unicode.GetBytes("aa"); udpClient.Send(bytes, bytes.Length,new IPEndPoint(IPAddress.Parse("169.254.2.2"), 11000)); udpserver c#: using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; using System.Net;
namespace ScocketRec { class Program { static void Main(string[] args) { UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse("169.254.2.2"), 11000)); try { //IPEndPoint object will allow us to read datagrams sent from any source. IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Parse("169.254.2.1"), 11000); while(true) { // Blocks until a message returns on this socket from a remote host. Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint); string returnData = Encoding.ASCII.GetString(receiveBytes);
// Uses the IPEndPoint object to determine which of these two hosts responded. Console.WriteLine("This is the message you received " + returnData.ToString()); Console.WriteLine("This message was sent from " + RemoteIpEndPoint.Address.ToString() + " on their port number " + RemoteIpEndPoint.Port.ToString()); }
udpClient.Close();
} catch (Exception e) { Console.WriteLine(e.ToString()); }
} } }
int BCDToInt(byte bcd)
{
return (0xff & (bcd>>4))*10 +(0xf & bcd);
}
#include<time.h>
main(){
char *wday[]={“Sun”,”Mon”,”Tue”,”Wed”,”Thu”,”Fri”,”Sat”};
time_t timep;
struct tm *p;
time(&timep);
p=localtime(&timep); /*取得當地時間*/
printf (“%d%d%d ”, (1900+p->tm_year),( l+p->tm_mon), p->tm_mday);
printf(“%s%d:%d:%d\n”, wday[p->tm_wday],p->tm_hour, p->tm_min,
p->tm_sec);
}
方法1 tmail -service "MMS" -to "" -body "" 方法2 附加到進程 static void LaunchInboxApp(void) { //#include <Toolhelp.h> HANDLE handle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); PROCESSENTRY32 info; info.dwSize=sizeof(info); int i=0; if(Process32First(handle,&info)) { if(_tcscmp(info.szExeFile,TEXT("tmail.exe"))==0 ) { HANDLE handle2=OpenProcess(0,FALSE,info.th32ProcessID); TerminateProcess(handle2,0); } else { while (Process32Next(handle,&info)!=FALSE) { if(_tcscmp(info.szExeFile,TEXT("tmail.exe"))==0 ) { HANDLE handle2=OpenProcess(0,FALSE,info.th32ProcessID); TerminateProcess(handle2,0); break; }
} }
} CloseToolhelp32Snapshot(handle);
SHELLEXECUTEINFO si ;
ZeroMemory( &si, sizeof(si) ) ; si.cbSize = sizeof(SHELLEXECUTEINFO) ; si.lpFile = L"\\windows\\tmail.exe" ; si.lpParameters = L"-NoUI";
ShellExecuteEx( &si ) ; }
void enumentryname() { char nstring[100]={0}; LPRASENTRYNAME lprasentryname; int i; DWORD nRet,cb,cEntries; lprasentryname = (LPRASENTRYNAME)LocalAlloc(LPTR, sizeof(RASENTRYNAME)); lprasentryname->dwSize = sizeof(RASENTRYNAME); if ((nRet = RasEnumEntries(NULL, NULL, lprasentryname, &cb, &cEntries)) == ERROR_BUFFER_TOO_SMALL) { lprasentryname = (LPRASENTRYNAME)LocalAlloc(LPTR, cb); lprasentryname->dwSize = sizeof(RASENTRYNAME); } // Calling RasEnumEntries to enumerate the phonebook entries nRet = RasEnumEntries(NULL, NULL, lprasentryname, &cb, &cEntries); if (nRet != ERROR_SUCCESS) { printf("RasEnumEntries failed: Error %d\n", nRet); } else { for(i=0;i < cEntries;i++) { wcstombs( nstring,lprasentryname->szEntryName,100); Write(nstring,strlen(nstring)); memset(nstring,0,100); lprasentryname++; } }
}
void WINAPI RasDialFunc(UINT nmsg,RASCONNSTATE st,DWORD dwError) {
wprintf(nmsg,"MSG"); } HRASCONN hs=NULL; RASDIALPARAMS RasDialParams; RasDialParams.dwSize=sizeof(RasDialParams); lstrcpy(RasDialParams.szEntryName,L"tdwap"); lstrcpy(RasDialParams.szPhoneNumber,L""); lstrcpy(RasDialParams.szUserName,L""); lstrcpy(RasDialParams.szPassword,L""); lstrcpy(RasDialParams.szCallbackNumber,L""); DWORD dwret=RasDial(NULL,NULL,&RasDialParams,0xFFFFFFFF,RasDialFunc,&hs); wprintf(dwret,"RasDial"); while(1) { Sleep(1000); }
unsigned int CloseRasGPRSConnections() { int index; // An integer index DWORD dwError, dwRasConnSize, dwNumConnections; // Number of connections found RASCONN RasConn[20]; // Buffer for connection state data,Assume the maximum number of entries is 20. BOOL RETURN_VALUE=0; WCHAR *MySelectNetName;
// Assume no more than 20 connections. RasConn[0].dwSize = sizeof (RASCONN); dwRasConnSize = 20 * sizeof (RASCONN);
// Find all connections. if (dwError = RasEnumConnections (RasConn, &dwRasConnSize,&dwNumConnections)) { return -1; }
// If there are no connections, return zero. if (!dwNumConnections) { return 0; }
// Terminate all of the remote access connections. GetConnectionStatus(); //here add to get selected network MySelectNetName=GetMMSSelectNet(); GPRSServerName* P_CMWAPtemp=pCMWAP_backup; for (index = 0; index < (int)dwNumConnections; ++index) { while( P_CMWAPtemp ) { if(!wcscmp(RasConn[index].szEntryName,P_CMWAPtemp->ServerName)|| !wcscmp(RasConn[index].szEntryName, MySelectNetName)) { if (dwError = RasHangUp (RasConn[index].hrasconn)) RETURN_VALUE=-1; else //successfully disconnect cmwap; RETURN_VALUE=0; } P_CMWAPtemp = P_CMWAPtemp->pnext ; } } //free mem freelink(pCMWAP_backup); return RETURN_VALUE; }
有個使用指針的方法:
int x = 1; if(*(char *)&x == 1) printf("little-endian\n"); else printf("big-endian\n");
另外一個可能是用聯合。
參見問題 10.15 和 20.7。
http://www.uml.org.cn/embeded/200610255.htm
setwindowlong
MapPtrToProcess
PerformCallBack4
分析一下UML類圖中關聯、聚合、組合三者的定義與關系。 @author:JZhang 06-11-27 E-mail:zhangjunhd@gmail.com Blog: http://blog.csdn.net/zhangjunhd/ 1.關聯(Association)類之間的關聯大多用來表示變量實例持有著對其他對象的引用。 Phone擁有一個對Button的引用。 2.聚合(Aggregation)聚合是關聯的一種特殊形式,它意味著一種整體/部分(whole/part)的關系。 一個整體不能是它自己的一部分。 因此 ,實例不能形成聚合回路,一個單獨的對象不能夠成為它自己的聚合,兩個對象不能互相聚合,三個對象不能形成一個聚合環。下圖為實例間的非法聚合循環: 3.組合(Composition)組合是一種特殊的聚合形式。 UML對組合的定義: ①如同聚合,實例不能有循環。 ②一個被所有者實例不能同時有兩個所有者。 ③所有者負責被組合的對象的生命周期的管理。如果所有者被銷毀,被所有者也必須跟著一起被銷毀,如果所有者被復制,被所有者也必須跟著一起被復制。 http://blog.csdn.net/dylgsy/archive/2006/08/16/1076044.aspx UML類圖關系全面剖析 http://www.ibm.com/developerworks/cn/rational/r-shenzj/ 利用Rational Rose進行C++代碼和數據庫結構分析 vs2008 支持 c++ 類圖了
|