窗口站和桌面

来源:互联网 发布:知到智慧树登陆 编辑:程序博客网 时间:2024/04/30 10:02

窗口站:

一个窗口站是一个安全对象,其中包含一个剪贴板,一系列的全局原子以及一组桌面对象。分配给一个交互式用户登陆会话的交互式窗口站还包含键盘,鼠标和显示设备。交互式的窗口站对用户可见并且能接受来自用户的输入。所有其它的窗口站对用户都是非交互的,这意味着它们对用户不可见,也不能接受用户输入;

 

桌面:

桌面是包含在窗口站中的安全对象,一个桌面有一个逻辑显示表面并且包含着窗口,菜单和钩子;一个windows的窗口站能有多个桌面,仅有可交互的窗口站的桌面能被显示以及接受用户输入。在一个可交互的窗口站上,同一时刻仅有一个活动桌面。活动桌面也被称为输入桌面,是当前显示给用户并接受用户输入的桌面。应用程序能够使用OpenInputDesktop函数来获得输入桌面都句柄;具有一定访问权限都应用程序,还能够使用SwitchDesktop函数来指定一个不同的输入桌面;

 

交互式服务:

交互式服务是一个能和输入桌面(活动桌面)交互的服务。其它的桌面不能够接收用户的输入。

交互服务必须运行在 LocalSystem 帐户的上下文中,进行配置后以交互方式运行。在调用CreateService函数时指定dwServeceType参数为SERVICE_INTERACTIVE_PROCESS旗标,服务就能够被配置为以交互的方式运行。但是,下面的注册表项包含一个的值NoInteractiveServices,用于控制该 SERVICE_INTERACTIVE_PROCESS 标志的影响:

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Windows

 

NoInteractiveServices的默认值为0,意味着有SERVICE_INTERACTIVE_PROCESS标志的服务能以交互的方式运行。当 NoInteractiveServices 值设置为非零值时,在那之后启动的服务,无论它是否设置了SERVICE_INTERACTIVE_PROCESS,都不能以交互都方式运行;

 

窗口站和桌面都创建:

系统会自动创建交互式窗口站。 当一个交互式的用户登录时,系统会将交互式窗口站和用户的登录会话相关联。 系统还将为交互式窗口站创建默认输入桌面。

当一个非交互都进程,如win32服务尝试连接到一个win32(系统)并且进程的登陆会话中并没有窗口站存在时,Win32 会尝试为会话创建一个窗口站和桌面。 创建的窗口站的名称基于该登录会话标识符并且桌面被命名为"default"。窗口站和桌面的自由访问列表DACL允许服务的用户账户拥有如下存取权限:

WindowStation: WINSTA_READATTRIBUTES | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | STANDARD_RIGHTS_REQUIRED

 

Desktop: DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_ENUMERATE | STANDARD_RIGHTS_REQUIRED

 

一个进程能使用CreateWIndowStation函数来创建一个新的窗口站,并调用CreateDeskTop函数创建一个新的桌面;

进程和窗口站的连接:

当一个进程第一次调用USER32GDI32的函数(其它的窗口站或桌面函数),必须转到win32服务时,进程能够与窗口站和桌面自动的产生一个连接。Wiin32服务依据下面的规则来判断窗口站应该和那一个进程连接:

如果进程调用了SetProcessWindowStation函数,它就和函数指定的窗口站连接。

如果进程并没有调用SetProcessWindowStation函数,它就和从父进程继承而来的窗口站连接。

 

如果进程没有调用SetProcessWindowStation函数,也没有继承到窗口站,那么win32服务尝试打开MAXIMUM_ALLOWED权限并按如下方式连接一个窗口站:

 

当创建进程时,如果一个窗口站的名称在STARTUPINFO结构的lpDeskTop成员中被指定并传递给CreateProcess函数,那么进程就和指定的窗口站连接。否则,如果这个进程在一个交互式用户的登陆会话中运行,进程就和交互式窗口站连接。

