如何在Excelmacros中使用JavaScript?

这里有一个非常酷的差异类:

http://code.google.com/p/google-diff-match-patch/

我之前在一些网站上使用过,但现在我需要 Excelmacros中使用它来比较两个单元格之间的文本。

但是,它只能在JavaScript,Python,Java和C ++中使用,而不能在VBA中使用。

我的用户仅限于Excel 2003,因此纯.NET解决scheme将无法工作。 手动将代码翻译成VBA会花费太多时间,难以升级。

我考虑的一个select是使用.NET编译器(JScript.NET或J#)编译JavaScript或Java源代码,使用Reflector输出为VB.NET,然后将VB.NET代码手动降级到VBA,给我一个纯粹的VBA解决scheme。 在使用任何.NET编译器进行编译之后,我放弃了这个path。

假设我可以得到一个可用的.NET库,我也可以使用ExcelDna( http://www.codeplex.com/exceldna )这个开放源码的Excel加载项来简化.NET代码集成。

我最后的想法是托pipe一个Internet Explorer对象,发送它的JavaScript源,并调用它。 即使我得到这个工作,我的猜测是这将是泥泞缓慢和凌乱。

更新:find解决scheme!

我使用了接受的答案在下面描述的WSC方法。 我不得不稍微更改一下WSC代码来清理差异,并给我一个VBA兼容的数组数组:

function DiffFast(text1, text2) { var d = dmp.diff_main(text1, text2, true); dmp.diff_cleanupSemantic(d); var dictionary = new ActiveXObject("Scripting.Dictionary"); // VBA-compatible array for ( var i = 0; i < d.length; i++ ) { dictionary.add(i, JS2VBArray(d[i])); } return dictionary.Items(); } function JS2VBArray(objJSArray) { var dictionary = new ActiveXObject("Scripting.Dictionary"); for (var i = 0; i < objJSArray.length; i++) { dictionary.add( i, objJSArray[ i ] ); } return dictionary.Items(); } 

我注册了WSC,它工作得很好。 VBA中用于调用它的代码如下所示:

 Public Function GetDiffs(ByVal s1 As String, ByVal s2 As String) As Variant() Dim objWMIService As Object Dim objDiff As Object Set objWMIService = GetObject("winmgmts:") Set objDiff = CreateObject("Google.DiffMatchPath.WSC") GetDiffs = objDiff.DiffFast(s1, s2) Set objDiff = Nothing Set objWMIService = Nothing End Function 

(我试着保留一个单独的全局objWMIService和objDiff,所以我不必为每个单元创build/销毁这些对象,但对性能没有任何影响。)

然后我写了我的主macros。 它需要三个参数:原始值的范围(一列),新值的范围以及diff应该转储结果的范围。 假设所有行都是相同的,我没有在这里进行严重的错误检查。

 Public Sub DiffAndFormat(ByRef OriginalRange As Range, ByRef NewRange As Range, ByRef DeltaRange As Range) Dim idiff As Long Dim thisDiff() As Variant Dim diffop As String Dim difftext As String difftext = "" Dim diffs() As Variant Dim OriginalValue As String Dim NewValue As String Dim DeltaCell As Range Dim row As Integer Dim CalcMode As Integer 

接下来的三行加速了更新,而不会在稍后修改用户的首选计算模式:

  Application.ScreenUpdating = False CalcMode = Application.Calculation Application.Calculation = xlCalculationManual For row = 1 To OriginalRange.Rows.Count difftext = "" OriginalValue = OriginalRange.Cells(row, 1).Value NewValue = NewRange.Cells(row, 1).Value Set DeltaCell = DeltaRange.Cells(row, 1) If OriginalValue = "" And NewValue = "" Then 

删除以前的差异,如果有的话,是重要的:

  Erase diffs 

这个testing对于我的用户来说是一个可视化的快捷方式,所以很清楚什么时候没有任何改变:

  ElseIf OriginalValue = NewValue Then difftext = "No change." Erase diffs Else 

将所有文本合并为增量单元值,不pipe文本是否相同,插入或删除:

  diffs = GetDiffs(OriginalValue, NewValue) For idiff = 0 To UBound(diffs) thisDiff = diffs(idiff) difftext = difftext & thisDiff(1) Next End If 

您必须开始格式化之前设置该值:

  DeltaCell.value2 = difftext Call FormatDiff(diffs, DeltaCell) Next Application.ScreenUpdating = True Application.Calculation = CalcMode End Sub 

以下是解释差异并格式化增量单元的代码:

 Public Sub FormatDiff(ByRef diffs() As Variant, ByVal cell As Range) Dim idiff As Long Dim thisDiff() As Variant Dim diffop As String Dim difftext As String cell.Font.Strikethrough = False cell.Font.ColorIndex = 0 cell.Font.Bold = False If Not diffs Then Exit Sub Dim lastlen As Long Dim thislen As Long lastlen = 1 For idiff = 0 To UBound(diffs) thisDiff = diffs(idiff) diffop = thisDiff(0) thislen = Len(thisDiff(1)) Select Case diffop Case -1 cell.Characters(lastlen, thislen).Font.Strikethrough = True cell.Characters(lastlen, thislen).Font.ColorIndex = 16 ' Dark Gray http://www.microsoft.com/technet/scriptcenter/resources/officetips/mar05/tips0329.mspx Case 1 cell.Characters(lastlen, thislen).Font.Bold = True cell.Characters(lastlen, thislen).Font.ColorIndex = 32 ' Blue End Select lastlen = lastlen + thislen Next End Sub 

有一些优化的机会,但到目前为止,它工作得很好。 感谢所有帮助过的人!

最简单的方法可能是直接使用Javascript将Javascript差异逻辑embedded到COM组件中。 这可以通过称为“ Windows脚本组件 ”的东西来实现。

这里是一个关于创buildWSCs的教程 。

Windows脚本组件是在脚本中定义的COM组件。 组件的接口是通过COM,这意味着它是VBA友好的。 这个逻辑是以任何Windows脚本托pipe兼容的语言实现的,比如JavaScript或者VBScript。 WSC被定义在一个XML文件中,它embedded了逻辑,组件类ID,方法,注册逻辑等等。

还有一个工具可以帮助创build一个WSC 。 基本上这是一个向导式的东西,向您提出问题并填写XML模板。 我自己,我刚从一个例子.wsc文件开始,用文本编辑器手工编辑它。 这是不言而喻的。

在脚本(在.wsc文件中)以这种方式定义的COM组件可以像任何其他COM组件那样在任何可以与COM跳舞的环境中调用。

更新 :我花了几分钟,为GoogleDiff制作WSC。 这里是。

 <?xml version="1.0"?> <package> <component id="Cheeso.Google.DiffMatchPatch"> <comment> COM Wrapper on the Diff/Match/Patch logic published by Google at http://code.google.com/p/google-diff-match-patch/. </comment> <?component error="true" debug="true"?> <registration description="WSC Component for Google Diff/Match/Patch" progid="Cheeso.Google.DiffMatchPatch" version="1.00" classid="{36e400d0-32f7-4778-a521-2a5e1dd7d11c}" remotable="False"> <script language="VBScript"> <![CDATA[ strComponent = "Cheeso's COM wrapper for Google Diff/Match/Patch" Function Register MsgBox strComponent & " - registered." End Function Function Unregister MsgBox strComponent & " - unregistered." End Function ]]> </script> </registration> <public> <method name="Diff"> <parameter name="text1"/> <parameter name="text2"/> </method> <method name="DiffFast"> <parameter name="text1"/> <parameter name="text2"/> </method> </public> <script language="Javascript"> <![CDATA[ // insert original google diff code here... // public methods on the component var dpm = new diff_match_patch(); function Diff(text1, text2) { return dpm.diff_main(text1, text2, false); } function DiffFast(text1, text2) { return dpm.diff_main(text1, text2, true); } ]]> </script> </component> </package> 

要使用这个东西,你必须注册它。 在资源pipe理器中,右键单击它,然后select“注册”。 或者从命令行:regsvr32文件:\ c:\ scripts \ GoogleDiff.wsc

我没有尝试从VBA中使用它,但是这里有一些使用该组件的VBScript代码。

 Sub TestDiff() dim t1 t1 = "The quick brown fox jumped over the lazy dog." dim t2 t2 = "The large fat elephant jumped over the cowering flea." WScript.echo("") WScript.echo("Instantiating a Diff Component ...") dim d set d = WScript.CreateObject("Cheeso.Google.DiffMatchPatch") WScript.echo("Doing the Diff...") x = d.Diff(t1, t2) WScript.echo("") WScript.echo("Result was of type: " & TypeName(x)) ' result is all the diffs, joined by commas. ' Each diff is an integer (position), and a string. These are separated by commas. WScript.echo("Result : " & x) WScript.echo("Transform result...") z= Split(x, ",") WScript.echo("") redim diffs(ubound(z)/2) i = 0 j = 0 For Each item in z If (j = 0) then diffs(i) = item j = j+ 1 Else diffs(i) = diffs(i) & "," & item i = i + 1 j = 0 End If Next WScript.echo("Results:") For Each item in diffs WScript.echo(" " & item) Next WScript.echo("Done.") End Sub 

Windows脚本引擎将允许您运行JavaScript库。 根据我的经验,它运作良好。

我的build议是,无论你做什么你把它包装在一个COM包装。 VBA最好地处理COM对象,所以你可以编译为.NET组件,然后使用.NET的互操作function公开为COM对象。

作为替代scheme,您还可以考虑使用Windows脚本宿主对象执行JavaScript文件并返回结果。

这里有另外一个选项可以考虑,但我绝不表示它是最好的一个。

  • 确保Python版本在IronPython中编译。 (这里不应该有任何问题,或者只有less量的移植。)
  • 使用C#创buildExcel加载项库,并从中引用IronPython。
  • 将必要的function包装在C#Excel加载项中。