精华内容
下载资源
问答
  • windows外壳扩展编程之windows右键菜单

    千次阅读 2011-12-09 16:55:38
     Windows外壳扩展(Windows Shell Extension),是一类特殊的COM对象,在这类COM对象中用户可以加入自己的特殊功能,而Windows外壳扩展最终都会被Windows Explorer所引用[1]。  A shell extension is a ...
      
    

    第一部分 SHELL基本概念

        Windows外壳扩展(Windows Shell Extension),是一类特殊的COM对象,在这类COM对象中用户可以加入自己的特殊功能,而Windows外壳扩展最终都会被Windows Explorer所引用[1]

        A shell extension is a COM object that adds some kind of functionality to the Windows shell (Explorer).

        There are two parts in the term "shell extension." Shell refers to Explorer, and extension refers to code you write that gets run by Explorer when a predetermined event happens (e.g., a right-click on a .DOC file). So a shell extension is a COM object that adds features to Explorer.[7]

        A shell extension is an in-process server that implements some interfaces that handle the communication with Explorer. ATL is the easiest way to get an extension up and running quickly, since without it you'd be stuck writing QueryInterface() and AddRef() code over and over. It is also much easier to debug extensions on Windows NT-based OSes, as I will explain later.

        “Shell 扩展从字面上分两个部分:Shell ExtensionShell Windows Explorer,而Extension 则指由你编写的当某一预先约定好的事件(如在以. doc 为后缀的文件图标上单击右键)发生时由 Explorer调用执行的代码。因此一个“Shell 扩展就是一个为 Explorer 添加功能的 COM 对象。

        动态库必须注册才能使用。除了使用 regasm 来注册 DLL 以外,还应该在代码中增加 RegisterServer UnregisterServer 方法,以指导 DLL 注册时,在 Windows 注册表中增加什么键。关于具体键以下做简单说明:

        1) 注册DLLShell Extensions。具体位置是 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved,增加以 GUID 为名称的键,值则是动态库说明。(此位置里面全是 Shell 扩展的动态库注册,许多相关软件就是从里面获取信息,例如 ShexView

        2) 关联文件。Shell扩展一般是针对文件或者文件夹的,因此必须关联;许多人都熟知“HKEY_CLASSES_ROOT\*”的作用,就是用来关联所有文件。而文件夹则是“HKEY_CLASSES_ROOT\Folder”[3]

        我们所看到的资源管理器以及整个桌面,都是一个 Shell。在win32中是以外壳名字空间的形式来组织文件系统的,在外壳名字空间里的每一个对象()都实现了一个IShellFolder的接口,通过这个接口我们可以直接查询或间接得到其他相关的接口。

        (注:这里的对象指的是外壳名字空间中的一个节点,对象有可能是一个文件夹,有可能是一个文件,也有可能是一个虚拟文件夹,例如:我的电脑,网上邻居,控制面板等)[4]

        A shell extension is an in-process server that implements some interfaces that handle the communication with Explorer. ATL is the easiest way to get an extension up and running quickly, since without it you'd be stuck writing QueryInterface() and AddRef() code over and over. It is also much easier to debug extensions on Windows NT-based OSes[7]

        在外壳编程中,要使用 PIDL 路径代替普通路径桌面是最顶级的文件夹,外壳名字空间中其他各项都可以用从桌面开始的 PIDL 加以表示。

    通过API SHGetDesktopFolder获取桌面PIDL 和其 IShellFolder 接口。

    通过桌面,来获取“C:\”这个路径的 PIDL IShellFolder 接口,可以通过 IShellFolder ParseDisplayName BindToObject 函数。

     

    第二部分 Windows SHELL编程

        There are many types of shell extensions, each type being invoked when different events happen. Here are a few of the more common types, and the situations in which they are invoked:[7]

    Type

    When it's invoked

    What it does

    Context menu handler

    User right-clicks on a file or folder. In shell versions 4.71+, also invoked on a right-click in the background of a directory window.

    Adds items to the context menu.

    Property sheet handler

    Properties dialog displayed for a file.

    Adds pages to the property sheet.

    Drag and drop handler

    User right-drags items and drops them on a directory window or the desktop.

    Adds items to the context menu.

    Drop handler

    User drags items and drops them on a file.

    Any desired action.

    QueryInfo handler (shell version 4.71+)

    User hovers the mouse over a file or other shell object like My Computer.

    Returns a string that Explorer displays in a tooltip.

    Before we begin coding, there are some tips that will make the job easier. When you cause a shell extension to be loaded by Explorer, it will stay in memory for a while, making it impossible to rebuild the DLL.

    To have Explorer unload extensions more often, create this registry key:

    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\AlwaysUnloadDLL

    and set the default value to "1". On 9x, that's the best you can do. On NT, go to this key:

    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer and create a DWORD called DesktopProcess with a value of 1. This makes the desktop and Taskbar run in one process,

    and subsequent Explorer windows each run in its own process. This means that you can do your debugging with a single Explorer window, and when you close it, your DLL is automatically unloaded, avoiding

    any problems with the file being in use. You will need to log off and back on for these changes to take effect.[7]

    第一:注册表编程。第二:Shell Extension COM编程。通过注册表方式实现

    其实十分简单,请参阅 COM[1]组件注册表实现。在以下的内容为 shell 扩展编程---" Context Menu 处理器。

    Shell扩展实例均为进程内组件,它们均以动态库的形式存在。

    When our shell extension is loaded, Explorer calls our QueryInterface() function to get a pointer to an IShellExtInit interface.[7]

    IShellExtInit接口:IShellExtInit 接口为 Shell 扩展编程必须要实现的接口。该接口主要用来初始化 Shell 扩展处理器,它仅有一个虚成员函数Initialize,用户所有的 Shell 扩展初始化动作都由该函数完成。

    Initialize 函数中,我们要做的事情就是获取用户鼠标右键点击的文件名称。当用户在一个拥有WS_EX_ACCEPTFILES风格的窗体中Drag/Drop文件时这些文件名会以同一种格式存储,而且文件完整路径的获取也都以DragQueryFile API函数来实现。但是DragQueryFile需要传入一个HDROP句柄,该句柄即为Drag/Drop文件名称列表数据句柄(开始存放数据的内存区域首指针)。而 HDROP句柄的可以通过接口"DATAOBJECT lpdobj"的成员函数"GetData"来获取。

    进程内组件编程的一些特点,大体总结如下:"新建自己的接口,然后继承某些接口,最后一一实现这些接口的所有虚成员函数或加入自己的成员函数,最后就是组件的注册"

    1) 初始化接口

    int Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID);

    HRESULT IShellExtInit::Initialize (

    LPCITEMIDLIST pidlFolder,

    LPDATAOBJECT pDataObj,

    HKEY hProgID )

    Explorer 使用该方法传递给我们各种各样的信息.

    Explorer uses this method to give us various information. pidlFolder is the PIDL of the folder containing the files being acted upon. (A PIDL [pointer to an ID

    list] is a data structure that uniquely identifies any object in the shell, whether it's a file system object or not.) pDataObj is an IDataObject interface

    pointer through which we retrieve the names of the files being acted upon. hProgID is an open HKEY which we can use to access the registry key containing

    our DLL's registration data. For this simple extension, we'll only need to use the pDataObj parameter.[7]

    pidlFolder 是用户所选择操作的文件所在的文件夹的PIDL变量(一个PIDL[指向ID列表的指针],是一个数据结构,它唯一地标识了在Shell命名空间的任何对象,一个Shell命名空间中的对象可以是也可以不是真实的文件系统中的对象。)lpdobj是一个IDataObject接口指针,通过它我们可以获取用户所选择操作的文件名。hKeyProgID是一个HKEY注册表键变量,可以用它获取我们的DLL的注册数据。

    因此我们可以在这个方法中,获取到被右击选择的一个或多个文件/文件夹名。

    The COM_MAP is how ATL implements QueryInterface(). It tells ATL what interfaces other programs can retrieve from our COM objects.[7]

    2) 与上下文菜单交互的接口

    Once Explorer has initialized our extension, it will call the IContextMenu methods to let us add menu items, provide fly-by help, and carry out the user's selection.[7]

    一旦 Explorer 初始化了扩展,它就会接着调用 IContextMenu 的方法让我们添加菜单项, 提供状态栏上的提示, 并响应执行用户的选择。

    处理器类型 COM接口

    Context menu 处理器 IContextMenu

    Property sheet 处理器 IShellPropSheetExt

    Drag and drop 处理器 IContextMenu

    Drop 处理器 IDropTarget

    QueryInfo 处理器(Shell V4.71+) IQueryInfo

    其中的"Drag and drop 处理器"除了COM接口IContextMenu需要实现外还得需要注册表的特殊注册才行。IContextMenu 接口有三个虚成员函数需要我们的组件来实现:

    The first one, QueryContextMenu(), lets us modify the menu.[7]

    QueryContextMenu,在QueryContextMenu 成员函数中我们可以加入自己的菜单项,通过 InsertMenu API 函数来实现。

    HRESULT IContextMenu::QueryContextMenu (

    HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd,

    UINT uidLastCmd, UINT uFlags );

    hmenu is a handle to the context menu. uMenuIndex is the position in which we should start adding our items. uidFirstCmd and uidLastCmd are the range of

    command ID values we can use for our menu items. uFlags indicates why Explorer is calling QueryContextMenu(), and I'll get to this later.[7]

    3) 在状态栏上显示提示帮助

    The next IContextMenu that can be called is GetCommandString(). If the user right-clicks a text file in an Explorer window, or selects a text file and then clicks the File menu, the status bar will show fly-by help when our menu item is highlighted. Our GetCommandString() function will return the string that we want Explorer to show.

    GetCommandStringGetCommandString 成员函数为 Explorer 提供了在状态栏显示菜单命令提示信息的方法。在这个方法中 "LPSTR pszName" 是我们要关注的参数,我们只要根据 "UINT uFlags" 参数来填充 "LPSTR pszName" 参数即可。在COM 编程中尽可能使用兼容的 TCHAR 类型,也尽量不要使用C类函数库,因为这样会使您无法通过 "Win32 Release Mindependency " 或其他 UINCode/Release 版本的编译过程。

    HRESULT IContextMenu::GetCommandString (

    UINT idCmd, UINT uFlags, UINT* pwReserved,

    LPSTR pszName, UINT cchMax );

    idCmd is a zero-based counter that indicates which menu item is selected. Since we have just one menu item, idCmd will always be zero. But if we had added, say, 3 menu items,

    idCmd could be 0, 1, or 2. uFlags is another group of flags, We can ignore pwReserved. pszName is a pointer to a buffer owned by the shell where we will store

    the help string to be displayed. cchMax is the size of the buffer. The return value is one of the usual HRESULT constants, such as S_OK or E_FAIL.[7]

    GetCommandString() can also be called to retrieve a "verb" for a menu item. A verb is a language-independent string that identifies an action that can be taken on a file. The docs for ShellExecute() have more to say, and the subject of verbs is best suited for another article, but the short version is that verbs can be either listed in the registry

    (such as "open" and "print"), or created dynamically by context menu extensions. This lets an action implemented in a shell extension be invoked by a call to ShellExecute().

    Anyway, the reason I mentioned all that is we have to determine why GetCommandString() is being called. If Explorer wants a fly-by help string, we provide it. If Explorer is asking for a verb, we'll just ignore the request. This is where the uFlags parameter comes into play. If uFlags

    has the GCS_HELPTEXT bit set, then Explorer is asking for fly-by help. Additionally, if the GCS_UNICODE bit is set, we must return a Unicode string.[7]

    下一个要被调用的IContextMenu 方法是 GetCommandString().。如果用户是在浏览器窗口中右击文本文件,或选中一个文本文件后单击文件菜单时,状态栏会显示提示帮助。我们的 GetCommandString() 函数将返回一个帮助字符串供浏览器显示。

    4)执行用户的选择

    InvokeCommand,实现最终菜单项命令的执行。

    This method is called if the user clicks on the menu item we added. The prototype for InvokeCommand() is:

    HRESULT IContextMenu::InvokeCommand (

    LPCMINVOKECOMMANDINFO pCmdInfo );

    IContextMenu 接口的最后一个方法是 InvokeCommand()。当用户点击我们添加的菜单项时该方法将被调用。其参数:CMINVOKECOMMANDINFO 结构带有大量的信息, 但我们只关心 lpVerb hwnd 这两个成员。

    lpVerb performs double duty - it can be either the name of the verb that was invoked, or it can be an index telling us which of our menu items was clicked on. hwnd is the handle of the Explorer

    window where the user invoked our extension; we can use this window as the parent window for any UI that we show.[7]

    lpVerb参数有两个作用------它或是可被激发的verb(动作),或是被点击的菜单项的索引值。hwnd 是用户激活我们的菜单扩展时所在的浏览器窗口的句柄。我们可以根据被点击的菜单项索引,来执行相应的操作。

    5)注册Shell扩展

    现在我们已经实现了所有需要的COM接口. 可是我们怎样才能让浏览器使用我们的扩展呢?首先,我们要注册动态库。但仅仅这样是不够的,为了告诉浏览器使用我们的扩展, 我们需要在文本文件类型(因为我们要关联文本)的注册表键下注册扩展。ATL automatically generates code that registers our DLL as a COM server, but that just lets other apps use our DLL. In order to tell Explorer our extension exists, we need to register it under the key that holds info about text files[7]

    The NoRemove keyword means that the key should not be deleted when the server is unregistered,ForceRemove, which means that if the key exists, it will be deleted

    before the new key is written.

     

    第三部分 SHELL Additional 

    一、编程中的一些关键点 

    1、 We implemented the IShellExtInit interface, which was how Explorer initialized our object. There is another initialization interface used for some shell extensions, IPersistFile, and this is the one an infotip extension uses. IShellExtInit::Initialize() receives an IDataObject pointer with which it can enumerate all of the files that were selected. Extensions that can only ever operate on a single file use IPersistFile. Since the mouse can't hover over more than one object at a time, an infotip extension only works on one file at a time, so it uses IPersistFile. 

    2、AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC

    The AFX_MANAGE_STATE macro is necessary for MFC to work properly. Because our DLL is being loaded by a non-MFC app, every exported function that uses MFC must initialize MFC manually. If you don't include that line, many MFC functions (mostly the ones related to resources) will break or have assertion failures.

    3、In order to have a dialog which has the XP theme enabled, it was necessary to set the following values in stdafx.h before any of the #includes:

    #define VC_EXTRALEAN //necessary

    #ifndef _WIN32_WINNT

    #define _WIN32_WINNT 0x501

    #endif

    #define _ATL_APARTMENT_THREADED

    #define ISOLATION_AWARE_ENABLED 1 //XP style awareness

    4、A shell extension is a COM DLL。

    5、Note that DllRegisterServer() and DllUnregisterServer() are not called by Explorer, but by the installer. If you downloaded the source files instead of the installer, you will have to register the DLL yourself using the command line tool regsvr32. If you compile the source from within Visual C++, then regsvr32 runs as part as the build process.

    6、整体而言,使用SHELL时流程基本如下:

    In short, this is what happens: When the user right-clicks inside an Explorer window, Explorer calls CtxMenu's Initialize() function. At this point, the context menu isn't visible yet. Explorer then calls the QueryContextMenu() function to add the Select... item to the menu, and shows it. When the user moves over this menu item, Explorer obtains a description from GetCommandString(). Finally, when the user picks the Select... item, Explorer calls InvokeCommand().

    The only place where we can figure out whether the user clicked on a file or on the background of the window is in Initialize().

    http://www.codeproject.com/KB/shell/wildcardselect.aspx

    http://www.allyoursoftware.com/

    第四部分 相关的API

    1、GlobalLock Function

    Locks a global memory object and returns a pointer to the first byte of the object's memory block.

    2、DragQueryFile Function

    Retrieves the names of dropped files that result from a successful drag-and-drop operation.

    http://msdn.microsoft.com/en-us/library/bb776408%28VS.85%29.aspx

    3、GetProcAddress Function

    Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).

    http://msdn.microsoft.com/en-us/library/ms683212%28VS.85%29.aspx

    4、InsertMenu Function

    Inserts a new menu item into a menu, moving other items down the menu.

    Note The InsertMenu function has been superseded by the InsertMenuItem function. You can still use InsertMenu, however, if you do not need any of the extended features of InsertMenuItem.

    http://msdn.microsoft.com/en-us/library/ms647987%28VS.85%29.aspx

    5、SetMenuItemBitmaps Function

    Associates the specified bitmap with a menu item. Whether the menu item is selected or clear, the system displays the appropriate bitmap next to the menu item.

    http://msdn.microsoft.com/en-us/library/ms647998%28VS.85%29.aspx

    6、GetCurrentDirectory Function

    Retrieves the current directory for the current process.

    SetCurrentDirectory

    7、lstrcpyn Function

    Copies a specified number of characters from a source string into a buffer.

    Warning Do not use. Consider using StringCchCopy instead. See Remarks.

    8、CStdioFile::ReadString

    Reads text data into a buffer, up to a limit of nMax–1 characters, from the file associated with the CStdioFile object.

    Reading is stopped by the first newline character. If, in that case, fewer than nMax–1 characters have been read, a newline character is stored in the buffer. A null character ('\0') is appended in either case.

    http://msdn.microsoft.com/en-us/library/x5t0zfyf%28VS.80%29.aspx

    9、FindFirstFile Function

    Searches a directory for a file or subdirectory with a name that matches a specific name (or partial name if wildcards are used).

    To specify additional attributes to use in a search, use the FindFirstFileEx function.

    To perform this operation as a transacted operation, use the FindFirstFileTransacted function.

    http://msdn.microsoft.com/en-us/library/aa364418%28VS.85%29.aspx

    10、SHGetPathFromIDList Function

    Converts an item identifier list to a file system path.

    http://msdn.microsoft.com/en-us/library/bb762194%28VS.85%29.aspx

    11、GetTopWindow Function

    Examines the Z order of the child windows associated with the specified parent window and retrieves a handle to the child window at the top of the Z order.

    http://msdn.microsoft.com/en-us/library/ms633514%28VS.85%29.aspx

    12、HIWORD Macro

    Retrieves the high-order word from the specified 32-bit value.

    http://msdn.microsoft.com/en-us/library/ms632657%28VS.85%29.aspx

    13、SHGetMalloc

    Retrieves a pointer to the shell's IMalloc interface

    12、FindNextFile

    Continues a file search from a previous call to the FindFirstFile or FindFirstFileEx function.

    http://msdn.microsoft.com/en-us/library/aa364428%28VS.85%29.aspx

     

    代码
         
    1 void CShellExtentionToVirC::RecursiveObtainDirFiles(WCHAR *lpPath) 2 { 3 WCHAR szFind[MAX_PATH]; 4 WCHAR szFile[MAX_PATH]; 5 WIN32_FIND_DATA FindFileData; 6 7 wcscpy(szFind, lpPath); 8 wcscat(szFind, TEXT("\\*.*")); 9 10 HANDLE hFind=::FindFirstFile((LPCWSTR)szFind, &FindFileData); 11  if (INVALID_HANDLE_VALUE == hFind) 12 return; 13 14 while(TRUE) 15 { 16 if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 17 { 18 if(FindFileData.cFileName[0] != L'.') 19 { 20 wcscpy(szFile, lpPath); 21 wcscat(szFile, TEXT("\\")); 22 wcscat(szFile, FindFileData.cFileName); 23 RecursiveObtainDirFiles(szFile); 24 } 25 } 26 else 27 { 28 std::wcout << FindFileData.cFileName << std::endl; 29 } 30 if (!FindNextFile(hFind, &FindFileData)) 31 break; 32 } 33 FindClose(hFind); 34 } 35 36 37 HRESULT CShellExtentionToVirC::InvokeCommand(LPCMINVOKECOMMANDINFO pInfo) 38 { 39 // If lpVerb really points to a string, ignore this function call and bail out. 40 if ( 0 != HIWORD( pInfo->lpVerb )) 41 return E_INVALIDARG; 42 43 // Check that lpVerb is one of our commands (0 or 1) 44 45 switch ( LOWORD( pInfo->lpVerb )) 46 { 47 case 0: 48 case 1: 49 { 50 TCHAR szMsg[MAX_PATH + 32]; 51 wsprintf( szMsg, _T("Only a test")); 52 MessageBox( pInfo->hwnd, szMsg, _T("VirCS"),MB_ICONINFORMATION ); 53 54 strCurrentDirectory = L"C:\\Intel\\Logs"; 55 RecursiveObtainDirFiles(const_cast<WCHAR*>(strCurrentDirectory.data())); 56 return S_OK; 57 } 58 break; 59 60 default: 61 return E_INVALIDARG; 62 break; 63 } 64 }

    参考

    [1] http://www.codeproject.com/KB/shell/shellextguideindex.aspx

    关 于Shell Extension,CodeProject讲解了更多,如drag and drop handler(用右键拖拽时显示的菜单),property sheet handler(在属性页中显示的菜单),icon handler(不同类型文件图标不同)等。

    [2] 相关文档下载地址

    http://download.csdn.net/source/2878021

    [3] http://www.codeproject.com/KB/shell/wildcardselect.aspx

    [4] http://msdn.microsoft.com/en-us/library/bb776426%28VS.85%29.aspx

    [5] http://blog.csdn.net/luckyboy101/archive/2009/11/25/4866408.aspx

    [6] http://www.programbbs.com/

    [7] http://www.cnblogs.com/MaxWoods/archive/2010/06/23/1764036.html

    [8] http://www.cnblogs.com/MaxWoods/archive/2010/06/23/1764034.html

    [9] http://www.cnblogs.com/lemony/archive/2007/04/16/715833.html

    [10] http://www.cnblogs.com/lemony/archive/2007/04/16/715833.html

    [11] Dino Esposito's great book Visual C++ Windows Shell Programming (ISBN 1861001843)

     

     

    展开全文
  • (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~) 接上一节:(C#)Windows Shell 外壳编程系列6 - 执行从本节起,我所要讲述的是对 ...
    (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单

    (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)

     

    接上一节:(C#)Windows Shell 外壳编程系列6 - 执行

    从本节起,我所要讲述的是对 Windows 系统的“Shell 扩展”。“Shell 扩展”从字面上分两个部分:Shell 与 Extension。Shell 指 Windows Explorer,而Extension 则指由你编写的当某一预先约定好的事件(如在以. doc 为后缀的文件图标上单击右键)发生时由 Explorer 调用执行的代码。因此一个“Shell 扩展”就是一个为 Explorer 添加功能的 COM 对象。 


    “Shell 扩展”有很多种类型,每种类型都在各自不同的事件发生时被调用运行,但也有一些扩展的类型和调用情形是非常相似的。
     

    类型 何时被调用应该作些什么
    Context menu
    扩展处理器
    用户右键单击文件或文件夹对象时,
    或在一个文件夹窗口中的背景处单击右键时(要求
    shell版本为4.71+
    添加菜单项到上下文菜单中
    Property sheet
    扩展处理器
    要显示一个文件对象的属性框时添加定制属性页到属性表中
    Drag and drop
    扩展处理器
    用户用右键拖放文件对象到文件夹窗口或桌面时添加菜单项到上下文菜单中
    Drop 扩展处理器 用户拖动Shell对象并将它放到一个文件对象上时任何想要的操作
    QueryInfo扩展处理器 (需要shell版本 4.71+) 用户将鼠标盘旋于文件或其他Shell对象的图标上时 返回一个浏览器用于显示在提示框中的字符串

     

    现在你可能想知道“Shell 扩展”到底是什么样的,不过我还是乐意把我后面所实现的技术效果直接展示出来。以下三副图片分别代表了三种“Shell 扩展”:

     

    (1)实现类似 WinRAR 的右键菜单

     

    (2)根据文本大小,显示不同的 TXT 文件图标

     

    (3)当鼠标移动到 TXT 文件图标上的时候,显示内容预览。

     

    好,废话不多说了,赶紧进入今天要讲述的内容: ContextMenu 注册文件右键菜单。

     

    对于 WinRAR 所实现的效果,其实叫做上下文菜单。例如我们把扩展关联到 .TXT 文件,当用户右键单击文本文件对象时扩展就会被调用,然后向系统菜单增加菜单项,并响应相应的命令。由此可见,基本上每种 Shell 扩展,都需要做一些几乎一样的事情。

     

    初始化接口
    当我们的shell扩展被加载时,Explorer 将调用我们所实现的COM对象的 QueryInterface() 函数以取得一个 IShellExtInit 接口指针。该接口仅有一个方法 Initialize(),其函数原型为:  

     


    [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), GuidAttribute("000214e8-0000-0000-c000-000000000046")]
    public interface IShellExtInit
    {
        [PreserveSig()]
        
    int Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID);
    }

     

    Explorer 使用该方法传递给我们各种各样的信息.
    pidlFolder 是用户所选择操作的文件所在的文件夹的 PIDL 变量. (一个 PIDL [指向ID 列表的指针] 是一个数据结构,它唯一地标识了在Shell命名空间的任何对象, 一个Shell命名空间中的对象可以是也可以不是真实的文件系统中的对象。)
    lpdobj 是一个 IDataObject 接口指针,通过它我们可以获取用户所选择操作的文件名。
    hKeyProgID 是一个HKEY 注册表键变量,可以用它获取我们的DLL的注册数据。

     

    因此我们可以在这个方法中,获取到被右击选择的一个或多个文件/文件夹名。

     


    protected ShellLib.IDataObject m_dataObject = null;
    uint m_hDrop = 0;

    int IShellExtInit.Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID)
    {
        
    try
        {
            m_dataObject 
    = null;
            
    if (lpdobj != (IntPtr)0)
            {
                m_dataObject 
    = (ShellLib.IDataObject)Marshal.GetObjectForIUnknown(lpdobj);
                FORMATETC fmt 
    = new FORMATETC();
                fmt.cfFormat 
    = CLIPFORMAT.CF_HDROP;
                fmt.ptd 
    = 0;
                fmt.dwAspect 
    = DVASPECT.DVASPECT_CONTENT;
                fmt.lindex 
    = -1;
                fmt.tymed 
    = TYMED.TYMED_HGLOBAL;
                STGMEDIUM medium 
    = new STGMEDIUM();
                m_dataObject.GetData(
    ref fmt, ref medium);
                m_hDrop 
    = medium.hGlobal;
            }
        }
        
    catch (Exception)
        {
        }
        
    return S_OK;
    }

     

    IDataObject 是一个接口,包含了一些获取文件名的方法,后面可以用到。

     

    与上下文菜单交互的接口

    一旦 Explorer 初始化了扩展,它就会接着调用 IContextMenu 的方法让我们添加菜单项, 提供状态栏上的提示, 并响应执行用户的选择。IContextMenu 接口定义了以下几个方法:


    [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), GuidAttribute("000214e4-0000-0000-c000-000000000046")]
    public interface IContextMenu
    {
        [PreserveSig()]
        
    int QueryContextMenu(HMenu hmenu, uint iMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags);
        [PreserveSig()]
        
    void InvokeCommand(IntPtr pici);
        [PreserveSig()]
        
    void GetCommandString(uint idcmd, GCS uflags, uint reserved, IntPtr commandstring, int cchMax);
    }

     

    第一个是 QueryContextMenu(), 它让我们可以修改上下文菜单。其参数:

    hmenu 上下文菜单句柄。
    uMenuIndex 是我们应该添加菜单项的起始位置。
    uidFirstCmduidLastCmd 是我们可以使用的菜单命令ID值的范围。
    uFlags 标识了Explorer 调用 QueryContextMenu() 的原因。

     

    我们调用该方法,为上下文菜单增加几个菜单项:

     


    int IContextMenu.QueryContextMenu(HMenu hMenu, uint iMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags)
    {
        
    int id = 0;
        
    if ((uFlags & (CMF.CMF_VERBSONLY | CMF.CMF_DEFAULTONLY | CMF.CMF_NOVERBS)) == 0 ||
            (uFlags 
    & CMF.CMF_EXPLORE) != 0)
        {
            
    //创建子菜单
            HMenu submenu = ShellLib.Helpers.CreatePopupMenu();
            Helpers.AppendMenu(submenu, MFMENU.MF_STRING, 
    new IntPtr(idCmdFirst + id++), "复制路径(&C)");
            Helpers.AppendMenu(submenu, MFMENU.MF_STRING, 
    new IntPtr(idCmdFirst + id++), "复制文本内容(&T)");
            Helpers.AppendMenu(submenu, MFMENU.MF_STRING, 
    new IntPtr(idCmdFirst + id++), "柠檬的博客(&L)");

            
    //将子菜单插入到上下文菜单中
            Helpers.InsertMenu(hMenu, 1, MFMENU.MF_BYPOSITION | MFMENU.MF_POPUP, submenu.handle, "MyContextMenu(&Y)");

            
    //为菜单增加图标
            Bitmap bpCopy = Resource1.copy;
            Helpers.SetMenuItemBitmaps(submenu, 
    0, MFMENU.MF_BYPOSITION, bpCopy.GetHbitmap(), bpCopy.GetHbitmap());
            Helpers.SetMenuItemBitmaps(submenu, 
    1, MFMENU.MF_BYPOSITION, bpCopy.GetHbitmap(), bpCopy.GetHbitmap());
            Bitmap bpHome 
    = Resource1.home;
            Helpers.SetMenuItemBitmaps(submenu, 
    2, MFMENU.MF_BYPOSITION, bpHome.GetHbitmap(), bpHome.GetHbitmap());
        }
        
    return id;
    }

     

    在状态栏上显示提示帮助

    下一个要被调用的IContextMenu 方法是 GetCommandString().。如果用户是在浏览器窗口中右击文本文件,或选中一个文本文件后单击文件菜单时,状态栏会显示提示帮助。我们的 GetCommandString() 函数将返回一个帮助字符串供浏览器显示。

     


    void IContextMenu.GetCommandString(uint idcmd, GCS uflags, uint reserved, IntPtr commandstring, int cchMax)
    {
        
    string tip = "";

        
    switch (uflags)
        {
            
    case GCS.VERB:
                
    break;
            
    case GCS.HELPTEXTW:
                
    switch (idcmd)
                {
                    
    case 0:
                        tip 
    = "把选中的文件/文件夹的全路径复制到剪切板";
                        
    break;
                    
    case 1:
                        tip 
    = "把选中的 TXT 文本内容复制到剪切板";
                        
    break;
                    
    case 2:
                        tip 
    = "访问柠檬的博客 http://lemony.cnblogs.com";
                        
    break;
                    
    default:
                        
    break;
                }
                
    if (!string.IsNullOrEmpty(tip))
                {
                    
    byte[] data = new byte[cchMax * 2];
                    Encoding.Unicode.GetBytes(tip, 
    0, tip.Length, data, 0);
                    Marshal.Copy(data, 
    0, commandstring, data.Length);
                }
                
    break;
        }
    }

     

    执行用户的选择

    IContextMenu 接口的最后一个方法是 InvokeCommand()。当用户点击我们添加的菜单项时该方法将被调用。其参数:

    CMINVOKECOMMANDINFO 结构带有大量的信息, 但我们只关心 lpVerbhwnd 这两个成员。
    lpVerb参数有两个作用 – 它或是可被激发的verb(动作)名, 或是被点击的菜单项的索引值。

    hwnd 是用户激活我们的菜单扩展时所在的浏览器窗口的句柄。

     

    我们可以根据被点击的菜单项索引,来执行相应的操作。

     


    void IContextMenu.InvokeCommand(IntPtr pici)
    {
        INVOKECOMMANDINFO ici 
    = (INVOKECOMMANDINFO)Marshal.PtrToStructure(pici, typeof(ShellLib.INVOKECOMMANDINFO));
        StringBuilder sb 
    = new StringBuilder(1024);
        StringBuilder sbAll 
    = new StringBuilder();
        
    uint nselected;

        
    switch (ici.verb)
        {
            
    case 0:
                
    //复制文件名
                nselected = Helpers.DragQueryFile(m_hDrop, 0xffffffffnull0);
                
    for (uint i = 0; i < nselected; i++)
                {
                    ShellLib.Helpers.DragQueryFile(m_hDrop, i, sb, sb.Capacity 
    + 1);
                    sbAll.Append(sb.ToString() 
    + "/n");
                }
                Clipboard.Clear();
                Clipboard.SetDataObject(sbAll.ToString(), 
    true);
                
    break;
            
    case 1:
                
    //复制文件内容
                nselected = Helpers.DragQueryFile(m_hDrop, 0xffffffffnull0);
                
    for (uint i = 0; i < nselected; i++)
                {
                    ShellLib.Helpers.DragQueryFile(m_hDrop, i, sb, sb.Capacity 
    + 1);
                    StreamReader sr 
    = new StreamReader(sb.ToString(), Encoding.GetEncoding("gb2312"));
                    sbAll.Append(sr.ReadToEnd());
                    sr.Close();
                }
                Clipboard.Clear();
                Clipboard.SetDataObject(sbAll.ToString(), 
    true);
                
    break;
            
    case 2:
                
    //调用浏览器,打开网页
                Process proc = new Process();
                proc.StartInfo.FileName 
    = "IExplore.exe";
                proc.StartInfo.Arguments 
    = "http://lemony.cnblogs.com";
                proc.Start();
                
    break;
            
    default:
                
    break;
        }
    }

     

    注册Shell扩展
    现在我们已经实现了所有需要的COM接口. 可是我们怎样才能让浏览器使用我们的扩展呢?首先,我们要注册动态库。但仅仅这样是不够的,为了告诉浏览器使用我们的扩展, 我们需要在文本文件类型的注册表键下注册扩展。

    (请原谅我未能抽出时间对注册扩展做详细的说明(如果以后有机会会补上),大家可以自行研究)

     


    [System.Runtime.InteropServices.ComRegisterFunctionAttribute()]
    static void RegisterServer(String str1)
    {
        
    try
        {
            
    //注册 DLL
            RegistryKey root;
            RegistryKey rk;
            root 
    = Registry.LocalMachine;
            rk 
    = root.OpenSubKey("Software//Microsoft//Windows//CurrentVersion//Shell Extensions//Approved"true);
            rk.SetValue(GUID, KEYNAME);
            rk.Close();
            root.Close();

            
    //注册文件
            RegTXT();
        }
        
    catch{
        }
    }

    [System.Runtime.InteropServices.ComUnregisterFunctionAttribute()]
    static void UnregisterServer(String str1)
    {
        
    try
        {
            
    //注销动态库
            RegistryKey root;
            RegistryKey rk;
            root 
    = Registry.LocalMachine;
            rk 
    = root.OpenSubKey("Software//Microsoft//Windows//CurrentVersion//Shell Extensions//Approved"true);
            rk.DeleteValue(GUID);
            rk.Close();
            root.Close();

            
    //注销文件
            UnRegTXT();
        }
        
    catch
        {
        }
    }

    private static void RegTXT()
    {
        RegistryKey root;
        RegistryKey rk;

        root 
    = Registry.ClassesRoot;
        rk 
    = root.OpenSubKey(".txt");
        
    string txtclass = (string)rk.GetValue("");
        
    if (string.IsNullOrEmpty(txtclass))
        {
            txtclass 
    = "TXT";
            rk.SetValue(
    "", txtclass);

        }
        rk.Close();

        rk 
    = root.CreateSubKey(txtclass + "//shellex//ContextMenuHandlers//" + KEYNAME);
        rk.SetValue(
    "", GUID);
        rk.Close();

        rk 
    = root.CreateSubKey(txtclass + "//shellex//IconHandler");
        rk.SetValue(
    "", GUID);
        rk.Close();

        rk 
    = root.CreateSubKey(txtclass + "//shellex//{00021500-0000-0000-C000-000000000046}");
        rk.SetValue(
    "", GUID);
        rk.Close();
    }

    private static void UnRegTXT()
    {
        RegistryKey root;
        RegistryKey rk;

        root 
    = Registry.ClassesRoot;
        rk 
    = root.OpenSubKey(".txt");
        rk.Close();
        
    string txtclass = (string)rk.GetValue("");
        
    if (!string.IsNullOrEmpty(txtclass))
        {
            root.DeleteSubKey(txtclass 
    + "//shellex//ContextMenuHandlers//" + KEYNAME);
            root.DeleteSubKey(txtclass 
    + "//shellex//IconHandler");
            root.DeleteSubKey(txtclass 
    + "//shellex//{00021500-0000-0000-C000-000000000046}");
        }
    }


    注册动态库

    .NET 开发的动态库有些特别,需要在 .NET SDK 中注册

    regasm MyContextMenu.dll /CodeBase
    反注册则是:regasm /unregister MyContextMenu.dll /CodeBase

     

    代码:http://files.cnblogs.com/lemony/MyContextMenu.rar

     

    关于代码:代码里面还包括了图标扩展和提示扩展的代码,如果有兴趣,可自行阅读。

     

    题外话:还有相当多的关于 Shell 扩展的内容无法一一说明,如果有机会,以后会尽量补上。或大家查阅网上的“Windows Shell扩展编程完全指南”(虽然是VC版的,但内容相当丰富)

     

    Tag标签: Shell, ContextMenu
    展开全文
  • windows xp基础知识

    2011-08-05 12:13:48
    菜单约定 可操作菜单选项与不可操作菜单选项 “…”的含义 此菜单项有相应的对话框 组合键的使用 ctrl+c(复制) ctrl+v(粘贴) ctrl+x(剪切) 三角形标记的含义 此菜单项有子菜单 分组线 将功能相似的菜单项...
  • windows 程序设计

    2011-07-24 21:16:30
    从程序写作者的角度看,一致的使用者接口来自于Windows建构菜单和对话框的内置程序。所有菜单都有同样的键盘和鼠标接口,因为这项工作是由Windows处理,而不是由应用程序处理。 为便于多个程序的使用,以及这些程序...
  • C#程序设计语言课程内容 Windows 窗体 学习完本节后你将能够 描述Windows窗体 创建Windows窗体 创建窗体的主菜单 学习完本节后你将能够 创建主菜单 添加菜单项 处理菜单项事件 使用窗体的主菜单菜单的概念 主菜单...
  • 前面我们已经强调过了,windows图形界面的应用程序的入口函数名必须为WinMain ,而且调用约定必须为_stdcall。因为入口函数是由操作系统来调用的。既然如此,该函数的四个参数也是由操作系统来赋值...
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow) 

    前面我们已经强调过了,windows图形界面的应用程序的入口函数名必须为WinMain ,而且调用约定必须为_stdcall。因为入口函数是由操作系统来调用的。既然如此,该函数的四个参数也是由操作系统来赋值的。

    下面我们来解释一下这四个参数的含义:

    1. HINSTANCE hInstance

    应用程序实例句柄,当用户运行应用程序时,就会创建一个应用程序实例句柄。该句柄的本质是进程句柄。一个应用程序运行时,操作系统就会创建一个进程,一个主线程(线程函数为WinMain )。进程创建的同时会返回一个进程句柄,操作系统以及其他的应用程序可以通过进程句柄来管理进程,如:终止进程的执行,或通过管道向进程传递数据等。

         2.HINSTANCE hPrevInstance

    前一个实例句柄,一个应用程序可以运行很多次,每次都会创建一个实例句柄。

         3. PSTR szCmdLine

    命令行参数,当我们通过命令方式启动一个应用程序时,可以在命令中输入相应的参数。

    比如DOS命令中的CD命令:C:\>CD  WINDOWS,其中 WINDOWS就是该命令的参数。

    展开全文
  • 权限菜单设计

    千次阅读 2017-02-24 16:17:05
    关于权限菜单的设计 http://blog.csdn.net/bearyb1982/article/details/2448301 权限设计(初稿)   1. 前言:   权限管理往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断...

    关于权限菜单的设计


    http://blog.csdn.net/bearyb1982/article/details/2448301


    权限设计(初稿)   
      1. 前言:   
      权限管理往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断“Who对What(Which)进行How的操作”的逻辑表达式是否为真。针对不同的应用,需要根据项目的实际情况和具体架构,在维护性、灵活性、完整性等N多个方案之间比较权衡,选择符合的方案。   
      2. 目标:   
      直观,因为系统最终会由最终用户来维护,权限分配的直观和容易理解,显得比较重要简单,包括概念数量上的简单和意义上的简单还有功能上的简单。想用一个权限系统解决所有的权限问题是不现实的。设计中将常常变化的“定制”特点比较强的部分判断为业务逻辑,而将常常相同的“通用”特点比较强的部分判断为权限逻辑就是基于这样的思路。   
      3. 现状:   
      对于在企业环境中的访问控制方法,一般有三种:   
      1.自主型访问控制方法:目前在我国的大多数的信息系统中的访问控制模块中基本是借助于自主型访问控制方法中的访问控制列表(ACLs)。   
      2.强制型访问控制方法:用于多层次安全级别的军事应用。   
      3.基于角色的访问控制方法(RBAC):是目前公认的解决大型企业的统一资源访问控制的有效方法。其显著的两大特征是:1.减小授权管理的复杂性,降低管理开销。2.灵活地支持企业的安全策略,并对企业的变化有很大的伸缩性。   
      4. 名词:   
      粗粒度:表示类别级,即仅考虑对象的类别(the   type   of   object),不考虑对象的某个特定实例。比如,用户管理中,创建、删除,对所有的用户都一视同仁,并不区分操作的具体对象实例。   
      细粒度:表示实例级,即需要考虑具体对象的实例(the   instance   of   object),当然,细粒度是在考虑粗粒度的对象类别之后才再考虑特定实例。比如,合同管理中,列表、删除,需要区分该合同实例是否为当前用户所创建。   
      5. 原则:   
      权限逻辑配合业务逻辑。即权限系统以为业务逻辑提供服务为目标。相当多细粒度的权限问题因其极其独特而不具通用意义,它们也能被理解为是“业务逻辑”的一部分。比如,要求:“合同资源只能被它的创建者删除,与创建者同组的用户可以修改,所有的用户能够浏览”。这既可以认为是一个细粒度的权限问题,也可以认为是一个业务逻辑问题。在这里它是业务逻辑问题,在整个权限系统的架构设计之中不予过多考虑。当然,权限系统的架构也必须要能支持这样的控制判断。或者说,系统提供足够多但不是完全的控制能力。即,设计原则归结为:“系统只提供粗粒度的权限,细粒度的权限被认为是业务逻辑的职责”。   
      权限公式:Who+What(Which)+How   的问题,在这里我们实现What和部分Which的权限问题(粗粒度和细粒度相合,到一定的程度),其他的权限问题留给业务逻辑解决   
      6. 概念:   
      Who:权限的拥用者或主体(Principal(负责人)、User、Group、Role、Actor等等)   
      What:权限针对的对象或资源(Resource、Class)。   
      How:具体的权限(Privilege,   正向授权与负向授权)。   
      Role:是角色,拥有一定数量的权限。   
      Operator:操作。表明对What的How   操作。   
      7. 解释:   
      User:与   Role   相关,用户仅仅是纯粹的用户,权限是被分离出去了的。User是不能与   Privilege   直接相关的,User   要拥有对某种资源的权限,必须通过Role去关联。解决   Who   的问题。   
      Resource:就是系统的资源,比如部门新闻,文档等各种可以被提供给用户访问的对象。   
      Privilege:是Resource   Related的权限。就是指,这个权限是绑定在特定的资源实例上的。比如说部门新闻的发布权限,叫做"部门新闻发布权限"。这就表明,该Privilege是一个发布权限,而且是针对部门新闻这种资源的一种发布权限。Privilege是由Creator在做开发时就确定的。Privilege   如"删除"   是一个抽象的名词,当它不与任何具体的   Object   或   Resource   绑定在一起时是没有任何意义的。拿新闻发布来说,发布是一种权限,但是只说发布它是毫无意义的。因为不知道发布可以操作的对象是什么。只有当发布与新闻结合在一起时,才会产生真正的   Privilege。这就是   Privilege   Instance。   
      Role:是粗粒度和细粒度(业务逻辑)的接口,一个基于粗粒度控制的权限框架软件,对外的接口应该是Role,具体业务实现可以直接继承或拓展丰富Role的内容,Role不是如同User或Group的具体实体,它是接口概念,抽象的通称。   
      Operator的定义包括了Resource   Type和Method概念。即,What和How的概念。之所以将What和How绑定在一起作为一个Operator概念而不是分开建模再建立关联,这是因为很多的How对于某What才有意义。比如,发布操作对新闻对象才有意义,对用户对象则没有意义。   
      8. 思想:   
      权限系统的核心由以下三部分构成:1.创造权限,2.分配权限,3.使用权限,然后,系统各部分的主要参与者对照如下:1.创造权限   -   Creator创造,2.分配权限   -   Administrator   分配,3.使用权限   -   User:   
      1.   Creator   创造   Privilege,   Creator   在设计和实现系统时会划分,一个子系统或称为模块,应该有哪些权限。这里完成的是   Privilege   与   Resource   的对象声明,并没有真正将   Privilege   与具体Resource   实例联系在一起,形成Operator。   
      2.   Administrator   指定   Privilege   与   Resource   Instance   的关联。在这一步,   权限真正与资源实例联系到了一起,   产生了Operator(Privilege   Instance)。Administrator利用Operator这个基本元素,来创造他理想中的权限模型。如,创建角色,创建用户组,给用户组分配用户,将用户组与角色关联等等...这些操作都是由   Administrator   来完成的。   
      3.   User   使用   Administrator   分配给的权限去使用各个子系统。Administrator   是用户,在他的心目中有一个比较适合他管理和维护的权限模型。于是,程序员只要回答一个问题,就是什么权限可以访问什么资源,也就是前面说的   Operator。程序员提供   Operator   就意味着给系统穿上了盔甲。Administrator   就可以按照他的意愿来建立他所希望的权限框架可以自行增加,删除,管理Resource和Privilege之间关系。可以自行设定用户User和角色Role的对应关系。(如果将   Creator看作是   Basic   的发明者,   Administrator   就是   Basic   的使用者,他可以做一些脚本式的编程)   Operator是这个系统中最关键的部分,它是一个纽带,一个系在Programmer,Administrator,User之间的纽带。

    但凡涉及多用户不同权限的网络或者单机程序,都会有权限管理的问题,比较突出的是MIS系统。     
        
      下面我要说的是MIS系统权限管理的数据库设计及实现,当然,这些思路也可以推广开来应用,比如说在BBS中用来管理不同级别的用户权限。     
        
      权限设计通常包括数据库设计、应用程序接口(API)设计、程序实现三个部分。     
        
      这三个部分相互依存,密不可分,要实现完善的权限管理体系,必须考虑到每一个环节可行性与复杂程度甚至执行效率。     
        
      我们将权限分类,首先是针对数据存取的权限,通常有录入、浏览、修改、删除四种,其次是功能,它可以包括例如统计等所有非直接数据存取操作,另外,我们还可能对一些关键数据表某些字段的存取进行限制。除此,我想不出还有另外种类的权限类别。     
        
      完善的权限设计应该具有充分的可扩展性,也就是说,系统增加了新的其它功能不应该对整个权限管理体系带来较大的变化,要达到这个目的,首先是数据库设计合理,其次是应用程序接口规范。     
        
      我们先讨论数据库设计。通常我们使用关系数据库,这里不讨论基于Lotus产品的权限管理。     
        
      权限表及相关内容大体可以用六个表来描述,如下:     
      1   角色(即用户组)表:包括三个字段,ID,角色名,对该角色的描述;     
      2   用户表:包括三个或以上字段,ID,用户名,对该用户的描述,其它(如地址、电话等信息);     
      3   角色-用户对应表:该表记录用户与角色之间的对应关系,一个用户可以隶属于多个角色,一个角色组也可拥有多个用户。包括三个字段,ID,角色ID,用户ID;     
      4   限制内容列表:该表记录所有需要加以权限区分限制的数据表、功能和字段等内容及其描述,包括三个字段,ID,名称,描述;     
      5   权限列表:该表记录所有要加以控制的权限,如录入、修改、删除、执行等,也包括三个字段,ID,名称,描述;     
      6   权限-角色-用户对应表:一般情况下,我们对角色/用户所拥有的权限做如下规定,角色拥有明令允许的权限,其它一律禁止,用户继承所属角色的全部权限,在此范围内的权限除明令禁止外全部允许,范围外权限除明令允许外全部禁止。该表的设计是权限管理的重点,设计的思路也很多,可以说各有千秋,不能生搬硬套说某种方法好。对此,我的看法是就个人情况,找自己觉得合适能解决问题的用。     
        
      先说第一种也是最容易理解的方法,设计五个字段:ID,限制内容ID,权限ID,角色/用户类型(布尔型字段,用来描述一条记录记录的是角色权限还是用户权限),角色/用户ID,权限类型(布尔型字段,用来描述一条记录表示允许还是禁止)     
        
      好了,有这六个表,根据表六,我们就可以知道某个角色/用户到底拥有/禁止某种权限。     
        
      或者说,这么设计已经足够了,我们完全实现了所需要的功能:可以对角色和用户分别进行权限定制,也具有相当的可扩展性,比如说增加了新功能,我们只需要添加一条或者几条记录就可以,同时应用程序接口也无须改动,具有相当的可行性。但是,在程序实现的过程中,我们发现,使用这种方法并不是十分科学,例如浏览某个用户所拥有的权限时,需要对数据库进行多次(甚至是递归)查询,极不方便。于是我们需要想其它的办法。使用过Unix系统的人们都知道,Unix文件系统将对文件的操作权限分为三种:读、写和执行,分别用1、2、4三个代码标识,对用户同时具有读写权限的文件被记录为3,即1+2。我们也可以用类似的办法来解决这个问题。初步的想法是修改权限列表,加入一个字段:标识码,例如,我们可以将录入权限标识为1,浏览权限标识为2,修改权限标识为4,删除权限标识为8,执行权限标识为16,这样,我们通过权限累加的办法就可以轻易的将原本要分为几条记录描述的权限放在一起了,例如,假定某用户ID为1,库存表对应的限制内容ID为2,同时规定角色类型为0、用户类型为1,我们就可以将该用户具有录入、浏览、修改、删除库存表的权限描述为:2,15,1,1。     
        
      确实很简单,不是吗?甚至还有更过激的办法,将限制内容列表也加上一列,定义好标识码,这样,我们甚至可以用简单的一条记录描述某个用户具有的对全部内容所具有的全部权限了。当然,这样做的前提是限制内容数量比较小,不然,呵呵,2的n次方递增起来可是数量惊人,不容易解析的。     
        
      从表面上看,上述方法足以达到实现功能、简化数据库设计及实现的复杂度这个目的,但这样做有个弊端,我们所涉及的权限列表不是相互独立而是互相依赖的,比如说修改权限,其实是包含浏览权限的,例如,我们可能只是简单的设置用户对库存表存取的权限值为录入+修改+删除(1+4+8=13),但事实上,该用户具有(1+2+4+8=15)的权限,也就是说,在这种方案中,13=15。于是当我们调用API询问某用户是否具有浏览权限时,就必须判断该用户是否具有对该数据表的修改权限,因此,如果不能在程序中固化权限之间的包含关系,就不能利用应用程序接口简单的做出判断。但这与我们的目的“充分的可扩展性”矛盾。     
        
      这个问题如何解决?我想到了另外一种设置标识码的方法,那就是利用素数。我们不妨将录入、浏览、修改、删除、执行的基本标志码定为2,3,5,7,11,当遇到权限互相包含的时候,我们将它的标识码设定为两个(或多个)基本标志码的乘积,例如,可以将“修改”功能的标志码定为3*5=15,然后将所有的权限相乘,就得到了我们需要的最终权限标识值。这样,我们在询问用户是否具有某项权限的时候,只需要将最终的值分解成质因子,例如,我们可以定义一个用户具有录入+修改+删除库存表的权限为   2*15*7=2*3*5*7,即表示,该用户具有了对库存表录入+浏览+修改+删除权限。     
        
      当然,对权限列表我们使用上述方法的前提是权限列表记录条数不会太多并且关系不是十分复杂,否则,光是解析权限代码就要机器忽悠半宿:)  

    通用权限管理设计篇  


    http://sxiaomais.blog.163.com/blog/static/31741203200811102630406/

    一.引言

           因为做过的一些系统的权限管理的功能虽然在逐步完善,但总有些不尽人意的地方,总想抽个时间来更好的思考一下权限系统的设计。

           权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

    二.设计目标

           设计一个灵活、通用、方便的权限管理系统。

           在这个系统中,我们需要对系统的所有资源进行权限控制,那么系统中的资源包括哪些呢?我们可以把这些资源简单概括为静态资源(功能操作、数据列)和动态资源(数据),也分别称为对象资源和数据资源,后者是我们在系统设计与实现中的叫法。

    系统的目标就是对应用系统的所有对象资源和数据资源进行权限控制,比如应用系统的功能菜单、各个界面的按钮、数据显示的列以及各种行级数据进行权限的操控。

    三.相关对象及其关系

           大概理清了一下权限系统的相关概念,如下所示:

    1.       权限

    系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子

    系统管理

            用户管理

                   查看用户

                    新增用户

                         修改用户

                         删除用户

           对于上面的每个权限,又存在两种情况,一个是只是可访问,另一种是可授权,例如对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

    2.       用户

    应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

    3.       角色

    为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

    4.       组

    为了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有自己的角色信息,例如普通群、高级群等。

    针对上面提出的四种类型的对象,让我们通过图来看看他们之间的关系。

    通用权限管理设计篇 - sxiaomais - 小麦

     

        有上图中可以看出,这四者的关系很复杂,而实际的情况比这个图还要复杂,权限、角色、组都具有上下级关系,权限管理是应用系统中比较棘手的问题,要设计一个通用的权限管理系统,工作量也着实不小。

    当然对于有些项目,权限问题并不是那么复杂。有的只需要牵涉到权限和用户两种类型的对象,只需要给用户分配权限即可。

    在另一些情况中,引入了角色对象,例如基于角色的权限系统, 只需要给角色分配权限,用户都隶属于角色,不需要单独为用户分配角色信息。

    通用权限管理设计篇(二)——数据库设计

       国庆前整的通用权限设计的数据库初步设计部分,现在贴上来。

    理清了对象关系之后,让我们接着来进行数据库的设计。在数据库建模时,对于N对N的

    关系,一般需要加入一个关联表来表示关联的两者的关系。初步估计一下,本系统至少需要十张表,分别为:权限表、用户表、角色表、组表、用户权限关联表、用

    户角色关联表、角色权限关联表、组权限关联表、组角色关联表、用户属组关联表。当然还可能引出一些相关的表。下面让我们在PowerDesigner中画出各表吧。

           各表及其关系如下:

    通用权限管理设计篇 - sxiaomais - 小麦

          

    1.       用户表

    用户表(TUser)

    字段名称

    字段

    类型

    备注

    记录标识

    tu_id

    bigint

    pk, not null

    所属组织

    to_id

    bigint

    fk, not null

    登录帐号

    login_name

    varchar(64)

    not null

    用户密码

    password

    varchar(64)

    not null

    用户姓名

    vsername

    varchar(64)

    not null

    手机号

    mobile

    varchar(20)

    电子邮箱

    email

    varchar(64)

    创建时间

    gen_time

    datetime

    not null

    登录时间

    login_time

    datetime

    上次登录时间

    last_login_time

    datetime

    登录次数

    count

    bigint

    not null

    2.       角色表

    角色表(TRole)

    字段名称

    字段

    类型

    备注

    角色ID

    tr_id

    bigint

    pk, not null

    父级角色ID

    parent_tr_id

    bigint

    not null

    角色名称

    role_name

    varchar(64)

    not null

    创建时间

    gen_time

    datetime

    not null

    角色描述

    description

    varchar(200)

    3.       权限表

    权限表(TRight)

    字段名称

    字段

    类型

    备注

    权限ID

    tr_id

    bigint

    pk, not null

    父权限

    parent_tr_id

    bigint

    not null

    权限名称

    right_name

    varchar(64)

    not null

    权限描述

    description

    varchar(200)

    4.       组表

    组表(TGroup)

    字段名称

    字段

    类型

    备注

    组ID

    tg_id

    bigint

    pk, not null

    组名称

    group_name

    varchar(64)

    not null

    父组

    parent_tg_id

    bigint

    not null

    创建时间

    gen_time

    datetime

    not null

    组描述

    description

    varchar(200)

    5.       角色权限表

    角色权限表(TRoleRightRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    trr_id

    bigint

    pk, not null

    角色

    Role_id

    bigint

    fk, not null

    权限

    right_id

    bigint

    fk, not null

    权限类型

    right_type

    int

    not null(0:可访问,1:可授权)

    6.       组权限表

    组权限表(TGroupRightRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    tgr_id

    bigint

    pk, not null

    tg_id

    bigint

    fk, not null

    权限

    tr_id

    bigint

    fk, not null

    权限类型

    right_type

    int

    not null(0:可访问,1:可授权)

    7.       组角色表

    组角色表(TGroupRoleRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    tgr_id

    bigint

    pk, not null

    tg_id

    bigint

    fk, not null

    角色

    tr_id

    bigint

    pk, not null

    8.       用户权限表

    用户权限表(TUserRightRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    tur_id

    bigint

    pk, not null

    用户

    tu_id

    bigint

    fk, not null

    权限

    tr_id

    bigint

    fk, not null

    权限类型

    right_type

    int

    not null(0:可访问,1:可授权)

    9.       用户角色表

    用户角色表(TUserRoleRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    tur_id

    bigint

    pk, not null

    用户

    tu_id

    bigint

    fk, not null

    角色

    tr_id

    bigint

    fk, not null

    10.   用户组表

    用户组表(TUserGroupRelation)

    字段名称

    字段

    类型

    备注

    记录标识

    tug_id

    bigint

    pk, not null

    用户

    tu_id

    bigint

    fk, not null

    tg_id

    bigint

    fk, not null

    11.   组织表

    组织表(TOrganization)

    字段名称

    字段

    类型

    备注

    组织id

    to_id

    bigint

    pk, not null

    父组

    parent_to_id

    bigint

    not null

    组织名称

    org_name

    varchar(64)

    not null

    创建时间

    gen_time

    datetime

    not null

    组织描述

    description

    varchar(200)

    12.   操作日志表

    操作日志表(TLog)

    字段名称

    字段

    类型

    备注

    日志ID

    log_id

    bigint

    pk, not null

    操作类型

    op_type

    int

    not null

    操作内容

    content

    varchar(200)

    not null

    操作人

    tu_id

    bigint

    fk, not null

    操作时间

    gen_time

    datetime

    not null

    通用权限管理系统设计篇(三)——概要设计说明书

     在前两篇文章中,不少朋友对我的设计提出了异议,认为过于复杂,当然在实际的各种系统的权限管理模块中,并不像这里设计得那么复杂,我以前所做的系统中,

    由只有用户和权限的,有只有用户、权限和角色的,还有一个系统用到了用户、权限、角色、组概念,这个系统是我在思考以前所做系统的权限管理部分中找到的一

    些共性而想到的一个设计方案,当然还会有不少设计不到位的地方,在设计开发过程中会慢慢改进,这个系统权当学习只用,各位朋友的好的建议我都会考虑到设计

    中,感谢各位朋友的支持。

        今天抽时间整了一份概念设计出来,还有一些地方尚未考虑清楚,贴出1.0版,希望各位朋友提出宝贵建议。

        大家也可以点击此处《通用权限管理概要设计说明书》自行下载,这是1.0版本,有些地方可能还会进行部分修改,有兴趣的朋友请关注我的blog。

         

    1.      引言

    1.1 编写目的

    本文档对通用权限管理系统的总体设计、接口设计、界面总体设计、数据结构设计、系统出错处理设计以及系统安全数据进行了说明。

    1.2 背景

    a、 软件系统的名称:通用权限管理系统;

    b、 任务提出者、开发者:谢星星;

    c、 在J2EE的web系统中需要使用权限管理的系统。

    1.3 术语

    本系统:通用权限管理系统;

    SSH:英文全称是Secure Shell。

    1.4 预期读者与阅读建议

    预期读者

    阅读重点

    开发人员

    总体设计、接口设计、数据结构设计、界面总体设计、系统出错处理设计

    设计人员

    总体设计、接口设计、数据结构设计、系统安全设计

    1.5 参考资料

    《通用权限管理系统需求规格说明书》

    《通用权限管理系统数据库设计说明书》

    2.      总体设计

    2.1 设计目标

    权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

    本系统的设计目标是对应用系统的所有资源进行权限控制,比如应用系统的功能菜单、各个界面的按钮控件等进行权限的操控。

    2.2 运行环境

    操作系统:Windows系统操作系统和Linux系列操作系统。

    2.3 网络结构

     通用权限管理系统可采用Java Swing实现,可以在桌面应用和Web应用系统中进行调用。如果需要要适应所有开发语言,可以将其API发布到WEB Service上。暂时用Java Swing实现。

    2.4 总体设计思路和处理流程

    在说明总体设计思路前,我们先说明本系统的相关概念:

    1. 权限资源

    系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子

    系统管理

            用户管理

                   查看用户

                   新增用户

                   修改用户

                   删除用户

    对于上面的每个权限,又存在两种情况,一个是只是可访问,另一种是可授权,例如对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

    2. 用户

    应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

    3. 角色

    为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

    4. 组

    了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、

    权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有

    自己的角色信息,例如普通群、高级群等。

    针对如上提出的四种对象,我们可以整理得出它们之间的关系图,如下所示:

    通用权限管理设计篇 - sxiaomais - 小麦

    总体设计思路是将系统分为组权限管理、角色权限管理、用户权限管理、组织管理和操作日志管理五部分。

    其中组权限管理包括包含用户、所属角色、组权限资源和组总权限资源四部分,某个组的权限信息可用公式表示:组权限 = 所属角色的权限合集 + 组自身的权限。

    角色权限管理包括包含用户、包含组和角色权限三部分,某个角色的权限的计算公式为:角色权限 = 角色自身权限。

    用户权限管理包括所属角色、所属组、用户权限、用户总权限资源和组织管理五部分。某个用户总的权限信息存在如下计算公式:用户权限 = 所属角色权限合集 + 所属组权限合集 + 用户自身权限。

    组织管理即对用户所属的组织进行管理,组织以树形结构展示,组织管理具有组织的增、删、改、查功能。

    操作日志管理用于管理本系统的操作日志。

    注意:因为组和角色都具有上下级关系,所以下级的组或角色的权限只能在自己的直属上级的权限中选择,下级的组或者角色的总的权限都不能大于直属上级的总权限。

    2.5 模块结构设计

    本系统的具有的功能模块结构如下图所示:

    通用权限管理设计篇 - sxiaomais - 小麦

    2.6 尚未解决的问题

    无。

    3.      接口设计(暂略)

    3.1 用户接口(暂略)

    3.2 外部接口(暂略)

    3.3 内部接口(暂略)

    4.      界面总体设计

    本节将阐述用户界面的实现,在此之前对页面元素做如下约定:

    序号

    页面元素

    约定

    1

    按钮

    未选中时:[按钮名称]

    选中时:[按钮名称]

    2

    单选框

    ○ 选项

    3

    复选框

    □ 选项

    4

    下拉框

     [选项,…,] ▽

    5

    文本框

     |________|

    6

    TextArea

     |…………|

    7

    页签

    未选中时:选项名称

     选中时:选项名称

    8

    未选中链接

    链接文字

    9

    选中链接

    链接文字

    10

    说明信息

    说明信息

     

    4.1 组权限管理

    4.1.1包含用户

    组信息

       组1

           组11

           组12

           组…

       组2

           组21

           组22

           组…

     

    所选择组:组1

    [包含用户] [所属角色] [组权限] [总权限]

    [修改]

    用户名   姓名     手机号   最近登录时间 登录次数

    阿蜜果 谢星星 13666666666 2007-10-8    66

    sterning xxx    13555555555 2007-10-8    10 

    ……

    当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该组所包含的用户。

    4.1.2所属角色

    组信息

       组1

           组11

           组12

           组…

       组2

           组21

           组22

           组…

     

    所选择组:组1

    [包含用户] [所属角色] [组权限] [总权限]

    [修改]

    角色ID   角色名称   角色描述

    1          访客       --

       2         初级用户    --

      

    当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该组所属的角色。

    4.1.3组权限

    组信息

       组1

           组11

           组12

           组…

       组2

           组21

           组22

           组…

     

    所选择组:组1

    [包含用户] [所属角色] [组权限] [总权限]

    通用权限管理设计篇 - sxiaomais - 小麦

                    [保存] [取消]

    4.1.4总权限

    组信息

       组1

           组11

           组12

           组…

       组2

           组21

           组22

           组…

     

    所选择组:组1

    [包含用户] [所属角色] [组权限] [总权限]

    通用权限管理设计篇 - sxiaomais - 小麦

                    [保存] [取消]

    通过对已具有的权限取消勾选,或为某权限添加勾选,来修改组的权限信息,点击“保存”按钮保存修改信息。

    4.1.5组管理

           在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

    组信息

       组1

           组11

           组12

           组…

       组2

           组21

           组22

           组…

     

    所选择组:组1

    [包含用户] [所属角色] [组权限] [总权限]

    [修改]

    用户名   姓名     手机号   最近登录时间 登录次数

    阿蜜果 谢星星 13666666666 2007-10-8    66

    sterning xxx    13555555555 2007-10-8    10 

    ……

    4.2 角色权限管理

    4.2.1包含用户

    角色信息

       角色1

           角色11

           角色12

           角色…

       角色2

           角色21

           角色22

           角色…

     

    所选择角色:角色1

    [包含用户] [包含组] [角色权限]

    [修改]

    用户名   姓名     手机号   最近登录时间 登录次数

    阿蜜果 谢星星 13666666666 2007-10-8    66

    sterning xxx    13555555555 2007-10-8    10 

    ……

    当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的用户。

    4.2.2包含组

    角色信息

       角色1

           角色11

           角色12

           角色…

       角色2

           角色21

           角色22

           角色…

     

    所选择角色:角色1

    [包含用户] [包含组] [角色权限]

    [修改]

    组ID   组名称     组描述

    1      xxx1       --

    2       xxx2        -- 

    ……

    当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的组。

    4.2.3角色权限

    角色信息

       角色1

           角色11

           角色12

           角色…

       角色2

           角色21

           角色22

           角色…

     

    所选择角色:角色1

    [包含用户] [包含组] [角色权限]

                     通用权限管理设计篇 - sxiaomais - 小麦

                   [保存] [取消]

    通过对已具有的权限取消勾选,或为某权限添加勾选,来修改角色的权限信息,点击“保存”按钮保存修改信息。

    4.2.4管理角色

           在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

    角色信息

       角色1

           角色11

           角色12

           角色…

       角色2

           角色21

           角色22

           角色…

     

    所选择角色:角色1

    [包含用户] [包含组] [角色权限]

    [修改]

    用户名   姓名     手机号   最近登录时间 登录次数

    阿蜜果 谢星星 13666666666 2007-10-8    66

    sterning xxx    13555555555 2007-10-8    10 

    ……

    4.3 用户权限管理

    4.3.1所属角色

    用户权限信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

    [修改]

    角色ID   角色名称   角色描述

    1          访客       --

       2         初级用户    --

    当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的角色。

    4.3.2所属组

    用户信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

    [修改]

    组ID   组名称     组描述

    1       组1         --

       2       组2         --

    当用户选择“修改”按钮时,弹出组的树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的组。

    4.3.3用户权限

    用户信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

                     通用权限管理设计篇 - sxiaomais - 小麦

                    [保存] [取消]

    通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

    4.3.4总权限

    用户信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

                     通用权限管理设计篇 - sxiaomais - 小麦

                    [保存] [取消]

    通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

    4.3.5用户管理

           当选择了某用户时,点击右键,弹出菜单列表:修改、删除、取消,点击修改和删除按钮可以实现用户的删除和修改功能。

           选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加用户按钮可以实现用户的添加功能。

    用户权限信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

    [修改]

    角色ID   角色名称   角色描述

    1          访客       --

       2         初级用户    --

    4.3.6组织管理

           选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加子组织、删除组织、修改组织按钮可以实现组织的添加、删除和修改功能。

    用户权限信息

    xx公司

       广州分公司

           阿蜜果

           肖xx

           yy…

       北京分公司

           zz1

           zz2

           zz3…

     

    所选择用户:阿蜜果

    [所属角色] [所属组] [用户权限] [总权限]

    [修改]

    角色ID   角色名称   角色描述

    1          访客       --

       2         初级用户    --

    4.4 操作日志管理

    4.4.1查询操作日志

    操作名称:|________|  操作人:|________|

    操作时间从 |________| 到 |________| [查询] [重置] [删除]

    编号    操作名称    操作内容    操作人    操作时间

    1        xx1         --        Amigo    2007-10-8

    2        xx2         --        xxyy     2007-10-8

    输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。

    4.4.2删除操作日志

    操作名称:|________| 操作人:|________|

    操作时间从 |________| 到 |________| [查询] [重置] [删除]

    编号    操作名称    操作内容    操作人    操作时间

    1        xx1       --           Amigo      2007-10-8

    2        xx2       --           xxyy       2007-10-8

    输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。而后点击“删除”按钮,可删除符合查询条件的操作日志。

    5.      数据结构设计

    数据库设计的模型请参见《通用权限管理系统_数据库模型.pdm》。表的说明请参见《通用权限管理系统数据库设计说明书》。

    5.1 设计原则

    5.1.1命名的规范

    数据库中表、主键、外键、索引的命名都以统一的规则,采用大小写敏感的形式,各种对象命名长度不要超过30个字符,这样便于应用系统适应不同的数据库平台。

    5.1.2数据的一致性和完整性

    为了保证数据库的一致性和完整性,往往通过表间关联的方式来尽可能的降低数据的冗余。表间关联是一种强制性措施,建立后,对父表(Parent Table)和子表(Child Table)的插入、更新、删除操作均要占用系统的开销。如果数据冗余低,数据的完整性容易得到保证,但增加了表间连接查询的操作,为了提高系统的响应时间,合理的数据冗余也是必要的。使用规则(Rule)和约束(Check)来防止系统操作人员误输入造成数据的错误是设计人员的另一种常用手段,但是,不必要的规则和约束也会占用系统的不必要开销,需要注意的是,约束对数据的有效性验证要比规则快。所有这些,需要在设计阶段应根据系统操作的类型、频度加以均衡考虑。

    5.2 数据库环境说明

    数据库:MySql5.0

    设计库建模工具:PowerDesigner12.0

    5.3 数据库命名规则

    表名以T开头,外键以FK开头,索引以INDEX开头。

    5.4 逻辑结构

    pdm文件的名称为:《通用权限管理系统_数据库模型》。

    5.5 物理存储

    通过数据库建模工具PowerDesigner12可以将pdm导出为文本文件,将数据库脚本放入文本文件中保存。

    5.6 数据备份和恢复

    数据库需定期备份(每天备份一次),备份文件格式为backup_yyyyMMdd,数据库被破坏时,利用最新的备份文件进行恢复。

    6.      系统出错处理设计

    6.1 出错信息

    错误分类

    子项及其编码

    错误名称

    错误代码

    备注

    数据库错误

    连接

    连接超时

    100001001

    连接断开

    100001002

    数据库本身错误代码

    数据库本身错误代码

    100002+数据库错误代码

    TCP连接错误

    连接

    连接超时

    101001001

    连接断开

    101001002

    其它TCP连接错误(socket自身错误代码)

    101002+ socket错误代码

    配置信息错误

    未配置输入参数

    102001

    未配置输出参数

    102002

    组管理部分自定义错误

    103001——103999

    角色管理部分自定义错误

    104001——104999

    用户管理部分自定义错误

    105001——105999

    操作日志管理

    106001——106999

    6.2 补救措施

    为了当某些故障发生时,对系统进行及时的补救,提供如下补救措施:

    a.后备技术   定期对数据库信息进行备份(每天一次),当数据库因某种原因被破坏时,以最新的数据库脚本进行恢复;。

    7.      系统安全设计

    7.1 数据传输安全性设计

    SSH可以通过将联机的封包加密的技术进行资料的传递; 使用SSH可以把传输的所有数据进行加密,即使有人截获到数据也无法得到有用的信息。同时数据经过压缩,大大地加快了传输的速度。通过SSH的使用,可以确保资料传输比较安全并且传输效率较高。

    7.2 应用系统安全性设计

    操作人的操作信息需要提供操作记录。对系统的异常信息需进行记录,已备以后查看。只有授权用户才能登录系统,对于某个操作,需要具有相应权限才能进行操作。

    7.3 数据存储安全性设计           

    对于用户的密码等敏感信息采用MD5进行加密。



    OA之权限管理

    权限管理自己做完了,但是很多的研究和总结,现在就来总结一下权限管理。

    第一、数据库中主要类:

    主要负责类:用户(User),角色(Role)、资源(module)和操作(Permission)

    衍生类:用户角色(UserRole)和对某个资源的某个操作(ACL)

    第二、ACL的具体理解:

    一条acl授权记录中主要记录了以下信息: 角色、资源和授权 

    授权作为一个int, 每一位是一个操作的权限. 

    假设从右向左, 分别代表CRUD 

    那么, 我们CRUD的代码就应该是0123(也就是移位时要移的位数), 因为我们要进行移位进行认证。

    先看授权与取消授权的代码:

    [java]  view plain copy
    1. public void setPermission(int permission,boolean yes){  
    2.         int temp = 1;  
    3.         temp = temp << permission;  
    4.         if(yes){  
    5.             aclState |= temp;  
    6.         }else{  
    7.             aclState &= ~temp;  
    8.         }  
    9.     }  

    首先, 一个int temp = 1的临时变量, aclState为原始授权状态

    tmp的二进制表示是: 00000000 00000000 00000000 00000001 


    U对应的代码是U, 对应的是2. 


    将tmp左移2位, temp = tmp < < 2; temp变成:00000000 00000000 00000000 00000100 


    假设原始授权是aclState=00000000 00000000 00000000 00001010 


    当变量yes=true时,为授权,将temp与aclState求|运算,因为temp现在只有他要授权的位为1,求或运算后,


    aclState=00000000 00000000 00000000 00001110,这样就授权成功


    当变量yes=false时,为取消授权,先将temp取反,即为11111111 11111111 11111111 11111011,


    现在只有要取消权限的位为0,其余全为1,然后与aclState求&运算,则除了要取消权限的位变0,其余的都不变,


    即aclState=00000000 00000000 00000000 00001010


    再来看认证:

    [java]  view plain copy
    1. public int getPermission(int permission){  
    2.                  int temp = 1;  
    3.     temp = temp << permission;  
    4.     temp &= aclState;  
    5.     if(temp != 0){  
    6.         return ACL_YES;  
    7.     }  
    8.     return ACL_NO;  
    9. }  

    认证更简单,直接将temp变量与aclState求&,temp为1的位为要验证的权限,其余全为0,如果aclState的这一位为1,则结果不为零,即有该权限;若aclState这一位为0,即没权限,则结果为0,没有该操作权限

    总结:权限管理最难理解的就是CRUD中的数据得来,至于别的我们可以关系,我们还是可以清晰的理解,还有一个概念就是集成的概念:

    a)        针对某个资源的所有操作,我们可以设置这些权限对用户来说是“继承”或“不继承”

                            i.             继承:意思是这些权限将使用其(即用户)所拥有的角色的权限,而不使用其(即用户)单独设置的权限

                          ii.             不继承:意思是这些权限将使用其单独设置的权限,而不使用其所拥有的角色的权限


    展开全文
  • 编码约定 变更日志 依存关系 作者和许可 它有什么作用? xshmenu是Linux X Window桌面的动态启动器菜单: 列出了给定路径中的Shell脚本文件,并在选择后运行。 列出了给定路径的同级目录,以便快速遍历。 xshmenu...
  • Windows面试题(一)

    万次阅读 2016-12-24 14:48:13
    1. 在Windows编程约定中,应用程序接口的英文缩写是( A )。  A. API B. SDI C. GDI D. MDI     2. 在Windows编程约定中,软件开发包的英文缩写是( C )。  A. API B. SDI C. SDK D. ...
  • C++ WINDOWS API 第2章 Windows API概要

    千次阅读 2016-04-18 19:16:07
    2.1 Windows数据类型.. 1 2.1.1 Windows数据类型示例.. 1 2.1.2 Windows 数据类型与标准C 数据类型的关系.. 5 2.1.3 Windows 数据类型与Windows API 5 2.1.4 Windows 中的数据结构.. 6 2.2 Windows API ...
  • WINDOWS程序内部运行原理

    千次阅读 2012-06-05 16:00:35
    API(Application programming interface) 应用程序编程的接口 MSG(message) 消息结构体  操作系统将每一个事件包装成一...MSG的结构定义如下:(windows user interface : platform sdk ) Typedef struct tagMSG{
  • 函数调用约定收藏

    千次阅读 2008-11-02 21:01:00
    函数调用约定收藏 新一篇: 函数名修饰约定规则 | 旧一篇: 构建自己的操作系统 1 function StorePage(){d=document;t=d.selection?(d.selection.type!=None?d.selection.createRange().text:):(d.getSelection?d....
  • Windows变量路径

    千次阅读 2018-09-18 19:47:41
    Windows系统还是要多了解常用变量路径的,这样也能帮助自己多了解VS中工程的配置路径。 参考文献: Windows变量路径与通配符 Windows变量路径 路径名称 实际路径 %System...
  • 该语句指定了一个呼叫约定,包括如何生产机械码以在堆栈中放置函数呼叫的参数。许多Windows函数呼叫声明为WINAPI。 WinMain参数介绍: 1.WinMain的第一个参数被称作「执行实体句柄」。在Windows程序设计中...
  • 文章目录前言分成不同的函数为了更好的封装利用句柄如何编写Windows应用程序1.入口函数WinMain2.创建窗口Step1.设计窗口类Step2.注册窗口类Step3.创建窗口 前言 接上一讲: 分成不同的函数为了更好的封装利用 ...
  • 第二章 Windows 操作系统及其应用知识 要点 理论部分 总体操作部分以熟练操作为基础如果熟练掌握操作理论也就比较简单 桌面图标外观任务栏开始菜单 窗口类型应用程序文档文件夹对话框四种 组成标题栏菜单栏四按钮...
  • 使用 Electron 自定义菜单 此系列文章的应用示例已发布于 GitHub: electron-api-demos-Zh_CN. 可以 Clone 或下载后运行查看. 欢迎 Star . 使用 Menu 和 MenuItem 模块可用于创建自定义本地菜单. 有两种菜单: 应用...
  • Windows编程基础 - 概述

    2013-01-22 22:28:52
    为了帮助开发Windows应用程序,Windows提供了大量的内建 函数以方便地使用弹出菜单、滚动条、对话框、图标和其他一些友好的用户界面应该具有的特性。  Windows运行应用程序以硬件无关的方式来
  • 1.7 本书示例约定 第2章 窗口处理 第3章 设备上下文(DC) 第4章 绘图函数 第5章 位图和图标 .. 第6章 菜单处理 第7章 内存处理 第8章 文件处理 第9章 处理文本和字体 第10章 硬件和系统 第11章 Windows消息 ...
  • 对于一个初学者来说,常常会用到各种库函数,例如printf等等,这些库函数是由你所使用的编译器厂商提供的,在Windows操作系统下,开发的应用程序, 也有这样的库函数,不同的是,这样的库函数,是有windows操作系统...
  • 函数调用约定

    2011-02-18 11:56:00
    这些现象通常是出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方的库的情况下(不是用C++语言开发的),其实这都是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则惹的祸。...
  • C/C++函数调用约定

    千次阅读 2015-05-08 10:47:46
    VC 中默认调用是 __cdecl 方式,Windows API 使用 __stdcall 调用方式,在 DLL 导出函数中,为了跟 Windows API保持一致,建议使用 __stdcall 方式。 调用约定跟堆栈清除密切相关。如果写一个汇编函数,...
  • windows程序设计笔记

    千次阅读 2016-09-08 16:29:35
    WINAPI标识符在WINDEF.H中定义:define WINAPI __stdcall该语句制订了一个调用约定,包括如何生成机器代码以及在堆栈中放置函数调用的参数, 1:1)在Win32API中,长指针和短(近)指针是没有区别的,这只是16为...
  • Windows标准控件

    千次阅读 2012-01-06 23:00:56
    使用Windows标准控件 我们在前面曾提到过,控件是一些行为标准化了的窗口,一般用于对话框或其它窗口中充当与用户交互的元素。在Visual C++中,可以使用的控件分成三类: (1) Windows标准控件 Windows标准控件...
  • Windows Shell编程

    千次阅读 2012-07-25 15:22:53
    第一章 Windows Shell是什么 一个操作系统外壳的不错的定义是它是一个系统提供的用户界面,它允许用户执行公共的任务,如访问文件系统,导出执行程序,改变系统设置等。MS-DOS有一个Command.COM扮演着这个角色。...
  • 菜单-》C/C++ =》Code Generation项选择设置函数调用约定。也可以直接在函数声明前添加关键字__stdcall、__cdecl或__fastcall等单独确定函数的调用方式。在Windows系统上开发软件常用到WINAPI宏,它可以根据编译...
  • 游戏编程之二 windows编程基础

    千次阅读 2016-06-15 19:16:43
    第二章 windows编程基础  第一节 引言  为了跟上潮流,我们抛弃了已快被淘汰的DOS操作系统,所有的讲解和例程都是基于微 软的Windows操作系统的。考虑到很多的用户并没有Windows编程基础,所以我们设置了这 一专门...
  • Windows程序运行机制

    千次阅读 2014-07-20 17:23:22
    Windows程序内部运行机制 一、API与SDK Windows操作系统提供了各种各样的函数,以方便我们开发Windows应用程序,这些函数是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),...
  • windows消息机制

    2014-03-24 20:03:45
    Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据的含义也不一样。 time表示产生消息的时间,pt...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,551
精华内容 5,020
关键字:

windows菜单的约定