如果进程运行在一个非交互的登陆会话中,窗口站命名形式基于登陆会话标识并尝试去打开窗口站,如果打开失败是因为这个窗口站不存在,那么win32尝试去创建一个窗口站和一个默认的桌面;

 

=================================================================================================================

 

 

翻译自:http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsAWindowStation.html

水平有限,请指正。

Window station 是一个不为人所熟知但是却是非常重要的安全特性,它被设计成为一个限制操作系统中窗口环境的“沙箱”。为了避免将访问控制列表(ACL)放置到每个窗口中和对每个窗口消息进行权限检查而导致的性能下降,我们简单地让窗口互相通知而不执行任何安全检查。然而,我们是在一个私有环境中这么做的,这就是Window station。

Window station 是一个安全核心对象,它包括了剪贴板,一个私有的原子表格,一套桌面以及一套窗口。每一个进程都依赖于一个window station,而且这个关联通常平行于登录会话的分配。也就是说,对于每一个登录会话来说,都有一个相应的window station,就像下图所描述的那样。实际上,window station的名称得自登录会话标识符。例如,用于网络服务的window station 名称是Service-0x0-0x3e4$。偶尔一个internet后台服务程序(例如IIS)会自己管理window station,但这只是一个例外。还有一个“交互式”的window station, 它会一直存在,甚至没有交互式用户存在时也如此。这个特殊的交互式window station 的名字硬编码为WinSta0, 需要我们对它更对关注。

WinSta0是唯一的一个实际绑定到硬件上的window station.也就是说,你可以在这里看到窗口,并且它们可以接收到鼠标和键盘的输入。WinSta0也是高度安全的,它通过一个能够限制登录会话运行权限的ACL来达到此目的,尽管在上图中你也可以看到一些运行在SYSTEM登录会话中的高度受信的服务程序可能运行在这里,比如说使winlogon.exe。WinSta0上的ACL在一个非常详细的等级上控制你的GUI可以做些什么,但是实际说来,这个一个要么全有,要么全无的授权,并且只有在限制哪个进程允许关联到window station时才真正需要。(也可以调用函数SetProcessWindowStation来改变进程所属的window station,或通过在调用CreatProcess时设置STARTUPINFO参数将新启动的程序附着到特定的window station).使用这种方法,windos station上的ACL就可以阻止其他登录会话中的程序入侵到你的窗口中。

拥有window station的一个实际用处就是避免来自相对于交互式用户的internet后台服务程序的攻击。如果进程运行在一个低特权账号中同时台帐号可以发送窗口消息到一个高特权级别的程序,那么它就控制了这个程序(这称为lurring attact)。因为window station边界通常是与登录会话边界是平行的,这就有助于阻止这类的攻击。

当一个进程又创建了另外一个,那么新的进程就被放置到与旧进程相同的window station中,除非你指明不这么做(但这种情况很少)。所以,就像是令牌和登录会话一样,一个新的进程很自然的继承了它的父进程的窗口环境。注意启动一个服务不同于简单的创建一个新的进程。当一个服务启动时,它就会由操作系统分配一个合适的登录会话和window station. 

 

 

 

==================================================================================================================

 

 

 

创建桌面交互式服务与非交互式服务的区别

 

顾名思义,交互式服务可以与界面进行交互,比如弹出对话框,日志窗口输出等。交互服务不允许访问网络共享资源,比如网络影射路径等。

 

如果需要访问网络共享资源,则需要创建非交互式服务。非交互服务不允许与桌面直接交互,如需交互可通过其他折中方式比如socket, name pipe等。

 

(Copy from MSDN

Note  It is possible to display a message box from a service, even if it is not running in the LocalSystem account or not configured to run interactively. Simply call the MessageBox function using the MB_SERVICE_NOTIFICATION flag. Do not call MessageBox during service initialization or from the HandlerEx routine, unless you call it from a separate thread, so that you return to the SCM in a timely manner.

It is also possible to interact with the desktop from a non-interactive service by modifying the DACLs on the interface window station and desktop or by impersonating the logged-on user and opening the interactive window station and desktop directly. For more information, see Interacting with the User in a Service.

)

 

代码范例(c/c++)

 

