如何获取CPU 或 主板 唯一标示(如:序列号)

noswordwj 2004-04-16 10:49:40
加精
如何获取CPU 或 主板 唯一标示(如:序列号)用汇编,c 都可以
...全文
1949 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
annyi 2004-08-14
  • 打赏
  • 举报
回复
你到网上下个aida32,这是一个系统检测工具,你的所有硬件信息都会显示出来的
huangbeyond 2004-08-14
  • 打赏
  • 举报
回复
gz
一条晚起的虫 2004-08-14
  • 打赏
  • 举报
回复
UP
kvw3000 2004-08-14
  • 打赏
  • 举报
回复
Windows 管理规范 (WMI) 是可伸缩的系统管理结构,它采用一个统一的、基于标准的、可扩展的面向对象接口。WMI 为您提供与系统管理信息和基础 WMI API 交互的标准方法。WMI 主要由系统管理应用程序开发人员和管理员用来访问和操作系统管理信息。
WMI 可用于生成组织和管理系统信息的工具,使管理员或系统管理人员能够更密切地监视系统活动。例如,可以使用 WMI 开发一个应用程序,用于在 Web 服务器崩溃时呼叫管理员。
将 WMI 与 .NET 框架一起使用
WMI 提供了大量的规范以便为许多高端应用程序(例如,Microsoft Exchange、Microsoft SQL Server 和 Microsoft Internet 信息服务 (IIS))实现几乎任何管理任务。管理员可以执行下列任务:
• 监视应用程序的运行状况。
• 检测瓶颈或故障。
• 管理和配置应用程序。
• 查询应用程序数据(使用对象关系的遍历和查询)。
• 执行无缝的本地或远程管理操作。
WMI 结构由以下三层组成:
• 客户端
使用 WMI 执行操作(例如,读取管理详细信息、配置系统和预订事件)的软件组件。
• 对象管理器
提供程序与客户端之间的中间装置,它提供一些关键服务,如标准事件发布和预订、事件筛选、查询引擎等。
• 提供程序
软件组件,它们捕获实时数据并将其返回到客户端应用程序,处理来自客户端的方法调用并将客户端链接到所管理的基础结构。
通过定义完善的架构向客户端和应用程序无缝地提供了数据和事件以及配置系统的能力。在 .NET 框架中,System.Management 命名空间提供了用于遍历 WMI 架构的公共类。
除了 .NET 框架,还需要在计算机上安装 WMI 才能使用该命名空间中的管理功能。如果使用的是 Windows Millennium Edition、Windows 2000 或 Windows XP,那么已经安装了 WMI。否则,将需要从 MSDN 下载 WMI。
用 System.Management 访问管理信息
System.Management 命名空间是 .NET 框架中的 WMI 命名空间。此命名空间包括下列支持 WMI 操作的第一级类对象:
• ManagementObject 或 ManagementClass:分别为单个管理对象或类。
• ManagementObjectSearcher:用于根据指定的查询或枚举检索 ManagementObject 或 ManagementClass 对象的集合。
• ManagementEventWatcher:用于预订来自 WMI 的事件通知。
• ManagementQuery:用作所有查询类的基础。
System.Management 类的使用编码范例对 .NET 框架环境很适合,并且 WMI 在任何适当的时候均使用标准基框架。例如,WMI 广泛利用 .NET 集合类并使用推荐的编码模式,如 .NET 异步操作的“委托”模式。因此,使用 .NET 框架的开发人员可以使用他们的当前技能访问有关计算机或应用程序的管理信息。
请参见
使用 WMI 管理应用程序 | 检索管理对象的集合 | 查询管理信息 | 预订和使用管理事件 | 执行管理对象的方法 | 远程处理和连接选项 | 使用强类型对象

