转载自http://hi.baidu.com/csw8923/blog/item/c568f682d1fb1eb66d811907.html
要点描述:
1.CreateBoundaryDescriptor -- 创建一个边界描述符
2.WinBuiltinAdministratorsSid -- 创建一个SID对应的本地管理员组(一个角色principal会有有一个sid来唯一标识)
3.AddSIDToBoundaryDescriptor -- 保证管理员用户运行的唯一的应用。
4.ConvertStringSecurityDescriptorToSecurityDescriptor -- 创建本地管理员命名空间
5.CreatePrivateNamespace -- 创建专有命名空间
使用 GetLastError() 获取返回值 ERROR_ACCESS_DENIED 表明没有创建足够高特权账户。
使用 GetLastError() 获取返回值 ERROR_ALREADY_EXISTS 表明命名空间已经创建。
使用 OpenPrivateNamespace 来访问 专有命名空间
利用 CreateMutex 可以创建一个互斥体。(它是本程序唯一创建一个内核对象的函数)
注意的是 CreatePrivateNamespace 和 OpenPrivateNamespace 返回的都不是内核对象句柄。
所以不能用 CloseHandle 关闭,CreateBoundaryDescriptor、CreatePrivateNamespace 和
OpenPrivateNamespace所产生的句柄。
必须使用 ClosePrivateNamespace 来关闭 CreatePrivateNamespace 和 OpenPrivateNamespace所产生的句柄。
CreateBoundaryDescriptor 用 DeleteBoundaryDescriptor关闭 边界描述符。
可以定义一个自定义的前缀,并把它作为自己的专有命名空间用,这和使用Global 和 Local 前缀是一样的
负责创建内核对象的服务器进出将定义一个边界描述符,以对命名空间的名称自身进行保护。
其中 CheckInstances 函数中 有三个知识点要理解:
1.如何创建一个边界
2.如何将对应于本地管理员组(Local Administrators)的一个安全描述符 和它关联起来。
3.如何创建或打开一个其名称被用作互斥量的内核对象前缀的一个专有命名空间。
边界描述符将获得一个名称,但更重要的是,它会获得与它关联的一个特权用户组的SID.
/******************************************************************************
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>
///////////////////////////////////////////////////////////////////////////////
//主对话框
HWND g_hDlg;
// 互斥,边界和用于检测以前的运行实例命名
HANDLE g_hSingleton = NULL;
HANDLE g_hBoundary = NULL;
HANDLE g_hNamespace = NULL;
// 跟踪命名空间是否创建或清除及外开放
BOOL g_bNamespaceOpened = FALSE;
// 边界和私人名称命名
PCTSTR g_szBoundary = TEXT("3-Boundary");
PCTSTR g_szNamespace = TEXT("3-Namespace");
#define DETAILS_CTRL GetDlgItem(g_hDlg, IDC_EDIT_DETAILS)
///////////////////////////////////////////////////////////////////////////////
// 添加一个字符串“详细资料”编辑控件
void AddText(PCTSTR pszFormat, ...) {
va_list argList;
va_start(argList, pszFormat);
TCHAR sz[20 * 1024];
Edit_GetText(DETAILS_CTRL, sz, _countof(sz));
_vstprintf_s(
_tcschr(sz, TEXT('\0')), _countof(sz) - _tcslen(sz),
pszFormat, argList);
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:
// 用户点击了退出按钮
// 或返回了用ESCAPE对话
EndDialog(hwnd, id);
break;
}
}
///////////////////////////////////////////////////////////////////////////////
void CheckInstances() {
// 创建边界描述符
g_hBoundary = CreateBoundaryDescriptor(g_szBoundary, 0);
// CreateBoundaryDescriptor 参数1是描述当前Windows版本,参数2无用传入0.
// 函数签名暗指 返回值 表示是 一个内核对象句柄,但实际是一个用户模式结构边界定义。
// 不能把g_hBoundary 传给 CloseHandle; 应该将其传给DeleteBoundaryDescriptor。
// 创建一个SID对应的本地管理员组
BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
PSID pLocalAdminSID = &localAdminSID;
DWORD cbSID = sizeof(localAdminSID);
if (!CreateWellKnownSid(
WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID)
) {
AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"),
GetLastError());
return;
}
// 准本地管理员的SID边界描述
// --> 根据管理员用户运行的唯一的应用
// 将能够访问相同的命名空间中的内核对象
if (!AddSIDToBoundaryDescriptor(&g_hBoundary, pLocalAdminSID)) {
AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"),
GetLastError());
return;
}
// 创建本地管理员命名空间
// SECURITY_ATTRIBUTES是通过调用ConvertStringSecurityDescriptorToSecurityDescriptor函数来构造的。取得一个复杂语法结构。
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
TEXT("D:(A;;GA;;;BA)"),
SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) {
AddText(TEXT("Security Descriptor creation failed: %u\r\n"), GetLastError());
return;
}
g_hNamespace = CreatePrivateNamespace(&sa, g_hBoundary, g_szNamespace);// 创建专有命名空间,边界描述符(伪)句柄传入参数2.
// 参数1,传给SECURITY_ATTRIBUTES结构供Winodws判断是否调用OpenPrivateNamespace来访问命名空间。
// 创建内核对象字符串前缀会被指定为第三参数。
// 如果试图打开一个已经创建好的命名空间,CreatePrivateNamespace会返回NULL
// 不要忘记释放内存的安全描述符
LocalFree(sa.lpSecurityDescriptor);
// 检查结果,创造私人空间
DWORD dwLastError = GetLastError();
if (g_hNamespace == NULL) { // 如果没有结果则访问被拒绝
// --> 此代码必须运行在一个本地管理员帐户 == 低权限访问高权限命名空间时调用失败,
// GetLastError()返回ERROR_ACCESS_DENIED。
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) {
// 同时 GetLastError() 返回 ERROR_ALREADY_EXISTS。
// 这是说已经有另一个实例已经创建的命名空间这时
// 就得用OpenPrivateNamespace来打开专有命名空间。
AddText(TEXT("CreatePrivateNamespace failed: %u\r\n"), dwLastError);
g_hNamespace = OpenPrivateNamespace(g_hBoundary, g_szNamespace);
if (g_hNamespace == NULL) { // 打开命名空间 失败 返回 NULL。
AddText(TEXT(" and OpenPrivateNamespace failed: %u\r\n"),
dwLastError);
return;
} else { // 打开命名空间 成功 返回 TRUE;
g_bNamespaceOpened = TRUE;
AddText(TEXT(" but OpenPrivateNamespace succeeded\r\n\r\n"));
}
} else { // 打开出现异常错误
AddText(TEXT("Unexpected error occured: %u\r\n\r\n"),
dwLastError); // dwLastError 返回错误代码。
return;
}
}
}
// CreatePrivateNamespace 和 OpenPrivateNamespace返回是 HANDLE ,
// 不是内核句柄要用 ClosePrivateNamespace 关闭句柄。
// 尝试创建一个互斥对象名称
// 基于私人空间
TCHAR szMutexName[64];
StringCchPrintf(szMutexName, _countof(szMutexName), TEXT("%s\\%s"),
g_szNamespace, TEXT("Singleton")); // char 转 Wchar_t 字符集转换。
g_hSingleton = CreateMutex(NULL, FALSE, szMutexName); // 创建一个互斥体, 其中 g_hSingleton是一个内核对象。
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// 目前已有一个Singleton对象实例在运行
AddText(TEXT("Another instance of Singleton is running:\r\n"));
AddText(TEXT("--> Impossible to access application features.\r\n"));
} else {
// 第一个Singleton对象被创建,没有相同例子在运行。
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);
// 保持主要对话窗口跟踪处理
g_hDlg = hwnd;
// 检查是否有另一个实例已在运行
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,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// 显示主窗口
DialogBox(hInstance, MAKEINTRESOURCE(IDD_SINGLETON), NULL, Dlg_Proc);
// 清理和释放内核资源
if (g_hSingleton != NULL) {
CloseHandle(g_hSingleton); // 关闭一个内核对象
}
if (g_hNamespace != NULL) {
if (g_bNamespaceOpened) { // 打开空间
ClosePrivateNamespace(g_hNamespace, 0);
// 创建了空间,且希望关闭后仍然可见 应传入0参数2.
} else { // 创建空间
ClosePrivateNamespace(g_hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY);
// 创建了空间,且不希望关闭后仍然可见 应传入 PRIVATE_NAMESPACE_FLAG_DESTROY 参数2.
}
}
if (g_hBoundary != NULL) {
DeleteBoundaryDescriptor(g_hBoundary);
// 边界的关闭,终止运行。或调用DeleteBoundaryDescriptor传入唯一参数的伪句柄。
}
return(0);
}
//////////////////////////////// End of File //////////////////////////////////
浙公网安备 33010602011771号