【问题标题】:GetVersionEx under Windows 8Windows 8 下的 GetVersionEx
【发布时间】:2012-04-06 17:05:24
【问题描述】:

我正在编写一个 C++ 代码来确定它在哪个操作系统上运行。我使用GetVersionEx() API 来执行此操作,并使用this code 作为教程,但它似乎无法处理 Windows 8。有谁知道如何修复它以在 Windows 8 下运行?

【问题讨论】:

  • 代码没问题,你只需要找出Windows 8的版本号。像6.1是Windows 7,也许是6.2 for 8。知道了这个,你可以根据MS文档调整代码8.
  • 考虑到 Windows 8 的巨大差异,我猜版本号应该是 7.0。如果他们想对此保持清醒并跳过版本号,甚至可能是 8.0。
  • 知道 MS 会有哪些版本的 Windows 8?
  • 不能只在调试器中设置断点,只看结构的字段吗?
  • CTP 是 6.2,所以 8 也应该如此。

标签: c++ windows winapi windows-8 windowsversion


【解决方案1】:

根据 MSDN 论坛中的几个 cmet 和this article,Windows 8 的版本号是 6.2

这是在 Windows 8 Developer Preview 中更新和测试的示例代码

#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

#pragma comment(lib, "User32.lib")

#define BUFSIZE 256

typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);

BOOL GetOSDisplayString( LPTSTR pszOS)
{
   OSVERSIONINFOEX osvi;
   SYSTEM_INFO si;
   PGNSI pGNSI;
   PGPI pGPI;
   BOOL bOsVersionInfoEx;
   DWORD dwType;

   ZeroMemory(&si, sizeof(SYSTEM_INFO));
   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));

   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
   bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);

   if( ! bOsVersionInfoEx ) return 1;

   // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.

   pGNSI = (PGNSI) GetProcAddress(
      GetModuleHandle(TEXT("kernel32.dll")), 
      "GetNativeSystemInfo");
   if(NULL != pGNSI)
      pGNSI(&si);
   else GetSystemInfo(&si);

   if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && 
        osvi.dwMajorVersion > 4 )
   {
      StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));

      // Test for the specific product.

      if ( osvi.dwMajorVersion == 6 )
      {
         if( osvi.dwMinorVersion == 0 )
         {
            if( osvi.wProductType == VER_NT_WORKSTATION )
                StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
            else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " ));
         }

         if ( osvi.dwMinorVersion == 1 || osvi.dwMinorVersion == 2 )
         {
            if ( osvi.wProductType == VER_NT_WORKSTATION && osvi.dwMinorVersion == 1)
                StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 "));
            else
            if ( osvi.wProductType == VER_NT_WORKSTATION && osvi.dwMinorVersion == 2)
                StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
            else 
                StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
         }

         pGPI = (PGPI) GetProcAddress(
            GetModuleHandle(TEXT("kernel32.dll")), 
            "GetProductInfo");

         pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);

         switch( dwType )
         {
            case PRODUCT_ULTIMATE:
               StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" ));
               break;
            case PRODUCT_PROFESSIONAL:
               StringCchCat(pszOS, BUFSIZE, TEXT("Professional" ));
               break;
            case PRODUCT_HOME_PREMIUM:
               StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" ));
               break;
            case PRODUCT_HOME_BASIC:
               StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" ));
               break;
            case PRODUCT_ENTERPRISE:
               StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
               break;
            case PRODUCT_BUSINESS:
               StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" ));
               break;
            case PRODUCT_STARTER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" ));
               break;
            case PRODUCT_CLUSTER_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" ));
               break;
            case PRODUCT_DATACENTER_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" ));
               break;
            case PRODUCT_DATACENTER_SERVER_CORE:
               StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" ));
               break;
            case PRODUCT_ENTERPRISE_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
               break;
            case PRODUCT_ENTERPRISE_SERVER_CORE:
               StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" ));
               break;
            case PRODUCT_ENTERPRISE_SERVER_IA64:
               StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" ));
               break;
            case PRODUCT_SMALLBUSINESS_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" ));
               break;
            case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
               StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" ));
               break;
            case PRODUCT_STANDARD_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" ));
               break;
            case PRODUCT_STANDARD_SERVER_CORE:
               StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" ));
               break;
            case PRODUCT_WEB_SERVER:
               StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" ));
               break;
         }
      }

      if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
      {
         if( GetSystemMetrics(SM_SERVERR2) )
            StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, "));
         else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER )
            StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003"));
         else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER )
            StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server"));
         else if( osvi.wProductType == VER_NT_WORKSTATION &&
                  si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
         {
            StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition"));
         }
         else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, "));

         // Test for the server type.
         if ( osvi.wProductType != VER_NT_WORKSTATION )
         {
            if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 )
            {
                if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" ));
                else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" ));
            }

            else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
            {
                if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" ));
                else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" ));
                else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" ));
            }

            else
            {
                if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" ));
                else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" ));
                else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" ));
                else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
                   StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" ));
                else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" ));
            }
         }
      }

      if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
      {
         StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP "));
         if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
            StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" ));
         else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
      }

      if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
      {
         StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 "));

         if ( osvi.wProductType == VER_NT_WORKSTATION )
         {
            StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
         }
         else 
         {
            if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
               StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" ));
            else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
               StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" ));
            else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" ));
         }
      }

       // Include service pack (if any) and build number.

      if( _tcslen(osvi.szCSDVersion) > 0 )
      {
          StringCchCat(pszOS, BUFSIZE, TEXT(" ") );
          StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion);
      }

      TCHAR buf[80];

      StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber);
      StringCchCat(pszOS, BUFSIZE, buf);

      if ( osvi.dwMajorVersion >= 6 )
      {
         if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
            StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" ));
         else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
            StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit"));
      }

      return TRUE; 
   }

   else
   {  
      printf( "This sample does not support this version of Windows.\n");
      return FALSE;
   }
}

