> 技术文档 > PowerBuilder操作系统服务与文件压缩解压功能实现

PowerBuilder操作系统服务与文件压缩解压功能实现

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文介绍了PowerBuilder(PB)如何管理操作系统服务和实现文件压缩解压功能。PB是一种适用于数据库应用的开发工具,允许开发者使用扩展库如extlib.dll来执行系统级任务。文章详细说明了枚举和操作系统服务的步骤,以及通过第三方库API实现文件压缩解压的方法。此外,强调了错误处理和代码封装的重要性,以确保应用程序的稳定性和易用性。

1. PowerBuilder与操作系统服务的交互

1.1 PowerBuilder概述

PowerBuilder(PB)是一个成熟的面向对象的应用开发工具,以其快速开发能力和强大的数据窗口功能闻名。PB在处理用户界面和数据访问方面表现出色,但在与系统底层交互方面存在一定的局限性。尽管如此,PB仍然提供了一系列接口和方法,使得开发者可以调用操作系统服务,进行诸如启动、停止、暂停和恢复服务等操作。

1.2 PB与操作系统服务交互的基本能力

PB通过调用外部命令、使用Windows API或者集成中间件组件来与操作系统服务进行交互。它能够启动和停止Windows服务,并可以通过API调用来获取服务的状态信息。然而,在执行这些操作时,开发者需要密切关注操作系统的权限要求和PB本身的安全策略。

1.3 实践操作的要点

在实际的PB应用中,与系统服务的交互不仅需要了解相关的技术细节,还应考虑到操作系统的安全机制和PB的安全管理策略。使用PB进行服务交互时,通常涉及到执行特定的系统命令或者调用PB提供的函数。例如, Run 函数可以用于启动一个服务,而 RegCreateKey RegCloseKey 可以用来操作服务注册表。在实践中,这些操作步骤需谨慎执行,以避免影响系统的稳定性和安全性。下面是一个简单的PB代码示例,展示如何启动一个服务:

// PB中启动服务的代码示例string ls_service_name = \"MyService\"Run(ls_service_name + \" /start\", \"\", RunStyle minimized!, wait!, ReturnStatus)

本章为PowerBuilder与操作系统服务的交互提供了一个基础的入门,接下来的章节将深入探讨PB中的文件压缩解压功能的实现。

2. 文件压缩解压功能在PB中的实现

2.1 文件压缩解压功能的理论基础

2.1.1 压缩解压算法的基本概念

文件压缩与解压缩是数据处理中的核心概念,它们涉及将数据以更少的空间表示,同时仍保持数据的完整性和可恢复性。压缩算法通过识别并消除数据中的冗余来减少文件大小,而解压算法则能够将压缩后的数据恢复到其原始状态。

在理想情况下,压缩算法应该能够提供较高的压缩比,同时在解压时最小化计算资源的消耗。一些常用的压缩算法包括ZIP、RAR、LZ77、LZ78、Huffman编码等,它们各自具有不同的优势和应用场景。例如,ZIP广泛应用于压缩文档和归档,Huffman编码在数据通信中常用以提高传输效率。

2.1.2 常见压缩格式的对比分析

不同的压缩格式有不同的压缩率、压缩速度和解压速度。对于开发者而言,选择合适的压缩格式取决于特定需求,比如是否需要跨平台支持、是否需要高比率压缩或者快速解压等。以下是对几种常见压缩格式的对比分析:

  • ZIP : 一种广泛使用的压缩格式,支持较高的压缩率和良好的跨平台兼容性。ZIP格式支持密码保护和文件分卷压缩。
  • RAR : 由RarLab开发,具有比ZIP更高的压缩率,但解压速度较慢,且不是开源格式。RAR文件通常用于备份和存储大量数据。
  • 7z : 7-Zip压缩格式,使用了高压缩率的算法如LZMA和PPMd。支持创建自解压档案,但不支持跨平台。
    通过对比,可以看出每种压缩格式各有千秋,开发者需要根据应用需求、目标用户和平台等因素来选择最合适的压缩算法。

2.2 PB内置功能的局限性与需求分析

2.2.1 PB在文件处理方面的内建支持

