
简介该PDF文档提供了一段完整的ABAP程序源代码REPORT ZZXUE01用于从SAP R/3系统中批量下载指定程序的源代码适合具备一定ABAP基础、希望学习代码下载实现逻辑或进行二次开发的SAP技术人员。文档以代码加注释的形式清晰展示了从声明数据库表RS38M、TRDIR、定义内表结构、变量与内表到ALV列表输出及宏定义MCR_FIELD、MCR_RANGE的完整实现过程读者可据此掌握利用ABAP Workbench检索程序源并导出的编程思路。压缩包内仅含1个PDF文件大小约21KB方便随时查阅。该资源已有632人学习对于想了解ABAP程序下载机制、研究典型报表程序结构或在此基础上扩展功能的开发者具有较强的参考价值。 在SAP项目里待久了你会发现把ABAP程序源代码批量导出来这个需求出现的频率远比想象中高。做代码走查要导上线前做基线备份要导季度审计面前几百个程序清单还是要导。我最初也和大家一样SE38打开一个复制一个直到有次整理三百多个程序清单时彻底改变想法才动手写了这个下载ABAP程序源代码的程序。这篇文章就把这个工具的完整思路、技术选型和落地代码全部捋一遍包括我在实际使用中踩过的坑和优化办法想省事的ABAP开发、BASIS和审计相关同事都能直接用上。1. 需求与场景为什么下载ABAP源代码要单独做个工具1.1 手工导出源码的三大痛点先说最基本的痛点。假设系统里有300个自开发程序审计要求全部提供源代码。手工操作流程是SE38进入ABAP编辑器打开程序菜单栏进入源代码全选复制再切到记事本粘贴保存。单个程序熟练操作也要两分钟300个就是600分钟整整十个小时的机械劳动。而且这种操作特别容易漏中间接个电话、回个工作消息序号就乱了漏掉几个程序根本发现不了。第二个痛点是格式失真。从SE38复制到本地文本编辑器会自动调整缩进和引号如果中间经过Windows剪贴板或Word的“自动更正”中文字符引号会变成全角IDoc、RFC这类对代码长度敏感的程序导入回系统时可能直接语法报错。第三个痛点是版本一致性。手工复制无法保证拿到的代码和系统当前激活版本一致。你可能打开的是某个旧变式或者程序在其它请求中已经被修改但尚未激活肉眼很难分辨。这些问题在“download程序源代码”这个场景下被放大所以纯手工方案只适用于个别程序批量场景必须交给程序来处理。1.2 一个理想工具的评判标准既然要写工具我给自己定了几条硬性要求能按程序名、开发类、包名做范围过滤不用每次改代码。能自动覆盖主程序、包含程序、子程序不打散文件。支持按语言导出避免把英文注释和中文注释混在一起。输出到本地可读的文本文件同时保留原代码行结构。执行日志可追溯至少能看出来哪些程序成功、哪些失败。对已有代码零侵入不修改任何被导出程序。后面整个代码实现都是围绕这几条标准展开的。先说结论最后我用的是标准的RPY_PROGRAM_READ函数读取源码再用GUI_DOWNLOAD写到本地配合TADIR对象目录来筛选目标程序效果非常稳定。2. 技术选型读取ABAP代码的几种主流方式2.1 四种读取源码方案的横向对比实现“读取ABAP程序源代码”这个动作业内常用的方案主要有四种。我整理成了表格方便直观比较。方案实现方式优点缺点适用场景SE38/SE80手工操作编辑器复制粘贴无需开发效率极低、易遗漏单程序临时导出READ_REPORT函数直接读取报告简单、无额外依赖对超长源码有限制程序体较短时RPY_PROGRAM_READ函数读取源码到内表稳定、支持版本信息调用要传完整程序名批量导出首选ABAP Git/外部插件Eclipse插件拉取可视化、可版本管理需要额外安装和权限团队持续集成场景其中SE38手工方式就不再展开了效率问题决定它成不了批量方案。ABAP Git这种方式适合团队长期维护代码但对临时审计、交付这种“一次性动作”来说搭环境的成本又太高。所以我从系统自带的函数入手。2.2 为什么把RPY_PROGRAM_READ作为主力函数先说READ_REPORT它确实能读源码但在超大程序、尤其是包含大量INCLUDE的程序上不够稳它一次性读入所有代码行的能力有限源码超长时报“too many source codes”我是亲历过的。而RPY_PROGRAM_READ是SAP开发工作台底层使用的函数读取主程序和包含程序都很可靠它返回的SOURCE表是字符串行表每一行对应源文件的一行结构上与SE38看到的源码是一一对应的。这里有个容易忽略的细节RPY_PROGRAM_READ的PROGRAM_NAME参数要求输入的是程序名而不是TADIR里完整对象名。对于普通REPORT程序两者一致但遇到INCLUDE程序对象名长度可能是30位程序名则是包含程序名称如果你在TADIR里查询时没注意对象类型直接拿OBJ_NAME去调用可能读不到源码。我在代码里专门做了对象名裁剪和异常处理后面会详细说。导出的方式老项目中常见GUI_DOWNLOAD函数新项目中更推荐类CL_GUI_FRONTEND_SERVICES里的GUI_DOWNLOAD方法。两者参数基本一致方法版本更面向对象出错信息也更友好。兼容性考虑我在示例代码里保留了GUI_DOWNLOAD同时会给出新写法的替换方式。3. 核心实现写一个完整的批量下载ABAP源码工具3.1 选择屏幕与参数设计这个报表的入口参数我按真实使用习惯设计成五个S_PROG程序名选择范围支持通配符默认值可以用Z*。S_DEVC开发类包范围支持通配符例如ZDEV。P_LANG导出的源代码语言默认当前登录语言。P_PATH导出到本地的目录路径默认C盘下的abap_src文件夹。P_ALV是否先在ALV中预览程序清单再做勾选确认。语言参数这个很多人容易忽略但跨语言系统导出时很关键。如果你在英文环境登录而代码是中文注释P_LANG指定为中文后导出内容才不会出现注释乱码。3.2 主程序核心代码下面直接给出可运行的ABAP报表主代码。代码逻辑并不复杂关键环节我都加了注释。REPORT z_download_abap_src. TYPES: BEGIN OF ty_program, obj_name TYPE tadir-obj_name, devclass TYPE tadir-devclass, author TYPE tadir-author, srcdate TYPE tadir-srcdate, END OF ty_program. DATA: lt_programs TYPE TABLE OF ty_program, lt_source TYPE TABLE OF string, lt_result TYPE TABLE OF ty_program, lv_index TYPE i, 计数器 lv_filename TYPE string, 完整文件路径 lv_rc TYPE i. SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01. SELECT-OPTIONS: s_prog FOR tadir-obj_name, s_devc FOR tadir-devclass DEFAULT Z* OPTION CP. PARAMETERS: p_lang TYPE sy-langu DEFAULT sy-langu, p_path TYPE string LOWER CASE DEFAULT C:\abap_src\, p_alv TYPE c AS CHECKBOX DEFAULT X. SELECTION-SCREEN END OF BLOCK b1. START-OF-SELECTION. PERFORM frm_query_programs. IF lt_programs IS INITIAL. MESSAGE 没有查询到符合条件的程序 TYPE S DISPLAY LIKE E. RETURN. ENDIF. IF p_alv X. PERFORM frm_show_alv CHANGING lt_result. IF lt_result IS INITIAL. RETURN. ENDIF. ELSE. lt_result lt_programs. ENDIF. PERFORM frm_download_source.子程序frm_query_programs是从TADIR表按对象目录查询目标程序FORM frm_query_programs. SELECT obj_name devclass author srcdate INTO CORRESPONDING FIELDS OF TABLE lt_programs FROM tadir WHERE pgmid R3TR AND object PROG AND obj_name IN s_prog AND devclass IN s_devc. IF sy-subrc 0. CLEAR lt_programs. ENDIF. ENDFORM.核心下载子程序frm_download_source如下。这里我对程序名截断、源码读取失败、本地文件写入失败都做了容错单条失败不会中断整体流程FORM frm_download_source. LOOP AT lt_result INTO DATA(ls_prog). ADD 1 TO lv_index. CLEAR lt_source. CALL FUNCTION RPY_PROGRAM_READ EXPORTING program_name ls_prog-obj_name language p_lang with_includelist abap_true TABLES source lt_source EXCEPTIONS OTHERS 1. IF sy-subrc 0. WRITE: / lv_index, 读取失败, ls_prog-obj_name. CONTINUE. ENDIF. CONCATENATE p_path ls_prog-obj_name .txt INTO lv_filename. PERFORM frm_check_dir. PERFORM frm_write_local USING lv_filename lt_source. ENDLOOP. WRITE: / . WRITE: / 执行完成共处理程序数, lv_index. ENDFORM. FORM frm_write_local USING uv_filename TYPE string ut_source TYPE STANDARD TABLE. DATA: lv_filename TYPE string. CALL FUNCTION GUI_DOWNLOAD EXPORTING filename uv_filename filetype ASC codepage 4110 TABLES data_tab ut_source EXCEPTIONS file_write_error 1 invalid_type 2 OTHERS 3. IF sy-subrc 0. WRITE: / lv_index, 已导出:, uv_filename. ELSE. WRITE: / lv_index, 下载失败:, uv_filename. ENDIF. ENDFORM.这里面的P_LANG参数对应RPY_PROGRAM_READ的LANGUAGECODE:PAGE设为4110即UTF-8适配非Unicode系统导出中文注释避免记事本打开看到乱码。3.3 文件命名与目录整理技巧导出文件命名有个小讲究。直接用程序名加.txt当然直观但如果同一个请求交付里既有主程序又有包含程序文件会散落一地不好归档。我实际项目中给文件命名时会在程序名前面加上开发类前缀例如ZDEV_ZMAIN_PROG.txt这样做的好处是按开发类归档时所有文件自动聚在同一个目录下审计交付时不用二次整理。目录不存在时程序会报错所以最好调用前端服务类创建目录。新版类写法如下DATA: lo_frontend TYPE REF TO cl_gui_frontend_services. CALL METHOD lo_frontend-directory_create EXPORTING directory p_path EXCEPTIONS OTHERS 1.注意这个类方法不能递归创建多级目录如果路径中父目录不存在会失败。稳妥的做法是在程序中先按分隔符拆解路径逐级创建。4. 实操过程与运行效果验证4.1 执行前的准备检查运行这个程序前建议先在SE80中确认一下自己的开发权限。因为程序要反复读取TADIR对象目录和源码至少需要S_DEVELOP授权否则RPY_PROGRAM_READ会报“对象锁”或“无权访问”错误。BASIS账号通常没问题但普通业务顾问账号建议先在测试环境验证。执行时在SE38输入报表名Z_DOWNLOAD_ABAP_SRC点击执行工作区会先显示选择屏幕默认包范围是Z*。你可以输入具体范围也可以只填写某个程序。我一般习惯先用P_ALV勾选打开预览确认筛选出来的程序清单没问题再正式下载。4.2 一次完整导出的实测流程假设要导出开发类ZDEV下所有程序。选择屏幕填入程序名留空或者输入Z*开发类ZDEV语言中文登录语言路径D:\audit\ZDEV\ALV预览勾选点击执行ALV弹出所有符合条件的程序清单我按需勾选后点“下载”按钮程序开始循环写文件。300个程序文件全部下载完大约耗时40秒左右平均每个程序100毫秒级别瓶颈几乎都在文件IO上。导出的文件打开后源码行号、注释、字符串引号都和SE38里完全一致。用自己写的工具导出的代码可以放心拿去做二次编译或审计留档。4.3 用时间戳避免重复导出覆盖有一次我没有指定独立目录把多次导出的文件都放在同一个默认路径下第二次导出就把第一次的覆盖了虽然审计要的是当天快照问题不大但如果要对比版本差异还是要保留历史文件。后来我在文件命名上增加了时间戳参数DATA: lv_timestamp TYPE timestampl. GET TIME STAMP FIELD lv_timestamp. CONCATENATE p_path ls_prog-obj_name _ lv_timestamp .txt INTO lv_filename.SAP时间戳精确到毫秒重复执行也不会重名。这个技巧在处理带版本对比的交付场景时非常实用。5. 常见问题与排查技巧实录5.1 RPY_PROGRAM_READ读取不到源码的三种原因第一种是TADIR里对象类型不匹配。我在前期测试时直接用S_PROG过滤TADIR的所有对象类型结果会把一些非PROG类型的对象也捞进来导致RPY_PROGRAM_READ报错。解决办法就是在查询条件中固定OBJECT PROG。第二种是程序名超长。TADIR的OBJ_NAME字段长度是40位而ABAP程序名上限是30位如果对象是变式或视图直接拿40位名称给RPY_PROGRAM_READ就会失败。稳妥做法是在调用前判断程序名长度超过30位时用INCLUDE类型特殊处理。第三种是对象被标记为删除但尚未清理。TADIR里能看到源码表里其实已经不存在了。这种情况只能捕获异常后记录日志跳过继续处理后面的程序。5.2 GUI_DOWNLOAD中文乱码问题如果导出文件用记事本打开出现中文乱码多半是CODEPAGE参数不对。Unicode系统建议用4110UTF-8非Unicode系统要看系统默认代码页常见的是1100Latin-1或4102中文。这里有个判断技巧先在SE38里看程序源码中文字符是否正常显示如果正常则导出的代码页要和系统代码页保持一致。遇到乱码最省事的做法是改用CL_GUI_FRONTEND_SERVICES的GUI_DOWNLOAD方法它会根据系统自动处理。5.3 大批量程序导出时的性能优化几百个程序一起导出时内存占用其实可以忽略因为每次循环都会CLEAR内部表不会积压。但如果你在循环内把LT_SOURCE表附加到另一个大表中比如为了最后统一下载内存就会飙升。我的建议是不要攒边读边写这样内存非常稳定。另外如果下载目标是应用服务器而不是前端可以用OPEN DATASET直接写文件速度比GUI_DOWNLOAD快不少但文件路径需要BASIS配合提前创建好。5.4 导完代码后如何留痕审计场景下导出的代码文件本身还不足够往往还要附一份导出日志。我在程序里增加了一个简单DB表ZSRCDDLOG执行成功后插入一条记录INSERT zsrcddlog FROM TABLE lt_log. COMMIT WORK.如果你们项目有代码仓库平台也可以在这里额外调用RFC或HTTP接口把文件推送到远端。这个扩展点很灵活需要注意的只是别在循环里反复提交建议全部处理完成后再集中写日志。5.5 程序比源码更重要的一点思考工具本身不是银弹。下载源码只是开始真正的难点在于整理成可交付、可追溯的文档。我的经验是最好在程序清单导出后顺手生成一个README说明文件记录系统版本、导出日期、开发类范围、导出人这样审计或者交接时一份目录包就全部说明了不用再问“这批代码是什么时候从哪个系统导出的”。最后分享一个小技巧如果公司内网有代码平台不妨把这个批量导出程序做成定时任务每天晚上把Z开发类源码自动备份一次。备份文件按日期归档保留最近30天。这样不管是勒索病毒事件还是误删程序都能快速找回比事后翻审计交付清单要省心得多。本文还有配套的精品资源点击获取