SC_HANDLE hService;

       if (strlen(m_szServiceUser) == 0 || strlen(m_szServicePassword) == 0)      

       {

              hService= ::CreateService(

                     hSCM, m_szServiceName, m_szServiceName,

                     SERVICE_ALL_ACCESS,

// SERVICE_INTERACTIVE_PROCESS, enable service to interact with desktop (window)

// If SERVICE_INTERACTIVE_PROCESS is set, network share resource will not be allowed to access (for example net share files or directories).

SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,

                     SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,

// If the user & password is set to NULL, the service will be created with localsystem, shared object created in the service can not be accessed by other process

                     szFilePath, NULL, NULL, _T("RPCSS/0"), NULL, NULL);             

       }

       else

       {

              hService= ::CreateService(

                     hSCM, m_szServiceName, m_szServiceName,

// If SERVICE_INTERACTIVE_PROCESS is not set, windows can not be created.

                     SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,

                     SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,

                     szFilePath, NULL, NULL, _T("RPCSS/0"), m_szServiceUser, m_szServicePassword);

             

// Grant the user the privilege to logon, else if user login with admin or other user name, the service can not be started

              GrantLoginPrivilege(m_szServiceUser);

          }

 

// copy from MSDN

//////////////////////////////////////////////////////////////////////

 

/*

#ifndef UNICODE

#define UNICODE

#endif // UNICODE

*/

#include "stdafx.h"

#include <windows.h>

#include <stdio.h>

#include <TCHAR.h>

#include "ntsecapi.h"

 

 

NTSTATUS

OpenPolicy(

    LPWSTR ServerName,          // machine to open policy on (Unicode)

    DWORD DesiredAccess,        // desired access to policy

    PLSA_HANDLE PolicyHandle    // resultant policy handle

    );

 

BOOL

GetAccountSid(

    LPCTSTR SystemName,          // where to lookup account

    LPCTSTR AccountName,         // account of interest

    PSID *Sid                   // resultant buffer containing SID

    );

 

NTSTATUS

SetPrivilegeOnAccount(

    LSA_HANDLE PolicyHandle,    // open policy handle

    PSID AccountSid,            // SID to grant privilege to

    LPWSTR PrivilegeName,       // privilege to grant (Unicode)

    BOOL bEnable                // enable or disable

    );

 

void

InitLsaString(

    PLSA_UNICODE_STRING LsaString, // destination

    LPWSTR String                  // source (Unicode)

    );

 

void

DisplayNtStatus(

    LPSTR szAPI,                // pointer to function name (ANSI)

    NTSTATUS Status             // NTSTATUS error value

    );

 

void

DisplayWinError(

    LPSTR szAPI,                // pointer to function name (ANSI)

    DWORD WinError              // DWORD WinError

    );

 

//

// If you have the ddk, include ntstatus.h.

//

#ifndef STATUS_SUCCESS

#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)

#endif

 

extern "C" bool GrantLoginPrivilege(LPCTSTR lpszAccountName)

