有没有办法从命令行在EA中运行VbScript?

时间:2020-05-04 13:45:13

标签: vbscript enterprise-architect

我想从命令行运行EA编写的VBScript代码(作为夜间工作)。除了将其复制到.vbs文件并像这样运行它之外,是否有其他方法可以做到?我的脚本中有一堆!INC-ludes,这意味着要进行双重维护才能使两个版本都保持最新。有解决办法吗?

1 个答案:

答案 0 :(得分:2)

此处没有开箱即用的解决方案。但是您可以编写VBScript代码来模仿EA在脚本运行时的功能,即

  • 从t_script表中获取脚本代码
  • 检索所有遮挡(!INC script_group.script_name)
  • 用随附的脚本文本替换!INC行
  • 将脚本放入.VBS临时文件中并运行它

此外,您至少需要在运行脚本时替换EA中易于使用的所有功能。

  • 将存储库对象的实例创建为全局变量
  • 替换所有Session.Output语句以获取到控制台的输出(或者,您可以实现并实例化自己的Session类)

由于EA是32位应用程序,因此您需要调用32位版本的脚本引擎(cscript.exe)来运行与EA API通信的脚本,通常在C:\ Windows \ SysWOW64 \ cscript.exe

以上所有任务均被编译为以下脚本代码:

option explicit
'(c) Martin Jecny 2020

'WScript.Echo "Scripting Engine: " & ScriptEngine & " ver. " & ScriptEngineMajorVersion & "." & ScriptEngineMinorVersion & "." & ScriptEngineBuildVersion
'WScript.Echo "Creating Repository object"
dim Repository 'as EA.Repository  'defined as global, as it is default in EA environment
set Repository=CreateObject("EA.Repository")
dim ses 'as EA.Session
WScript.Echo "Start: " & Now
runIt(WScript.Arguments) 'complete script name, -v|--verbose
WScript.Echo "End: " & Now
'cleanup
on error resume next
Repository.exit
set Repository=Nothing
on error goto 0

sub runIt(argList)
  dim result 'as Variant
  if argList.Count <1 then
    WScript.Echo "Usage: runEaScript <scriptGroup>.<scriptName> [-v|--verbose] [-c|--copysource]"
    result=cleanup(Nothing)
    WScript.Quit(0)
  end if  

'parse arguments
  dim scriptFullName 'as String
  scriptFullName=argList(0)
  dim arg
  dim verbose 'as Boolean
  verbose=false
  dim copysource 'as Bollean
  copysource=false
  for each arg in argList
    select case arg
      case "-v", "--verbose"
        WScript.Echo "Verbose mode on"
        verbose=true
      case "-c", "--copysource"
        copysource=true
        if verbose then
          WScript.Echo "Copy of the produced code will be stored to current directory"
        end if
    end select
  next

  if verbose then WScript.Echo "Requested script to run: '" & scriptFullName & "'"
  if verbose then WScript.Echo "Opening cloud Repository ..."
  Repository.OpenFile ("mycloudrepository --- ;Connect=Cloud=protocol:http,address:cloudhost,port:80;Data Source=modelname;DSN=modelname;LazyLoad=1;")

  if verbose then WScript.Echo "Retrieving main script code ..."
  dim sql 'as String
  dim mainScriptCode 'as String
  mainScriptCode=getScriptCode(scriptFullName)
  if Len(mainScriptCode)<1 then 
    WScript.Echo "500002" & ": " & "Main script code retrieval failed."
    result=cleanup(Nothing)
    WScript.Quit(3)
  end if
  if verbose then WScript.Echo "Resolving !INCludes ..."
  dim startPos 'as Integer 'position of !INC in the code
  dim endOfPreviousLinePos 'as Integer 'position before start of the !INC line
  dim startToInc 'as String ' string between start of line and !INC directive
  dim endLinePos 'as Integer 'end position of !INC line
  dim endIncPos 'as Integer 'end of included script name within the line
  startPos=1 'start position of !INC in script code
  endLinePos=0 
  endIncPos=0 
  dim includeList 'as Scripting.Dictionary 'list of already included scripts
  set includeList=CreateObject("Scripting.Dictionary")
  includeList.RemoveAll
  dim includeString 'as String '!INC <script> string
  dim toBeReplaced 'as String 'usualy full line with !INC string
  do while startPos<>0

    'detect !INC
    startPos = InStr(1,mainScriptCode,"!INC ")

    'detection and removal of !INC within commented line
    if startPos > 0  then       
     endLinePos=InStr(startPos,mainScriptCode,chr(10))
     endOfPreviousLinePos=InStrRev(mainScriptCode, chr(10),startPos)
     if endOfPreviousLinePos <> (startPos-1) then
       startToInc=mid(mainScriptCode,endOfPreviousLinePos,startPos-endOfPreviousLinePos)
       if InStr(startToInc,"'")>0 then
         if verbose then WScript.Echo "Skipping commented reference " & startToInc & toBeReplaced
         toBeReplaced=mid(mainScriptCode,startPos,endLinePos-startPos)
         mainScriptCode=Replace(mainScriptCode,toBeReplaced,"",1,1)
         startPos=InStr(1,mainScriptCode,"!INC ")
       end if
     end if
    end if

    'including the code if not already included
    if startPos > 0  then    
      endLinePos=InStr(startPos,mainScriptCode,chr(10))
      includeString=trim(mid(mainScriptCode,startPos+5,endLinePos-(startPos+5))) 'ommit !INC string
      toBeReplaced=mid(mainScriptCode,startPos,endLinePos-startPos)
      'remove comment from reference line
      endIncPos=InStr(1,includeString,"'") 'comment?
      if endIncPos>0 then 'strip comment after reference
        includeString=left(includeString,endIncPos-1)
      end if
      includeString=trim(includeString)
      Err.Clear 'probably not necessary, just for sure
      on error resume next
        includeList.Add includeString,includeString 'Dictionary object has natively unique index
        if Err.Number >0  then 'already exists 
          if verbose then WScript.Echo includeString & " already included, removing the reference."
          mainScriptCode=Replace(mainScriptCode,toBeReplaced,"",1,1)
        else 'new one found
          if verbose then WScript.Echo "Including '" & includeString & "'"
          mainScriptCode=Replace(mainScriptCode,toBeReplaced,getScriptCode(includeString),1,1)
        end if
      on error goto 0
    end if
  loop

  'adapt code for running in pure VBS environment
  if verbose then WScript.Echo "Adapting the code ..."
  mainScriptCode=adaptToPureVbsCode(mainScriptCode)

  'make file with the code to run
  dim tempFileName 'as String
  dim fso 'as Scripting.FileSystemObject
  dim tempFolder 'as Folder
  set fso=CreateObject("Scripting.FileSystemObject")
  set tempFolder=fso.GetSpecialFolder(2) 'get temp diectory
  tempFileName=fso.GetSpecialFolder(2).Path & "\" & fso.getTempName
  dim mainScriptFile 'as File
  set mainScriptFile=fso.createTextFile(tempFileName)
  result=mainScriptFile.Write(mainScriptCode) '@TODO error handling
  result=mainScriptFile.Close
  if verbose then WScript.Echo "Written to file: " & tempFileName
  if copysource then
    dim scriptdir 'as Folder
    scriptdir = fso.GetParentFolderName(WScript.ScriptFullName)
    result=fso.CopyFile (tempFileName, scriptdir & scripFullName& ".vbs",true) 'overwrite allowed
  end if
  executeGlobal fso.openTextFile( tempFileName).readAll() 'run the complete script from temporary file

  if verbose then Wscript.Echo "000000" & ": " & "Successful exit"
  WScript.Quit(0)