获取CPU序列号代码
string cpuInfo = "";//cpu序列号
ManagementClass cimobject = new ManagementClass("Win32_Processor");
ManagementObjectCollection moc = cimobject.GetInstances();
foreach(ManagementObject mo in moc)
{
cpuInfo = mo.Properties["ProcessorId"].value.ToString();
Console.WriteLine(cpuInfo);
Console.ReadLine();
}
获取网卡硬件地址
using System.Management;
...
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection moc = mc.GetInstances();
foreach(ManagementObject mo in moc)
{
if((bool)mo["IPEnabled"] == true)
Console.WriteLine("MAC address\t{0}", mo["MacAddress"].ToString());
mo.Dispose();
}
}
获取硬盘ID
String HDid;
ManagementClass cimobject = new ManagementClass("Win32_DiskDrive");
ManagementObjectCollection moc = cimobject.GetInstances();
foreach(ManagementObject mo in moc)
{
HDid = (string)mo.Properties["Model"].value;
MessageBox.Show(HDid );
}
ozymandias 2004-08-14
  • 打赏
  • 举报
回复
mark
linfeng1216 2004-04-19
  • 打赏
  • 举报
回复
如果是98系统,直接用如下代码就可得到主板的序列号:
strcpy(szMainboard, (char*)000fec71); // AMI的主板序列号放在内存000fec71处。
AhBian 2004-04-18
  • 打赏
  • 举报
回复
关注
taianmonkey 2004-04-18
  • 打赏
  • 举报
回复
调用方法:
CString strBiosId = GetBiosId();
AfxMessageBox(strBiosId);
taianmonkey 2004-04-18
  • 打赏
  • 举报
回复
CString CReadBiosIdDlg::GetBiosId()
{
HANDLE physmem;
DWORD vaddress, paddress;
DWORD length=1;

CString strId;

char ch;


if(!ReadBiosIdClass.LocateNtdllEntryPoints())
{
AfxMessageBox("Unable to locate NTDLL entry points!");
return -1;
}
//
// Open physical memory
//
if( !(physmem = ReadBiosIdClass.OpenPhysicalMemory()))
{
return -1;
}
//
// Enter the command loop
//
//printf("Enter values in hexadecimal. Enter 'q' to quit.\n");
/*while( 1 )
{
printf("\nAddress: " ); fflush( stdout );
gets( input );
if( input[0] == 'q' || input[0] == 'Q' )
break;
sscanf( input, "%x", &paddress );
printf("Bytes: "); fflush( stdout );
gets( input );
if( input[0] == 'q' || input[0] == 'Q' )
break;
sscanf( input, "%x", &length );
//
// Map it
//
if( !MapPhysicalMemory( physmem, &paddress, &length,&vaddress ))
continue;
//
// Dump it
//
lines = 0;
for( i = 0; i < length; i += BYTESPERLINE )
{
printf("%08X: ", paddress + i );
for( j = 0; j < BYTESPERLINE; j++ )
{
if( i+j == length ) break;
if( j == BYTESPERLINE/2 )
printf("-" );
printf("%02X ", *(PUCHAR) (vaddress + i +j ));
}
for( j = 0; j < BYTESPERLINE; j++ )
{
if( i+j == length ) break;
ch = *(PUCHAR) (vaddress + i +j );
if( __iscsym( ch ) || isalnum( ch ) || ch == ' ')
{
printf("%c", ch);
}
else
{
printf("." );
}
}
printf("\n");
if( lines++ == LINESPERSCREEN )
{
printf("-- more -- ('q' to abort)" ); fflush(stdout);
ch = getchar();
if( ch == 'q' || ch == 'Q' )
{
fflush( stdin );
break;
}
lines = 0;
}
}
//
// Unmap the view
//
UnmapPhysicalMemory( vaddress );
}*/
//
// Close physical memory section
//
int i=0;
paddress=0x000fec71;//Award
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000fec71-0x000fe000;

if(ReadBiosIdClass.biosCheckAward(vaddress+paddress))
{
strId = _T("Is Award Bios! ");
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
//printf("\r\n");
break;
}
i++;
}
}

paddress=0x000ff478;//AMI
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000ff478-0x000ff000;

if(ReadBiosIdClass.biosCheckAMI(vaddress+paddress))
{
strId = _T("Is AMI Bios! ");
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
//printf("\r\n");
break;
}
i++;
}
}