{

    LSA_HANDLE PolicyHandle;

    PSID pSid;

    NTSTATUS Status;

       bool bSuccess = false;

    WCHAR wComputerName[256]=L"";   // static machine name buffer

 

    //

    // Open the policy on the target machine.

    //

    Status = OpenPolicy(

                wComputerName,      // target machine

                POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,

                &PolicyHandle       // resultant policy handle

                );

 

    if(Status != STATUS_SUCCESS)

       {

        DisplayNtStatus("OpenPolicy", Status);

        return false;

    }

 

       //

       //Remove Domain name if lpszAccountName include one.

       //

 

       TCHAR szName[256];

       TCHAR* p = _tcschr(lpszAccountName, _T('//'));

       if(p != NULL)

       {

              _tcscpy(szName, p+1);

       }

       else

       {

              _tcscpy(szName, lpszAccountName);

       }

 

    //

    // Obtain the SID of the user/group.

    // Note that we could target a specific machine, but we don't.

    // Specifying NULL for target machine searches for the SID in the

    // following order: well-known, Built-in and local, primary domain,

    // trusted domains.

    //

 

 

    if(GetAccountSid(

            NULL,       // default lookup logic

            szName,// account to obtain SID

            &pSid       // buffer to allocate to contain resultant SID

            ))

       {

        //

        // We only grant the privilege if we succeeded in obtaining the

        // SID. We can actually add SIDs which cannot be looked up, but

        // looking up the SID is a good sanity check which is suitable for

        // most cases.

 

        //

        // Grant the SeServiceLogonRight to users represented by pSid.

        //

        Status = SetPrivilegeOnAccount(

                    PolicyHandle,           // policy handle

                    pSid,                   // SID to grant privilege

                    L"SeServiceLogonRight", // Unicode privilege

                    TRUE                    // enable the privilege

                    );

 

        if(Status == STATUS_SUCCESS)

              {

            bSuccess = true;

              }

        else

              {

            DisplayNtStatus("SetPrivilegeOnAccount", Status);

              }

    }

    else

       {

        //

        // Error obtaining SID.

        //

        DisplayWinError("GetAccountSid", GetLastError());

    }

 

    //

    // Close the policy handle.

    //

    LsaClose(PolicyHandle);

 

    //

    // Free memory allocated for SID.

    //

    if(pSid != NULL) HeapFree(GetProcessHeap(), 0, pSid);

 

    return bSuccess;

}

 

 

/*++

This function attempts to obtain a SID representing the supplied

account on the supplied system.

 

If the function succeeds, the return value is TRUE. A buffer is

allocated which contains the SID representing the supplied account.

This buffer should be freed when it is no longer needed by calling

HeapFree(GetProcessHeap(), 0, buffer)

 

If the function fails, the return value is FALSE. Call GetLastError()

to obtain extended error information.

 

Scott Field (sfield)    12-Jul-95

--*/

 

BOOL

GetAccountSid(

    LPCTSTR SystemName,

    LPCTSTR AccountName,

    PSID *Sid

    )

{

    LPTSTR ReferencedDomain=NULL;

    DWORD cbSid=128;    // initial allocation attempt

    DWORD cchReferencedDomain=16; // initial allocation size

    SID_NAME_USE peUse;

    BOOL bSuccess=FALSE; // assume this function will fail

 

    __try {

 

    //

    // initial memory allocations

    //

    *Sid = (PSID)HeapAlloc(GetProcessHeap(), 0, cbSid);

 

    if(*Sid == NULL) __leave;

 

    ReferencedDomain = (LPTSTR)HeapAlloc(

                    GetProcessHeap(),

                    0,

                    cchReferencedDomain * sizeof(TCHAR)

                    );

 

    if(ReferencedDomain == NULL) __leave;

 

    //

    // Obtain the SID of the specified account on the specified system.

    //

    while(!LookupAccountName(

                    SystemName,         // machine to lookup account on

                    AccountName,        // account to lookup

                    *Sid,               // SID of interest

                    &cbSid,             // size of SID

                    ReferencedDomain,   // domain account was found on

                    &cchReferencedDomain,

                    &peUse

                    ))

       {

        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)

              {

            //

            // reallocate memory

            //

            *Sid = (PSID)HeapReAlloc(

                        GetProcessHeap(),

                        0,

                        *Sid,

                        cbSid

                        );

            if(*Sid == NULL) __leave;

 

            ReferencedDomain = (LPTSTR)HeapReAlloc(

                        GetProcessHeap(),

                        0,

                        ReferencedDomain,

                        cchReferencedDomain * sizeof(TCHAR)

                        );

            if(ReferencedDomain == NULL) __leave;

        }

        else __leave;

    }

 

    //

    // Indicate success.

    //

    bSuccess = TRUE;

 

    } // try

    __finally {

 

    //

    // Cleanup and indicate failure, if appropriate.

    //

 

    HeapFree(GetProcessHeap(), 0, ReferencedDomain);

 

    if(!bSuccess) {

        if(*Sid != NULL) {

            HeapFree(GetProcessHeap(), 0, *Sid);

            *Sid = NULL;

        }

    }

 

    } // finally

 

    return bSuccess;

}

 