PowerBuilder (PB) 作为一个快速应用开发(RAD)工具,提供了强大的对象和事件驱动模型,以及丰富的内置功能,用于处理日常的文件操作,如创建、读取、写入、重命名等。然而,PB的内建功能在文件压缩与解压缩方面存在明显局限性,它并没有提供直接的API支持这些高级操作。

2.2.2 现有功能的不足及扩展需求

由于PB缺乏直接的压缩和解压功能,开发者在需要这些功能时通常会面临两个问题。一是需要依赖外部工具或服务进行压缩和解压,这可能会增加软件的复杂度并降低性能;二是自行实现压缩和解压算法可能会消耗大量的开发资源和时间。

因此,对于需要在PB应用程序中集成文件压缩解压功能的项目来说,寻求外部支持和扩展现有工具集是必要的。这可能涉及利用第三方库或调用系统服务实现所需功能。

2.3 使用extlib.dll扩展PB内置功能

2.3.1 extlib.dll的功能简介和配置方法

为了解决PowerBuilder内置功能在文件压缩解压方面的局限性,开发者可以使用第三方提供的动态链接库DLL,例如extlib.dll。extlib.dll是一个功能丰富的库,提供了多种文件处理功能,包括但不限于压缩和解压。它能够帮助开发者以较少的工作量扩展PB应用程序的功能。

要使用extlib.dll,首先需要下载该库并将其添加到PB项目的库列表中。然后,在PB的全局外部函数声明部分添加对应的函数声明。在应用extlib.dll之前,必须确保该DLL文件与PB应用程序在同一目录下,或者在系统的环境变量中已正确配置。

2.3.2 extlib.dll在文件压缩解压中的应用实例

以下是一个使用extlib.dll进行文件压缩的简单示例代码。这个例子展示了如何将文件压缩成ZIP格式,并保存到指定路径。

// 声明外部函数FUNCTION boolean CompressFile(string fileToCompress, string destinationZip) LIBRARY \"extlib.dll\" ALIAS FOR \"PBDLL_CompressFile\"// 使用extlib.dll压缩文件string ls_fileToCompress = \"C:\\path\\to\\your\\file.txt\"string ls_destinationZip = \"C:\\path\\to\\your\\destination.zip\"Boolean lb_success// 调用外部函数进行压缩lb_success = CompressFile(ls_fileToCompress, ls_destinationZip)IF lb_success THEN MessageBox(\"压缩成功\", \"文件已被成功压缩至\" + ls_destinationZip)ELSE MessageBox(\"压缩失败\", \"压缩文件时发生错误\")END IF

在这个示例中, CompressFile 是extlib.dll中提供的一个外部函数,用于压缩指定路径的文件到ZIP格式。代码首先声明了这个外部函数,然后调用它来执行文件压缩操作。这段代码展示了如何在PB中有效地使用第三方库来扩展内置功能的边界。

通过extlib.dll,开发者可以更加灵活地处理文件压缩解压的需求,提高PB应用程序的功能性和用户体验。不过,使用第三方库时,开发者也需要考虑库的稳定性和安全性,以及对目标用户系统兼容性的影响。

3. 枚举系统服务的实现方法

3.1 枚举系统服务的基本原理

3.1.1 服务枚举的定义和重要性

枚举系统服务是指在操作系统中识别并列出所有可用服务的过程。这一过程对于系统管理员以及需要在应用程序中管理服务的开发者来说至关重要。服务枚举不仅可以帮助我们了解系统中安装了哪些服务,还能监控服务的状态,这对于维护系统稳定性和安全性是基础工作。

3.1.2 系统服务枚举的主要技术手段

在不同的操作系统中,枚举服务的技术手段有所不同。在Windows系统中,通常可以使用Win32 API函数,如 EnumServicesStatus EnumServicesStatusEx ,来获取服务的详细信息。在UNIX-like系统中,服务信息通常存储在 /etc/init.d/ 目录下或者使用 systemd 等系统管理工具来管理服务。

3.2 枚举系统服务的具体实现

3.2.1 PB中枚举系统服务的API调用

在PowerBuilder中,可以通过调用外部命令或使用API接口来枚举系统服务。由于PB本身不提供直接枚举系统服务的内建函数,因此我们通常会借助Windows的Service Control Manager (SCM)提供的API接口。以下是一个使用 EnumServicesStatus API在PB中枚举服务的示例代码块:

// 声明外部函数和结构体// Win32 API中枚举服务的函数声明Function boolean EnumServicesStatusW(handle hSCManager, Integer dwServiceType, Integer dwControlsAccepted, ref tagENUM_SERVICE_STATUS lpServices, ref DWORD pcbBufSize, ref DWORD lpServicesReturned, ref DWORD lpResumeHandle, Integer dwServiceState) Library \"advapi32.dll\"// Win32 API中枚举服务的状态结构体声明Structure tagENUM_SERVICE_STATUS dwServiceType as long dwCurrentState as long dwControlsAccepted as long dwWin32ExitCode as long dwServiceSpecificExitCode as long dwCheckPoint as long dwWaitHint as long szServiceName as String * 256 szDisplayName as String * 256End Structure// 使用示例// 需要先获取服务管理器的句柄handle hSCManager = OpenSCManager(null, null, SC_MANAGER_ENUMERATE_SERVICE)// 设置API调用参数tagENUM_SERVICE_STATUS[] serviceStatusListDWORD pcbBufSize = 1024DWORD lpServicesReturned = 0DWORD lpResumeHandle = 0Integer dwServiceState = SERVICE_ACTIVE// 调用API枚举服务Long enumResult = EnumServicesStatusW(hSCManager, SERVICE_WIN32, dwServiceState, serviceStatusList, pcbBufSize, lpServicesReturned, lpResumeHandle, dwServiceState)// 错误处理和检查返回值If enumResult = 0 Then MessageBox(\"Error\", \"EnumServicesStatusW failed with error: \" + Str(GetLastError()))Else // 遍历返回的服务列表 For i = 1 To lpServicesReturned // 输出服务名称 MessageBox(\"Service Name\", serviceStatusList[i].szServiceName) NextEnd If// 关闭服务管理器句柄CloseHandle(hSCManager)

3.2.2 实现过程中的常见问题及解决方案

在实现枚举系统服务的过程中,常见的问题包括权限不足和句柄错误。为了解决这些问题,首先确保程序有足够的权限来访问服务管理器。在PB程序中,可以通过调用 ImpersonateLoggedOnUser 等函数来提升权限。其次,确保正确处理API调用的返回值,以便于问题的诊断和修复。当API调用失败时,使用 GetLastError 函数来获取错误信息并进行相应的错误处理。

在实际应用中,还可能需要循环调用 EnumServicesStatus 来处理可能的大数据集。PB代码需要准备好足够大的数组来接收服务状态,并且在每次调用API后根据返回的 pcbBufSize lpResumeHandle 参数来调整下一次调用。

在下一章节,我们将深入讨论管理系统服务的具体操作和API函数,包括启动、停止和配置服务,以及监控服务状态和处理异常情况。

4. 管理系统服务的具体操作和API函数

在深入探讨如何通过PowerBuilder (PB) 管理系统服务之前,我们需要了解哪些API函数能够实现这一功能,以及它们如何工作的具体细节。系统服务的管理涉及到服务的启动、停止、暂停、恢复和配置等操作。掌握这些操作不仅能够帮助开发者更好地控制系统环境,还可以增强应用程序的健壮性和用户体验。

4.1 管理系统服务的API函数概述

4.1.1 主要API函数的描述和功能

在PB中, PB.INI 文件通常用于包含一些系统级别的配置信息。然而,对于系统服务的管理,主要依赖于外部命令或通过调用操作系统的特定API函数。PB本身提供了调用外部程序的能力,通过一系列的外部函数可以实现对系统服务的管理。

例如, Run 函数可以用来启动一个外部程序,例如服务管理器,但在PB内部并没有直接控制服务的函数。因此,我们主要依赖于Windows API函数如 OpenSCManager , OpenService , StartService , ControlService , CloseServiceHandle 等。

下面代码块展示了如何使用Windows API函数来获取服务控制管理器的句柄:

// 声明API函数Function long OpenSCManagerA( String lpMachineName, String lpSCDB, Long dwDesiredAccess ) Library \"advapi32.dll\"Function long CloseServiceHandle( long hSCObject ) Library \"advapi32.dll\"Function long StartServiceA( long hService, long dwNumServiceArgs, String lpServiceArgVectors ) Library \"advapi32.dll\"// 获取服务控制管理器的句柄Local long lServiceManagerHandle = OpenSCManagerA( \"\", \"\", SERVICE_START OR SERVICE_QUERY_STATUS OR SERVICE_STOP )// 如果返回值非零,则句柄有效If lServiceManagerHandle > 0 Then MessageBox(\"Service Manager Handle\", \"Service Manager Handle: \" + String(lServiceManagerHandle))Else MessageBox(\"Error\", \"Failed to Open Service Manager.\")End If// 关闭句柄CloseServiceHandle(lServiceManagerHandle)

4.1.2 函数调用的具体示例和步骤

在上述代码块中,首先我们声明了需要使用的API函数。 OpenSCManagerA 函数用于打开服务控制管理器数据库以获取服务控制管理器的句柄,而 CloseServiceHandle 函数用于关闭句柄。 StartServiceA 函数用于启动指定的服务。

需要注意的是,API函数的调用涉及到一系列的参数设置和权限要求。在本例中, OpenSCManagerA 函数使用了权限标志 SERVICE_START , SERVICE_QUERY_STATUS , SERVICE_STOP ,这表示我们希望对服务进行启动、查询状态和停止操作。

通过这样的示例,我们可以看到如何在PB中通过Windows API来管理系统服务。

4.2 管理系统服务的操作实践

4.2.1 服务的启动、停止和配置

在实际操作中,管理服务需要了解服务的基本属性,包括服务名称、服务类型、启动类型、依赖关系、服务进程名称等。

  • 启动服务

启动服务通常需要使用 StartServiceA 函数,并传入服务控制管理器的句柄和对应服务的句柄,以及启动参数数组。

  • 停止服务

停止服务可以使用 ControlService 函数,传入服务句柄和服务控制管理器的句柄,指定服务控制请求代码,例如 SERVICE_CONTROL_STOP

  • 配置服务

配置服务可能需要修改注册表项或通过服务配置编辑器来完成。PB不直接支持这些操作,但可以通过调用外部程序或脚本来实现。

4.2.2 服务状态的监控和异常处理

服务的状态监控是通过查询服务状态完成的,使用 QueryServiceStatus 函数可以获取当前服务的状态信息。异常处理同样重要,需要编写代码来处理如权限不足、服务不存在或状态不可更改等问题。

下面是一个示例代码块,展示了如何检查服务是否运行,并尝试启动未运行的服务:

Function boolean CheckServiceStatus( String serviceName ) Local String lServiceName Local long lServiceStatus Local long lServiceHandle lServiceHandle = OpenServiceA( \"\", serviceName, SERVICE_QUERY_STATUS ) If lServiceHandle > 0 Then If QueryServiceStatus( lServiceHandle, lServiceStatus ) Then If lServiceStatus = SERVICE_RUNNING Then Return True End If End If CloseServiceHandle( lServiceHandle ) End If Return FalseEnd FunctionFunction boolean StartServiceIfStopped( String serviceName ) If Not CheckServiceStatus( serviceName ) Then // 服务未运行,尝试启动服务 // 注意:此处需要实现启动服务的逻辑 Return True End If Return FalseEnd Function

在这个示例中,我们首先定义了一个函数 CheckServiceStatus 用于查询服务的状态,然后定义了 StartServiceIfStopped 函数用于启动停止的服务。

通过这些实践操作,我们可以有效地控制和管理系统服务,确保系统环境的稳定性和应用程序的可靠运行。

5. 错误处理在服务管理中的重要性

5.1 错误处理的基本原则和方法

5.1.1 错误处理的必要性和对稳定性的影响

在服务管理系统中,错误处理是确保系统稳定性和可靠性运行的关键组成部分。如果没有有效的错误处理机制,服务管理过程中的小问题可能会迅速升级成为系统崩溃或者数据损坏的重大事故。错误处理能够帮助开发者和系统管理员及时发现和解决问题,减少系统停机时间,保障服务的连续性。

错误处理的必要性可以从以下几个方面来考虑:

  • 系统安全 :错误处理能够防止潜在的安全漏洞,如缓冲区溢出、未授权的系统访问等。
  • 数据保护 :通过错误处理,系统可以及时纠正数据处理中的异常,避免数据损坏或丢失。
  • 用户体验 :良好的错误处理能够向用户明确错误原因,避免用户的困惑,提升用户体验。
  • 系统维护 :错误处理有助于记录错误发生的环境和情况,为系统维护和后续的错误诊断提供重要信息。

5.1.2 PB中错误处理的基本策略和实现方式

在PowerBuilder (PB) 中,错误处理通常涉及以下几个方面:

  • 异常捕获和处理 :使用 TRY...CATCH...FINALLY 结构捕获运行时错误。
  • 错误代码 :为不同的错误情况定义特定的错误代码。
  • 日志记录 :记录错误信息,用于事后分析和调试。

PB提供了 ERRORCODE() 函数用于获取错误代码, SYBERROR 函数用于获取错误消息。同时,PB还允许使用自定义的错误处理类来封装复杂的错误处理逻辑。

下面是一个简单的PB中错误处理的代码示例:

// 定义一个用于错误处理的自定义对象CustomError objErrorTRY // 一些可能抛出异常的操作 // 例如:访问数据库,调用外部程序等 // ... // 假设在此处发生了错误 RAISE Exception \"发生了一个已知错误\"CATCH Exception as objEx objError = Create CustomError objError.ErrorNumber = GetTransObject().LastErrorCode objError.ErrorDescription = GetTransObject().LastErrorText objError.ErrorLocation = GetTransObject().LastErrorLocation MessageBox(\"错误信息\", objError.ToString())END CATCHFINALLY // 清理资源 // ...END FINALLY

在上述代码中,我们使用了 TRY...CATCH...FINALLY 结构来捕获异常。任何在 TRY 块中抛出的异常都会被 CATCH 块捕获,并可以进行处理。 FINALLY 块则用来执行无论是否发生异常都需要执行的清理工作。

5.2 错误处理的实践应用

5.2.1 常见错误类型和分析

在管理系统服务的过程中,会遇到各种类型的错误,以下是一些常见的错误类型及其分析:

  • 资源访问错误 :这类错误通常发生在尝试访问或修改受保护的系统资源时,例如文件权限不足、端口已被占用等。
  • 数据库操作错误 :这类错误包括数据库连接失败、SQL语句错误、事务处理问题等。
  • 系统调用错误 :调用外部系统服务或命令时的错误,如返回值非预期、服务未运行等。
  • 网络通信错误 :与网络相关的错误,例如网络不可达、数据包丢失等。
  • 用户输入错误 :不合法的用户输入导致的错误,如格式不正确的数据、必填项缺失等。

每一种错误类型都需要开发者根据实际情况来编写特定的错误处理代码,并且在错误发生时提供详细的错误信息。

5.2.2 实例演示错误处理在服务管理中的应用

让我们来演示一个具体的例子,说明如何在服务管理中应用错误处理。以下是一个使用PB来启动Windows服务并处理可能出现的错误的脚本:

// 定义服务管理对象PowerService objService// 服务管理操作// 假设要启动的Windows服务名为\"MyService\"string ls_serviceName = \"MyService\"int li_returnCodeTRY // 尝试启动服务 li_returnCode = objService.StartService(ls_serviceName) IF li_returnCode = 0 THEN MessageBox(\"成功\", \"服务 \" + ls_serviceName + \" 已成功启动。\") ELSE RAISE Exception \"无法启动服务,错误代码:\" + String(li_returnCode) END IFCATCH Exception as objEx // 弹出错误信息 MessageBox(\"错误\", \"启动服务时发生错误:\" + objEx.Message)END CATCH

在上述脚本中,我们尝试启动一个名为”MyService”的服务,并根据返回代码判断启动是否成功。如果不成功,我们使用 RAISE 关键字抛出异常,并通过 MessageBox 函数向用户显示错误信息。这种错误处理机制能够确保用户知道什么环节出现了问题,同时也保留了错误代码和相关信息,便于开发人员后续调试。

通过本节的学习,读者应已经具备了基本的错误处理概念和PB中错误处理的实践应用技巧。这些知识对于编写健壮的服务管理程序至关重要。

6. 第三方库的压缩解压算法应用

在现代应用开发中,合理利用第三方库能够极大提升开发效率,并解决一些复杂的任务。在PowerBuilder(PB)中,通过集成第三方库,比如zlib和7-Zip,我们可以实现更为高效和功能丰富的文件压缩与解压功能。

6.1 第三方库如zlib和7-Zip的压缩解压算法

6.1.1 zlib和7-Zip的算法特点和适用场景

zlib是一个广泛使用的数据压缩库,它提供了一系列用于数据压缩的算法,其中最著名的是DEFLATE压缩算法。zlib通常用于网络传输压缩和存储空间优化,具有较高的压缩比和较快的处理速度。

7-Zip是一种文件压缩软件,它使用了名为7z的压缩格式。7-Zip的7z格式可以实现更高的压缩比,尤其擅长于压缩大型文件。它的算法支持多种压缩方式和强大的过滤功能。

在PB中使用这些第三方库可以有效地处理文件压缩与解压的需求,特别是在需要高效率和高压缩率的场合。

6.1.2 第三方库压缩解压算法的集成和使用

为了在PB中集成zlib和7-Zip算法,首先需要下载这些库的相关文件,比如DLL和头文件,并按照PB的调用规范进行配置。通过创建外部函数声明和外部库声明,PB就能够调用这些库提供的接口函数。

使用时,首先需要初始化压缩或解压环境,然后进行相应的数据处理,并在完成后清理环境。例如,在PB中使用zlib进行压缩的代码片段如下:

// 声明外部函数Function boolean zlibInit() Library \"zlib.dll\" Alias For \"zlibInit\" Function integer zlibCompress( ref byte pbuffOut[], ref long plBuffOutLength, byte pbuffIn[], long lBuffInLength) Library \"zlib.dll\" Alias For \"zlibCompress\" Function boolean zlibCleanup() Library \"zlib.dll\" Alias For \"zlibCleanup\"// 使用zlib压缩数据long ll_sizebyte lv_data[]byte lv_compressed_data[]boolean lb_resultll_size = Len( lv_data )lb_result = zlibInit()// 调用压缩函数lv_compressed_data[] = Nulllb_result = zlibCompress( lv_compressed_data[], ll_size, lv_data[], ll_size )// 结束压缩过程lb_result = zlibCleanup()

6.2 动态链接库(DLL)在PB中的应用

6.2.1 DLL的作用和在PB中的集成方法

动态链接库(DLL)是一种模块化的代码库,它可以被多个程序同时调用,节省内存并增强程序的可维护性。在PB中,集成DLL库需要创建外部函数声明和外部库声明,这样PB才能识别并调用DLL中的函数。

6.2.2 自定义对象或函数库的封装和复用策略

集成外部库之后,可以创建自定义的对象或函数库进行封装,实现功能的复用。通过定义类和函数,将相关操作封装起来,并提供一个简洁的接口供其他脚本或窗口使用,可以极大提升开发效率和代码的可读性。

例如,创建一个专门进行压缩和解压操作的自定义对象,用户只需调用对象的方法即可完成复杂的文件处理任务。下面是创建这样一个对象的基本框架:

// 自定义压缩对象的声明Class ZipUtil Extends Object Function boolean CompressFile(string as_file, string as_output) Library \"zlib.dll\" Alias For \"zlibCompressFile\" Function boolean DecompressFile(string as_file, string as_output) Library \"zlib.dll\" Alias For \"zlibDecompressFile\"End Class// 使用自定义对象进行文件压缩ZipUtil lo_zip_utillo_zip_util = Create ZipUtilIf lo_zip_util.CompressFile(\"C:\\input.txt\", \"C:\\output.zip\") Then MessageBox(\"压缩成功\", \"压缩文件成功完成。\")Else MessageBox(\"压缩失败\", \"文件压缩失败,请检查路径和文件状态。\")End If

这样的封装不仅提高了代码的可重用性,也便于后期的维护和功能升级。通过将复杂的库操作封装在对象中,PB开发者可以更加专注于业务逻辑的实现,而无需关心底层的复杂性。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文介绍了PowerBuilder(PB)如何管理操作系统服务和实现文件压缩解压功能。PB是一种适用于数据库应用的开发工具,允许开发者使用扩展库如extlib.dll来执行系统级任务。文章详细说明了枚举和操作系统服务的步骤,以及通过第三方库API实现文件压缩解压的方法。此外,强调了错误处理和代码封装的重要性,以确保应用程序的稳定性和易用性。

本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

陕汽配件