paddress=0x000ff478;//AMI
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000ff478-0x000ff000;

if(ReadBiosIdClass.biosCheckAMI(vaddress+paddress))
{
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
printf("\r\n");
break;

}
i++;
}
}

paddress=0x000f6577;//Phoenix
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000f6577-0x000f6000;

if(ReadBiosIdClass.biosCheckPhoenix(vaddress+paddress))
{
strId = _T("Is Phoenix Bios! ");
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
//printf("\r\n");
break;
}
i++;
}
}

paddress=0x000f7196;//Phoenix
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000f7196-0x000f7000;

if(ReadBiosIdClass.biosCheckPhoenix(vaddress+paddress))
{
strId = _T("Is Phoenix Bios! ");
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
//printf("\r\n");
break;
}
i++;
}
}
paddress=0x000f7550;//Phoenix
ReadBiosIdClass.MapPhysicalMemory( physmem, &paddress, &length,&vaddress ) ;
paddress=0x000f7550-0x000f7000;

if(ReadBiosIdClass.biosCheckPhoenix(vaddress+paddress))
{
strId = _T("Is Phoenix Bios! ");
while(i<50)
{
if(*(PUCHAR)(vaddress+paddress+i) > 0x20 && *(PUCHAR)(vaddress+paddress+i) < 0x71)
{
ch = *(PCHAR)(vaddress+paddress+i);
strId = strId + ch;
//printf("%c", ch);
}
else
{
//printf("\r\n");
break;
}
i++;
}
}
CloseHandle(physmem);
return strId;
}
taianmonkey 2004-04-18
  • 打赏
  • 举报
回复
// ReadBiosIdClass.cpp: implementation of the CReadBiosIdClass class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ReadBiosIdClass.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//
#define BYTESPERLINE 16
//
// Lines to print before pause
//
#define LINESPERSCREEN 25