NTSTATUS

SetPrivilegeOnAccount(

    LSA_HANDLE PolicyHandle,    // open policy handle

    PSID AccountSid,            // SID to grant privilege to

    LPWSTR PrivilegeName,       // privilege to grant (Unicode)

    BOOL bEnable                // enable or disable

    )

{

    LSA_UNICODE_STRING PrivilegeString;

 

    //

    // Create a LSA_UNICODE_STRING for the privilege name.

    //

    InitLsaString(&PrivilegeString, PrivilegeName);

 

    //

    // grant or revoke the privilege, accordingly

    //

    if(bEnable) {

        return LsaAddAccountRights(

                PolicyHandle,       // open policy handle

                AccountSid,         // target SID

                &PrivilegeString,   // privileges

                1                   // privilege count

                );

    }

    else {

        return LsaRemoveAccountRights(

                PolicyHandle,       // open policy handle

                AccountSid,         // target SID

                FALSE,              // do not disable all rights

                &PrivilegeString,   // privileges

                1                   // privilege count

                );

    }

}

 

void

InitLsaString(

    PLSA_UNICODE_STRING LsaString,

    LPWSTR String

    )

{

    DWORD StringLength;

 

    if(String == NULL) {

        LsaString->Buffer = NULL;

        LsaString->Length = 0;

        LsaString->MaximumLength = 0;

        return;

    }

 

    StringLength = lstrlenW(String);

    LsaString->Buffer = String;

    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);

    LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);

}

 

NTSTATUS

OpenPolicy(

    LPWSTR ServerName,

    DWORD DesiredAccess,

    PLSA_HANDLE PolicyHandle

    )

{

    LSA_OBJECT_ATTRIBUTES ObjectAttributes;

    LSA_UNICODE_STRING ServerString;

    PLSA_UNICODE_STRING Server;

 

    //

    // Always initialize the object attributes to all zeroes.

    //

    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

 

    if (ServerName != NULL) {

        //

        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in

        //

        InitLsaString(&ServerString, ServerName);

        Server = &ServerString;

    } else {

        Server = NULL;

    }

 

    //

    // Attempt to open the policy.

    //

    return LsaOpenPolicy(

                Server,

                &ObjectAttributes,

                DesiredAccess,

                PolicyHandle

                );

}

 

void

DisplayNtStatus(

    LPSTR szAPI,

    NTSTATUS Status

    )

{

    //

    // Convert the NTSTATUS to Winerror. Then call DisplayWinError().

    //

    DisplayWinError(szAPI, LsaNtStatusToWinError(Status));

}

 

void

DisplayWinError(

    LPSTR szAPI,

    DWORD WinError

    )

{

    LPSTR MessageBuffer;

    DWORD dwBufferLength;

 

    if(dwBufferLength=FormatMessageA(

                        FORMAT_MESSAGE_ALLOCATE_BUFFER |

                        FORMAT_MESSAGE_FROM_SYSTEM,

                        NULL,

                        WinError,

                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

                        (LPSTR) &MessageBuffer,

                        0,

                        NULL

                        ))

    {

              char szCaption[256];

              sprintf(szCaption,"GrantLoginPrivilege:%s", szAPI);

              ::MessageBox(NULL, MessageBuffer, szCaption, MB_OK|MB_ICONSTOP);

 

        //

        // Free the buffer allocated by the system.

        //

        LocalFree(MessageBuffer);

    }

}

        

==========================================================================================

概要
窗口站和与之相关联的桌面组合都有 Microsoft Windows NT、 Windows 2000 和 Windows XP 的服务。 这基于在服务运行哪个...

