programing

엑셀 매크로 내에서 자바스크립트를 어떻게 사용할 수 있습니까?

padding 2023. 6. 17. 08:45
반응형

엑셀 매크로 내에서 자바스크립트를 어떻게 사용할 수 있습니까?

Google에서 주최하는 정말 멋진 Diff 클래스가 있습니다.

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

예전에 몇 개의 웹 사이트에서 사용한 적이 있지만, 지금은 두 셀 간의 텍스트를 비교하기 위해 Excel 매크로 내에서 사용해야 합니다.

그러나 VBA가 아닌 JavaScript, Python, Java 및 C++에서만 사용할 수 있습니다.

사용자는 Excel 2003으로 제한되므로 순수한 .NET 솔루션으로는 작동하지 않습니다.코드를 수동으로 VBA로 변환하면 시간이 너무 많이 걸리고 업그레이드가 어려워집니다.

고려한 한 가지 옵션은 .NET 컴파일러(JScript.NET 또는 J#)를 사용하여 JavaScript 또는 Java 소스를 컴파일하고 Reflector를 사용하여 VB.NET으로 출력한 다음 마지막으로 VB.NET 코드를 VBA로 수동으로 다운그레이드하여 순수한 VBA 솔루션을 제공하는 것이었습니다.로 컴파일하는 데 문제가 발생한 후.NET 컴파일러, 이 경로를 포기했습니다.

.NET 라이브러리를 사용할 수 있었다고 가정하면, 오픈 소스 Excel 추가 기능인 ExcelDna(http://www.codeplex.com/exceldna), 를 사용하여 .NET 코드 통합을 쉽게 할 수 있었을 것입니다.

제 마지막 아이디어는 Internet Explorer 개체를 호스트하여 JavaScript 소스를 보내고 호출하는 것이었습니다.제가 이걸 작동시키더라도, 제 추측으로는 그것은 더디고 지저분할 것입니다.

업데이트: 솔루션을 찾았습니다!

저는 아래에 명시된 WSC 방법을 채택하였습니다.문제를 해결하고 VBA 호환 어레이를 제공하기 위해 WSC 코드를 약간 변경해야 했습니다.

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

(글로벌 obj를 하나만 유지하려고 노력했습니다.WMI 서비스와 objDiff는 각 셀에 대해 이러한 기능을 생성/제거할 필요가 없지만 성능에는 차이가 없는 것 같습니다.)

그리고 나서 저는 제 메인 매크로를 작성했습니다.원래 값의 범위(한 열), 새 값의 범위, 디프가 결과를 덤프해야 하는 범위 등 세 가지 매개 변수가 필요합니다.모두 행 수가 동일한 것으로 가정되며, 심각한 오류 검사는 진행하지 않습니다.

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

이 테스트는 사용자에게 시각적인 바로 가기이므로 변경 사항이 전혀 없을 때 명확합니다.

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

텍스트가 동일한지, 삽입되었는지 또는 삭제되었는지 여부에 관계없이 모든 텍스트를 델타 셀 값으로 결합합니다.

            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 diff 로직을 COM 구성 요소에 포함시키는 것일 수 있습니다.이것은 "Windows Script Components"라는 것을 통해 가능합니다.

다음은 WSC를 만드는 방법에 대한 튜토리얼입니다.

윈도우즈 스크립트 구성 요소는 스크립트에 정의된 COM 구성 요소입니다.구성 요소에 대한 인터페이스는 COM을 통해 이루어지며, 이는 VBA 친화적임을 의미합니다.이 논리는 JavaScript 또는 VBScript와 같은 Windows Script 호스팅 호환 언어로 구현됩니다.WSC는 논리, 구성 요소 클래스 ID, 메서드, 등록 논리 등이 포함된 단일 XML 파일에 정의됩니다.

WSC를 만드는도움이 되는 도구도 있습니다.기본적으로 사용자에게 질문을 하고 XML 템플릿을 채우는 마법사 유형입니다.저는 샘플 .wsc 파일로 시작해서 텍스트 편집기로 손으로 편집했습니다.그것은 꽤 자기 설명적입니다.

스크립트에서 이렇게 정의된 COM 구성 요소(.wsc 파일)는 다른 COM 구성 요소와 마찬가지로 COM과 함께 춤을 출 수 있는 모든 환경에서 호출할 수 있습니다.

업데이트: 잠시 시간을 내어 Google Diff용 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>

그것을 사용하려면, 당신은 그것을 해야 합니다.탐색기에서 마우스 오른쪽 단추를 클릭하고 "등록"을 선택하거나 명령줄에서 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 라이브러리를 실행할 수 있습니다.그것은 제 경험으로 잘 작동합니다.

제 제안은 당신이 무엇을 하든지 COM 래퍼로 포장하는 것입니다. VBA는 COM 객체를 가장 잘 처리하여 당신이 .NET 컴포넌트로 컴파일한 다음 .NET의 인터op 기능을 사용하여 COM 객체로 노출할 수 있도록 합니다.

또는 Windows Scripting Host 개체를 사용하여 Javascript 파일을 실행하고 결과를 반환하는 방법도 검토할 수 있습니다.

여기 고려해야 할 다른 옵션이 있습니다. 비록 제가 그것이 가장 좋은 것이라고 결코 말하지는 않지만요.

  • Python 버전이 IronPython으로 컴파일되는지 확인하십시오. (여기에는 문제가 없거나 최대 소량의 포팅만 가능합니다.)
  • C#을 사용하여 Excel 추가 라이브러리를 만들고 여기서 IronPython을 참조합니다.
  • 필요한 기능을 C# Excel 추가 기능으로 래핑합니다.

언급URL : https://stackoverflow.com/questions/848246/how-can-i-use-javascript-within-an-excel-macro

반응형