int __cdecl _tmain()
{
    TCHAR szOS[BUFSIZE];

    if( GetOSDisplayString( szOS ) )
    {
        _tprintf( TEXT("\n%s\n"), szOS );
        cin.get();
    }
}

这会在我的 Windows 8 测试机上返回 Microsoft Windows 8 (build 8102), 64-bit

【讨论】:

  • 好的。这就是我得到的。我希望他们更新 MSDN 中的所有内容(包括 Windows 8 的许可证名称...)
【解决方案2】:

使用Win32_OperatingSystem WMI 类的另一个选项

#include "stdafx.h"
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")

#pragma argsused
int main(int argc, char* argv[])
{
    BSTR strNetworkResource;
    //To use a WMI remote connection set localconn to false and configure the values of the pszName, pszPwd and the name of the remote machine in strNetworkResource    
    strNetworkResource = L"\\\\.\\root\\CIMV2";

    COAUTHIDENTITY *userAcct =  NULL ;
    COAUTHIDENTITY authIdent;

    // Initialize COM. ------------------------------------------

    HRESULT hres;
    hres =  CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        cout << "press enter to exit" << endl;
        cin.get();      
        return 1;                  // Program has failed.
    }

    // Set general COM security levels --------------------------


        hres =  CoInitializeSecurity(
            NULL,
            -1,                          // COM authentication
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities
            NULL                         // Reserved
            );


    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();
        cout << "press enter to exit" << endl;
        cin.get();      
        return 1;                    // Program has failed.
    }

    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);

    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object." << " Err code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();       
        cout << "press enter to exit" << endl;
        cin.get();      
        return 1;                 // Program has failed.
    }

    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;

        hres = pLoc->ConnectServer(
             _bstr_t(strNetworkResource),      // Object path of WMI namespace
             NULL,                    // User name. NULL = current user
             NULL,                    // User password. NULL = current
             0,                       // Locale. NULL indicates current
             NULL,                    // Security flags.
             0,                       // Authority (e.g. Kerberos)
             0,                       // Context object
             &pSvc                    // pointer to IWbemServices proxy
             );


    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;    
        cout << _com_error(hres).ErrorMessage() << endl;
        pLoc->Release();
        CoUninitialize();
        cout << "press enter to exit" << endl;
        cin.get();          
        return 1;                // Program has failed.
    }

    cout << "Connected to root\\CIMV2 WMI namespace" << endl;

    // Set security levels on the proxy -------------------------

        hres = CoSetProxyBlanket(
           pSvc,                        // Indicates the proxy to set
           RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
           RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
           NULL,                        // Server principal name
           RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
           RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
           NULL,                        // client identity
           EOAC_NONE                    // proxy capabilities
        );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        cout << "press enter to exit" << endl;
        cin.get();      
        return 1;               // Program has failed.
    }

    // Use the IWbemServices pointer to make requests of WMI ----

    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery( L"WQL", L"SELECT * FROM Win32_OperatingSystem",
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

    if (FAILED(hres))
    {
        cout << "ExecQuery failed" << " Error code = 0x"    << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        cout << "press enter to exit" << endl;
        cin.get();      
        return 1;               // Program has failed.
    }



    // Get the data from the WQL sentence
    IWbemClassObject *pclsObj = NULL;
    ULONG uReturn = 0;

    while (pEnumerator)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

        if(0 == uReturn || FAILED(hr))
          break;

        VARIANT vtProp;

                hr = pclsObj->Get(L"Caption", 0, &vtProp, 0, 0);// String
                if (!FAILED(hr))
                {
                    wcout << "OS Version " << vtProp.bstrVal << endl;
                }

                hr = pclsObj->Get(L"BuildNumber", 0, &vtProp, 0, 0);// String
                if (!FAILED(hr))
                {
                    wcout << "Build Number " << vtProp.bstrVal << endl;
                }

                hr = pclsObj->Get(L"Version", 0, &vtProp, 0, 0);// String
                if (!FAILED(hr))
                {
                    wcout << "Version " << vtProp.bstrVal << endl;
                }

                VariantClear(&vtProp);


        pclsObj->Release();
        pclsObj=NULL;
    }

    // Cleanup

    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    if (pclsObj!=NULL)
     pclsObj->Release();

    CoUninitialize();
    cout << "press enter to exit" << endl;
    cin.get();
    return 0;   // Program successfully completed.
}