窗口站和与之相关联的桌面组合都有 Microsoft Windows NT、 Windows 2000 和 Windows XP 的服务。 这基于在服务运行哪个帐户上:
  • 如果该服务在本地系统帐户运行,并不是交互式 (也就是服务类型不包括 SERVICE_INTERACTIVE_PROCESS 标志),该服务将使用下列窗口站和桌面:
    服务 0x0 3e7 $ /default
    "服务 0x0 3e7 $"与的窗口站和"默认"名称哪里是桌面的名称。

    这是一个非交互式窗口站。
  • 如果该服务在本地系统帐户运行,并且与桌面交互 (也就是,服务类型包括 SERVICE_INTERACTIVE_PROCESS 标志),该服务将使用下列窗口站和桌面:
    Winsta0/default
    这是一个交互式窗口站。
  • 如果服务正在运行的用户帐户的安全上下文中,系统将为该服务创建唯一的非交互式窗口站和桌面。 窗口站名称将基于用户的上登录安全标识符 (SID):

    服务 0xZ1 Z2 $ /default Z1 其中是高的一部分,Z2 是登录 SID 的较低的一部分。
此外,在同一安全上下文 (相同的服务帐户名称) 将运行的两个服务不接收相同的窗口站和桌面,因为登录安全 Identifier's(SID) 唯一到该登录会话。

更多信息
窗口站可以是交互式或非交互式。 (只有"Winsta0"可以是一个交互式窗口站。 在交互式窗口站创建任何桌面可以成为活动。 活动桌面环绕对象 (如窗口和对话框)...

窗口站可以是交互式或非交互式。 (只有"Winsta0"可以是一个交互式窗口站。 在交互式窗口站创建任何桌面可以成为活动。 活动桌面环绕对象 (如窗口和对话框) 可以看到交互式登录的用户 (也就是按 CTRL ALT DELETE 登录系统用户) 在桌面上创建和接收用户输入的用户的能力。

交互式登录的用户启动的任何进程,这些进程都是与"默认"中"Winsta0"窗口站的桌面相关联。 "默认"桌面被视为活动桌面。 在活动桌面上的一过程可以切换桌面,这样另一个桌面将成为如"winlogon"桌面活动桌面。 只有一个桌面可以在任何时间是活动桌面。 SwitchDesktop API 可用于激活一个桌面。 有关详细信息请参阅平台 SDK 联机文档。

非交互式的窗口站与相关联的台式计算机可以创建用户对象。 这些对象决不会以交互方式登录的用户可见,并且将永远不会收到任何用户输入。 濡傛灉鎮 ㄦ 湁非交互式服务在本地系统帐户运行服务创建任何用户对象不会以交互方式登录的用户可见。 此外,由该服务启动的任何进程也无法看到。

下面是关于窗口站和桌面上的其他重要信息:
  • 仅可在同一桌面上的进程间发送窗口消息。 它们不能发送跨不同的桌面与关联的进程。
  • 应用程序定义挂钩限于 Windows 消息的方式。 在特定桌面中运行的进程的挂钩过程仅将获得针对同一桌面中创建的窗口的消息。
有关其他信息,请单击下面文章编号,查看 Microsoft 知识库中相应的文章:
327618  (http://support.microsoft.com/kb/327618/EN-US/ ) 信息: 安全、 服务和交互式桌面

这篇文章中的信息适用于:
  • Microsoft Win32 Application Programming Interface 当用于
    • Microsoft Windows NT 3.51 Service Pack 5
    • Microsoft Windows NT 4.0
    • the operating system: Microsoft Windows 2000
    • the operating system: Microsoft Windows XP
回到顶端
关键字: 
kbmt kbinfo kbkernbase kbservice KB171890 KbMtzh
机器翻译机器翻译
注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完成。微软很高兴能同时提供给您由人工翻译的和由机器翻译的文章, 以使您能使用您的语言访问所有的知识库文章。然而由机器翻译的文章并不总是完美的。它可能存在词汇,语法或文法的问题,就像是一个外国人在说中文时总是可能犯这样的错误。虽然我们经常升级机器翻译软件以提高翻译质量,但是我们不保证机器翻译的正确度,也不对由于内容的误译或者客户对它的错误使用所引起的任何直接的, 或间接的可能的问题负责。
点击这里察看该文章的英文版: 171890  (http://support.microsoft.com/kb/171890/en-us/ )
回到顶端
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。 
原创粉丝点击