end sub


function getScriptCode(scriptFullName)
  if Len(scriptFullName)<1 then
    WScript.Echo "500001" & ": " & "No script name provided"
    getScriptCode=""
    exit function
  end if
  if InStr(scriptFullName,".")<2 then
    WScript.Echo "500004" & ": " & "No group - provide full script name in the form  <Group>.<Script>"
    getScriptCode=""
    exit function
  end if
  dim dotPos 'as Integer
  dotPos=InStr(scriptFullName,".")
  dim scriptGroupName 'as String
  dim ScriptName 'as String
  scriptGroupName=Left(scriptFullName,dotPos-1)
  scriptName=Mid(scriptFullName,dotPos+1)
  if Len(scriptName)<1 then
    WScript.Echo "500005" & ": " & "No script name - provide full script name in the form  <Group>.<Script>" 
    getScriptCode=""   
  end if
  dim sql
  sql="select s.script from t_script s, t_script g where s.scriptauthor=g.scriptname"
  sql = sql & " and g.script like '" & scriptGroupName & "'"
  sql = sql & " and s.notes like '%Script Name=""" & scriptName & """%'"
  dim scriptCode 'as String
  getScriptCode=getSqlSingleValue(sql)
end function



'* adapts the code to run in pure VBS outside of EA
'* @param String 'original EA VBS code
'* @return String 'code with replacements
function adaptToPureVbsCode(code)       '@TODO: replacement for Session.Input and Session.Prompt
  dim regEx 'as RegExp
  set regEx=New RegExp
  regEx.IgnoreCase=true
  regEx.Global=true
  regEx.Pattern=chr(10)
  'beautification of the code, mainly for debug purposes
  code=regEx.Replace(code,chr(13) & chr(10))
  'redirect outuput commands
  regEx.Pattern="session.output" 'replace output command
  code=regEx.Replace(code,"WScript.Echo")
  'comment out manipulation with script output window
  regEx.Pattern="Repository.EnsureOutputVisible \""Script\"""
  code=regEx.Replace(code,"'"& "Repository.EnsureOutputVisible ""Script""")
  regEx.Pattern="Repository.ClearOutput \""Script\"""  
  code=regEx.Replace(code,"'Repository.ClearOutput ""Script""")
  adaptToPureVbsCode=code
end function




'* returns single (or first) value from single column; SQL query must comply to this; returns empty string if not found
'* @param sql as String  SQL query
'* @return String
public function getSqlSingleValue(sql) 'as String
        dim xmlDoc 'as MSXML2.DomDocument60 '1.2.0
        set xmlDoc=CreateObject("Msxml2.DOMDocument.6.0") '1.2.0
    dim node 'as MSXML2.IXMLDomNode
    xmlDoc.loadXML(Repository.SQLQuery(sql))    '@TODO fails with field names like COUNT(*) on "("; needs escaping
    set node= xmlDoc.selectSingleNode("//Row[1]/child::node()")
    if node is nothing then
        getSqlSingleValue=""
        exit function
    end if
    if len(node.text)>0 then
        getSqlSingleValue=node.text
    else
        getSqlSingleValue=""
    end if
end function

'*@ Cleanup of the environment
'*@param FileSystemObject fso
'*@return void
function cleanup(fso)
  on error resume next
    Repository.CloseFile
    Repository.Exit
    set Repository=nothing
    result=fso.DeleteFile(tempFileName)
    set fso=nothing
  on error goto 0
end function

脚本用法

“ C:\ Windows \ SysWOW64 \ cscript.exe”“ C:\ Users \ user \ Documents \ jobs \ runEaScript.vbs”“我的脚本组。我的脚本名称” --verbose / nologo

必须注意的是,VBScript的文本操作并不是很快,并且脚本准备可能需要一些时间。如果您需要经常运行脚本,请对生成的VBS文件进行一些缓存。