NTSTATUS (__stdcall *NtUnmapViewOfSection)
(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress
);
NTSTATUS (__stdcall *NtOpenSection)
(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);
NTSTATUS (__stdcall *NtMapViewOfSection)
(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset, /* optional */
IN OUT PULONG ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect
);
VOID (__stdcall *RtlInitUnicodeString)
(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
ULONG (__stdcall *RtlNtStatusToDosError)
(
IN NTSTATUS Status
);

CReadBiosIdClass::CReadBiosIdClass()
{

}

CReadBiosIdClass::~CReadBiosIdClass()
{

}

int CReadBiosIdClass::biosCheckAward(DWORD Add)
{
//Example
//AWard:07/08/2002-i845G-ITE8712-JF69VD0CC-00
// 10/10/98-xxx……
//Phoenix-Award:03/12/2002-sis645-p4s333
if(*(PUCHAR)(Add+2)=='/' && *(PUCHAR)(Add+5)=='/'){
CHAR *p=(CHAR*)Add;
while(*p){
if(*p < 0x20 || *p > 0x71)
goto NOT_AWARD;
p++;
}
return 1;

}

NOT_AWARD:
return 0;
}

int CReadBiosIdClass::biosCheckPhoenix(DWORD Add)
{
//Example
//Phoenix:NITELT0.86B.0044.P11.9910111055
if(*(PUCHAR)(Add+7)=='.' && *(PUCHAR)(Add+11)=='.'){
CHAR *p=(PCHAR)Add;
while(*p){
if(*p < 0x20 || *p > 0x71)
goto NOT_PHOENIX;
p++;
}
return 1;
}
NOT_PHOENIX:
return 0;
}

int CReadBiosIdClass::biosCheckAMI(DWORD Add)
{
//Example
//AMI:51-2300-000000-00101111-030199-
if(*(PUCHAR)(Add+2)=='-' && *(PUCHAR)(Add+7)=='-'){
CHAR *p=(PCHAR)Add;
while(*p){
if(*p < 0x20 || *p > 0x71)
goto NOT_AMI;
p++;
}
return 1;

}
NOT_AMI:
return 0;
}

VOID CReadBiosIdClass::UnmapPhysicalMemory(DWORD Address)
{
NTSTATUS status;
status = NtUnmapViewOfSection( (HANDLE) -1, (PVOID) Address );
if( !NT_SUCCESS(status))
{
PrintError("Unable to unmap view", status );
}
}

BOOL CReadBiosIdClass::MapPhysicalMemory(HANDLE PhysicalMemory, PDWORD Address, PDWORD Length, PDWORD VirtualAddress)
{
NTSTATUS ntStatus;
PHYSICAL_ADDRESS viewBase;
char error[256];
*VirtualAddress = 0;
viewBase.QuadPart = (ULONGLONG) (*Address);
ntStatus = NtMapViewOfSection (PhysicalMemory,
(HANDLE) -1,
(PVOID *) VirtualAddress,
0L,
*Length,
&viewBase,
Length,
ViewShare,
0,
PAGE_READONLY );
if( !NT_SUCCESS( ntStatus ))
{
sprintf( error, "Could not map view of %X length %X",
*Address, *Length );
PrintError( error, ntStatus );
return FALSE;
}
*Address = viewBase.LowPart;
return TRUE;
}

HANDLE CReadBiosIdClass::OpenPhysicalMemory()
{
NTSTATUS status;
HANDLE physmem;
UNICODE_STRING physmemString;
OBJECT_ATTRIBUTES attributes;
WCHAR physmemName[] = L"\\device\\physicalmemory";
RtlInitUnicodeString( &physmemString, physmemName );
InitializeObjectAttributes( &attributes, &physmemString,
OBJ_CASE_INSENSITIVE, NULL, NULL );
status = NtOpenSection( &physmem, SECTION_MAP_READ, &attributes );
if( !NT_SUCCESS( status ))
{
PrintError( "Could not open \\device\\physicalmemory", status );
return NULL;
}
return physmem;
}

BOOL CReadBiosIdClass::LocateNtdllEntryPoints()
{
if( !(RtlInitUnicodeString = (void (__stdcall *)(PUNICODE_STRING,PCWSTR)) GetProcAddress( GetModuleHandle("ntdll.dll"),"RtlInitUnicodeString" )) )
{
return FALSE;
}
if( !(NtUnmapViewOfSection = (NTSTATUS (__stdcall *)(HANDLE,PVOID)) GetProcAddress( GetModuleHandle("ntdll.dll"),"NtUnmapViewOfSection" )) )
{
return FALSE;
}
if( !(NtOpenSection = (NTSTATUS (__stdcall *)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES)) GetProcAddress( GetModuleHandle("ntdll.dll"),"NtOpenSection" )) )
{
return FALSE;
}
if( !(NtMapViewOfSection = (NTSTATUS (__stdcall *)(IN HANDLE,
IN HANDLE,
IN OUT PVOID *,
IN ULONG ,
IN ULONG ,
IN OUT PLARGE_INTEGER ,
IN OUT PULONG,
IN SECTION_INHERIT ,
IN ULONG ,
IN ULONG )) GetProcAddress( GetModuleHandle("ntdll.dll"),"NtMapViewOfSection" )) )
{
return FALSE;
}
if( !(RtlNtStatusToDosError = (ULONG (__stdcall *)(NTSTATUS)) GetProcAddress( GetModuleHandle("ntdll.dll"),"RtlNtStatusToDosError" )) )
{
return FALSE;
}
return TRUE;
}

void CReadBiosIdClass::PrintError(char *message, NTSTATUS status)
{
char *errMsg;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, RtlNtStatusToDosError( status ),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &errMsg, 0, NULL );
TCHAR errMessage[MAX_PATH];
sprintf(errMessage, _T("%s: %s"), message, errMsg);
//printf("", );
LocalFree( errMsg );
AfxMessageBox(errMessage);
}
taianmonkey 2004-04-18
  • 打赏
  • 举报