此代码返回

操作系统版本 Microsoft Windows Developer Preview 内部版本号 8102 版本 6.2.8102

在 Windows 8 开发者预览版上。

【讨论】:

  • 这是唯一可靠的技术,它可以为所有未来未知的 Windows 版本提供正确的版本,而无需担心“Is-This-Feature-Available”或清单。
【解决方案3】:

好的,我希望有人已经这样做了......但是,我想没有,所以你去吧:

Windows 8 消费者预览版

//OSVERSIONINFO for GetVersionEx returns:
dwMajorVersion = 6;
dwMinorVersion = 2;
wProductType = 1;

//GetProductInfo returns:
dwType = 0x4A;   //Not documented yet???

PS。我没有在那里安装 VS IDE。必须制作一个小应用程序才能获得这些...

【讨论】:

  • 0x4A 代表 PRODUCT_PRERELEASE(用于开发者和消费者预览版)。
  • 请注意,在 Windows 8.1/10 中,除非您的应用程序具有嵌入式清单,其中包含 Windows 8.1/10 的supportedOS GUID,否则GetVersionEx 仍将返回 6.2。见Manifest Madness
【解决方案4】:

Windows 8 是 6.2。
Windows Server 2012 也是 6.2。
见:http://msdn.microsoft.com/en-us/library/windows/desktop/ms724834(v=vs.85).aspx

