/****************************************************************************** Module: Singleton.cpp Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre ******************************************************************************/ #include "resource.h" #include "../CommonFiles/CmnHdr.h" /* See Appendix A. */ #include <windowsx.h> #include <Sddl.h> // for SID management #include <tchar.h> #include <strsafe.h> /// // Main dialog HWND g_hDlg; // Mutex, boundary and namespace used to detect previous running instance HANDLE g_hSingleton = NULL; HANDLE g_hBoundary = NULL; HANDLE g_hNamespace = NULL; // Keep track whether or not the namespace was created or open for clean-up BOOL g_bNamespaceOpened = FALSE; // Names of boundary and private namespace PCTSTR g_szBoundary = TEXT("3-Boundary"); PCTSTR g_szNamespace = TEXT("3-Namespace"); #define DETAILS_CTRL GetDlgItem(g_hDlg, IDC_EDIT_DETAILS) /// // Adds a string to the "Details" edit control void AddText(PCTSTR pszFormat, ...) { va_list argList;//http://baike.baidu.com/view/1340814.htm va_start(argList, pszFormat); TCHAR sz[20 * 1024]; /*在windowsx.h可以找到次宏 #define Edit_GetText(hwndCtl, lpch, cchMax) GetWindowText((hwndCtl), (lpch), (cchMax)) Edit_GetText宏的作用是从第一个参数(一般是对话框)中获取一消息,第二个参数是消息的存储空间,第三个参数是存储空间的容量。例如如下例子: Edit_GetText(GetDlgItem(hwnd, IDC_REQUEST), g_szSharedRequestAndResultBuffer, _countof(g_szSharedRequestAndResultBuffer));*/ Edit_GetText(DETAILS_CTRL, sz, _countof(sz)); //_vstprintf_s( szBuffer,MAX_PATH,pcszFormat,pArgList ); 将参数列表的内容存放在szBuffer 里 _vstprintf_s( _tcschr(sz, TEXT('/0')), _countof(sz) - _tcslen(sz), pszFormat, argList); /*设置一个编辑控件的文本。 int Edit_SetText( HWND hwndCtl, LPTSTR lpsz); hwndCtl 一个控件句柄。 lpsz 一个指针,指向以NULL为结尾的文本设置文本到控件中。 返回值;如果函数成功,返回值是非零。如果失败,返回值是零。 这个宏描述去调用一个 SetWindowText. 宏信息;头文件 声明在 Windowsx.h */ Edit_SetText(DETAILS_CTRL, sz); va_end(argList); } /// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDOK: case IDCANCEL: // User has clicked on the Exit button // or dismissed the dialog with ESCAPE EndDialog(hwnd, id); break; } } /// void CheckInstances() { // Create the boundary descriptor //HANDLE CreateBoundaryDescriptor(PCTSTR pszName,DWORD dwFlags); //在当前版本的Windows中,第二个参数还没有什么用,因此应该为它传入0。 //注意,函数的签名暗示返回值是一个内核对象句柄,但实情并非如此。 //返回的是一个指针,它指向一个用户模式的结构,结构中包含了边界的定义。 //由于这个原因,永远都不要把返回的句柄值传给CloseHandle; //相反,应该把它传给DeleteBoundaryDescriptor。 //HANDLE WINAPI CreateBoundaryDescriptor( __in LPCTSTR Name, __in ULONG Flags ) g_hBoundary = CreateBoundaryDescriptor(g_szBoundary, 0); // Create a SID corresponding to the Local Administrator group BYTE localAdminSID[SECURITY_MAX_SID_SIZE]; PSID pLocalAdminSID = &localAdminSID; DWORD cbSID = sizeof(localAdminSID); /*SID是一个可变长的数据结构,用来标识用户或组。 SID的典型字符串表示形式为“S-R-I-S”,以字母S打头,R表示修订号,现在都是1,I表示授权(Authority,6bytes),S表示子授权(SubAuthority), 因为子授权个数不定,所以整个SID的长度是可变的,结构里使用了“子授权数”域来表示SID包含了多少个子授权。 知名SID(well-known SIDs)是一组系统已经预定了的常用SID,如Everyone(S-1-1-0),Administrators(S-1-5-32-544)SID等,详见资料11。 创建知名SID可用CreateWellKnownSid函数,示例参见Msdn。 函数原型 BOOL WINAPI CreateWellKnownSid( __in WELL_KNOWN_SID_TYPE WellKnownSidType, __in_opt PSID DomainSid, __out_opt PSID pSid, __inout DWORD *cbSid );*/ if (!CreateWellKnownSid( WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID) ) { AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u/r/n"), GetLastError()); return; } // Associate the Local Admin SID to the boundary descriptor // --> only applications running under an administrator user // will be able to access the kernel objects in the same namespace if (!AddSIDToBoundaryDescriptor(&g_hBoundary, pLocalAdminSID)) { AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u/r/n"), GetLastError()); return; } // Create the namespace for Local Administrators only SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa);//sizeof计算的是字节的长度;countof计算的字符的长度(包括/0) sa.bInheritHandle = FALSE;//不可继承 /*BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptor ( __in LPCTSTR StringSecurityDescriptor , DWORD StringSDRevision , __out PSECURITY_DESCRIPTOR __in * SecurityDescriptor , __out PULONG SecurityDescriptorSize ) */ if (!ConvertStringSecurityDescriptorToSecurityDescriptor( TEXT("D:(A;;GA;;;BA)"), SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) { AddText(TEXT("Security Descriptor creation failed: %u/r/n"), GetLastError()); return; } /* 最后创建专有命名空间 HANDLE CreatePrivateNamespace( PSECURITY_ATTRIBUTES psa; PVOID pvBoundaryDescriptor; PCTSTR pszAliasPrefix); */ g_hNamespace = CreatePrivateNamespace(&sa, g_hBoundary, g_szNamespace); // Don't forget to release memory for the security descriptor LocalFree(sa.lpSecurityDescriptor); // Check the private namespace creation result DWORD dwLastError = GetLastError(); if (g_hNamespace == NULL) { // Nothing to do if access is denied // --> this code must run under a Local Administrator account if (dwLastError == ERROR_ACCESS_DENIED) { AddText(TEXT("Access denied when creating the namespace./r/n")); AddText(TEXT(" You must be running as Administrator./r/n/r/n")); return; } else { if (dwLastError == ERROR_ALREADY_EXISTS) { // If another instance has already created the namespace, // we need to open it instead. AddText(TEXT("CreatePrivateNamespace failed: %u/r/n"), dwLastError); g_hNamespace = OpenPrivateNamespace(g_hBoundary, g_szNamespace); if (g_hNamespace == NULL) { AddText(TEXT(" and OpenPrivateNamespace failed: %u/r/n"), dwLastError); return; } else { g_bNamespaceOpened = TRUE; AddText(TEXT(" but OpenPrivateNamespace succeeded/r/n/r/n")); } } else { AddText(TEXT("Unexpected error occured: %u/r/n/r/n"), dwLastError); return; } } } // Try to create the mutex object with a name // based on the private namespace TCHAR szMutexName[64]; StringCchPrintf(szMutexName, _countof(szMutexName), TEXT("%s//%s"), g_szNamespace, TEXT("Singleton")); g_hSingleton = CreateMutex(NULL, FALSE, szMutexName); if (GetLastError() == ERROR_ALREADY_EXISTS) { // There is already an instance of this Singleton object AddText(TEXT("Another instance of Singleton is running:/r/n")); AddText(TEXT("--> Impossible to access application features./r/n")); } else { // First time the Singleton object is created AddText(TEXT("First instance of Singleton:/r/n")); AddText(TEXT("--> Access application features now./r/n")); } } /// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_SINGLETON); // Keep track of the main dialog window handle g_hDlg = hwnd; // Check whether another instance is already running CheckInstances(); return(TRUE); } /// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); } return(FALSE); } /// int APIENTRY _tWinMain(HINSTANCE hInstance,// 应用程序当前事例的句柄 HINSTANCE hPrevInstance, /*应用程序的先事例的句柄。对于同一个程序打开两次,出现两个窗口第一次打开的窗口就是先前实例的窗口。 对于一个32的位程序,该参数总为NULL。如果需要检测另外一个事例是否已经存在,则使用CreateMutex函数创建一个独一无二的名字。 即使互斥名已经存在,CreateMutex函数也是成功的,但是GetLastError函数将返回 ERROR_ALREADY_EXISTS, 这就表明在应用程序中有另外一个事例存在,因为它首先创建了互斥名。*/ LPTSTR lpCmdLine,//指向应用程序命令行的空字符串的指针,不包括函数名。获得整个命令行,参看GetCommandLine。 int nCmdShow)//指明窗口如何显示。 { //UNREFERENCED_PARAMETER 展开传递的参数或表达式。其目的是避免编译器关于未引用参数的警告。 UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // Show main window //函数原型:int DialogBox(HINSTANCE hlnstance,LPCTSTRIpTemplate,HWND hWndParent,DLGPROC IpDialogFunc); DialogBox(hInstance, MAKEINTRESOURCE(IDD_SINGLETON), NULL, Dlg_Proc); // Don't forget to clean up and release kernel resources if (g_hSingleton != NULL) { CloseHandle(g_hSingleton); } if (g_hNamespace != NULL) { if (g_bNamespaceOpened) { // Open namespace ClosePrivateNamespace(g_hNamespace, 0); } else { // Created namespace ClosePrivateNamespace(g_hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY); } } if (g_hBoundary != NULL) { DeleteBoundaryDescriptor(g_hBoundary); } return(0); //如果函数运行成功,则会启动消息泵,进入消息循环。当消息循环收到WM_QUIT消息时,就会退出程序,并返回消息的wParam参数。 //如果函数在进入消息循环前就退出,则应返回0 /*备注:WinMain函数应初始化应用程序,显示主窗口,进入一个消息接收一发送循环, 这个循环是应用程序执行的其余部分的顶级控制结构。当接收到一个WM_QUIT消息时,程序就中止。 这时,WinMain函数应退出应用程序,并且返回传递给WM_QUIT消息的wParam参数的值。 如果由于调用PostQuitMessage函数而接收到WM_QUIT消息,wParam的值是PostQuiMessage函数的nExitCode的值。 请参看“创建一个窗口循环”。*/ } End of File //