回复
获取BIOS ID:
// ReadBiosIdClass.h: interface for the CReadBiosIdClass class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_READBIOSIDCLASS_H__70BE242F_AA8A_4E12_9F03_80EF82BD053D__INCLUDED_)
#define AFX_READBIOSIDCLASS_H__70BE242F_AA8A_4E12_9F03_80EF82BD053D__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define PAGE_NOACCESS 0x01 // winnt
#define PAGE_READONLY 0x02 // winnt
#define PAGE_READWRITE 0x04 // winnt
#define PAGE_WRITECOPY 0x08 // winnt
#define PAGE_EXECUTE 0x10 // winnt
#define PAGE_EXECUTE_READ 0x20 // winnt
#define PAGE_EXECUTE_READWRITE 0x40 // winnt
#define PAGE_EXECUTE_WRITECOPY 0x80 // winnt
#define PAGE_GUARD 0x100 // winnt
#define PAGE_NOCACHE 0x200 // winnt
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; // windbgkd
typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
PWSTR Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

typedef UNICODE_STRING *PUNICODE_STRING;

typedef enum _SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;

#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_VALID_ATTRIBUTES 0x000001F2L

typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;

typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
#define InitializeObjectAttributes( p, n, a, r, s ) { (p)->Length = sizeof( OBJECT_ATTRIBUTES ); (p)->RootDirectory = r; (p)->Attributes = a; (p)->ObjectName = n; (p)->SecurityDescriptor = s; (p)->SecurityQualityOfService = NULL; }

class CReadBiosIdClass
{
public:
void PrintError( char *message, NTSTATUS status );
BOOL LocateNtdllEntryPoints();
HANDLE OpenPhysicalMemory();
BOOL MapPhysicalMemory( HANDLE PhysicalMemory,PDWORD Address, PDWORD Length,PDWORD VirtualAddress );
VOID UnmapPhysicalMemory( DWORD Address );
int biosCheckAMI(DWORD Add);
int biosCheckPhoenix(DWORD Add);
int biosCheckAward(DWORD Add);
CReadBiosIdClass();
virtual ~CReadBiosIdClass();

};

#endif // !defined(AFX_READBIOSIDCLASS_H__70BE242F_AA8A_4E12_9F03_80EF82BD053D__INCLUDED_)
taianmonkey 2004-04-18
  • 打赏
  • 举报
回复
获取CPU序列号:
CString CPUID;

unsigned long s1,s2;
unsigned char vendor_id[]="------------";
char sel;
sel='1';
CString VernderID;
CString MyCpuID,CPUID1,CPUID2;
switch(sel)
{
case '1':
__asm{
xor eax,eax
cpuid
mov dword ptr vendor_id,ebx
mov dword ptr vendor_id[+4],edx
mov dword ptr vendor_id[+8],ecx
}
VernderID.Format("%s-",vendor_id);
__asm{
mov eax,01h
xor edx,edx
cpuid
mov s1,edx
mov s2,eax
}
CPUID1.Format("%08X%08X",s1,s2);
__asm{
mov eax,03h
xor ecx,ecx
xor edx,edx
cpuid
mov s1,edx
mov s2,ecx
}
CPUID2.Format("%08X%08X",s1,s2);
break;
case '2':
{
__asm{
mov ecx,119h
rdmsr
or eax,00200000h
wrmsr
}
}
AfxMessageBox("CPU id is disabled.");
break;
}
MyCpuID = CPUID1+CPUID2;
CPUID = MyCpuID;
return CPUID;
itmaster 2004-04-18
  • 打赏
  • 举报
回复
http://www.vczx.com/article/file/20040312101435_GetCpuInfo.zip
itmaster 2004-04-18
  • 打赏
  • 举报
回复
Intel的CPU检测程序源代码
http://www.codecool.net/CodeDown.Asp?ID=318
ztmyoyo 2004-04-18
  • 打赏
  • 举报
回复
不好意思啊,占个地,我的帖为什么老是发了而没在VC/MFC 硬件/系统论坛显示啊,火大了,然后删帖再发,又口我分,NND.
baixc 2004-04-18
  • 打赏
  • 举报
回复
我也想知道

2,640

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 硬件/系统
社区管理员
  • 硬件/系统社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