【讨论】:

  • 感谢您提供指向带有 Windows 版本表的 MS 文档的链接。
【解决方案5】:

对系统进行基本(主要、次要)验证但不深入产品详细信息的另一种可能方法是检查核心系统文件的版本,例如 kernel32.dll。

感谢另一个线程用于 GetFileVersionEx...

抱歉迟到了.. ;)

#include <Windows.h>
#include <stdio.h>
#include "Shlwapi.h"

#define a64 "(x64)"
#define i86 "(x86)"

#pragma comment( lib, "Shlwapi.lib")
#pragma comment( lib, "Version.lib")

BOOL IsWow64();
void PrintSystemVersion();

int main()
{
    PrintSystemVersion();

    getchar();
}

void PrintSystemVersion()
{
    DWORD dwSize = 0;
    BYTE *pbVersionInfo = NULL;
    VS_FIXEDFILEINFO *pFileInfo = NULL;
    UINT puLenFileInfo = 0;
    TCHAR pszPath[ MAX_PATH ];
    DWORD dwMajor, dwMinor;
    BOOL Is64 = FALSE;

    GetSystemDirectory( pszPath, sizeof( pszPath ) );

    PathAppend(pszPath, "kernel32.dll");

    dwSize = GetFileVersionInfoSize(pszPath, NULL);

    if (dwSize != 0)
    {
        pbVersionInfo = new BYTE[dwSize];

        if (GetFileVersionInfo(pszPath, 0, dwSize, pbVersionInfo))
        {
            if (VerQueryValue(pbVersionInfo, "\\", (LPVOID*)&pFileInfo, &puLenFileInfo))
            {
                if ( IsWow64( ) == TRUE ) {
                    Is64 = TRUE;
                    dwMajor = pFileInfo->dwProductVersionMS >> 16 & 0xff; 
                    dwMinor = pFileInfo->dwProductVersionMS >> 0 & 0xff;
                } else {
                    dwMajor = pFileInfo->dwProductVersionMS;
                    dwMinor = pFileInfo->dwProductVersionMS;
                }
                if ( dwMajor == 5 )
                {
                    if ( dwMinor ==  0 ) {
                        printf("Windows 2000 %s\r\n", Is64 ? a64 : i86);
                    }
                    else if ( dwMinor == 1) {
                        printf("Windows XP %s\r\n", Is64 ? a64 : i86);
                    }
                }
                else if ( dwMajor == 6)
                {
                    if ( dwMinor == 0) {
                        printf("Windows Vista %s\r\n", Is64 ? a64 : i86);
                    }
                    else if ( dwMinor == 1) {
                        printf("Windows 7 %s\r\n", Is64 ? a64 : i86);
                    }
                    else if ( dwMinor == 2) {
                        printf("Windows 8 %s\r\n", Is64 ? a64 : i86);
                    }
                    else if ( dwMinor == 3) {
                        printf("Windows 8.1 %s\r\n", Is64 ? a64 : i86);
                    }
                }
            }
        }
    }
}

BOOL IsWow64()
{
    typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    BOOL bIsWow64 = FALSE;

    fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
        GetModuleHandle(TEXT("kernel32")), "IsWow64Process");

    if (NULL != fnIsWow64Process)
    {
        if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
        {
            bIsWow64 = TRUE;
        }
    }
    return bIsWow64;
}

【讨论】:

    【解决方案6】:

    根据this MSDN page,实际上建议使用Version Helper函数而不是GetVersionInfoEx来确定正在运行的Windows版本。如GetVersionEx 页面所述,该 API 将来可能会消失。

    【讨论】:

    • 确实 MS 已弃用 GetVersionEx。但是为什么使用 versionHelper 函数很麻烦? API 对我来说似乎相当简单。
    • 你是对的。又看了一遍,调用代码简化了。
    猜你喜欢
    • 2020-02-06
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多