.net - llamar - para que se puede utilizar javascript
¿Cómo puedo usar JavaScript dentro de una macro de Excel? (4)
Aquí hay otra opción para considerar, aunque de ninguna manera afirmo que es la mejor.
- Asegúrate de que la versión de Python compila en IronPython. (No debería haber ningún problema aquí, o solo una pequeña cantidad de portabilidad como máximo).
- Cree una biblioteca de complementos de Excel utilizando C # y haga referencia a IronPython desde allí.
- Envuelva la funcionalidad necesaria en su complemento C # Excel.
Hay una muy buena clase de diferencias alojada por Google aquí:
http://code.google.com/p/google-diff-match-patch/
Lo he usado anteriormente en algunos sitios web, pero ahora necesito usarlo dentro de una macro de Excel para comparar texto entre dos celdas.
Sin embargo, solo está disponible en JavaScript, Python, Java y C ++, no en VBA.
Mis usuarios están limitados a Excel 2003, por lo que una solución .NET pura no funcionaría. Traducir el código a VBA manualmente tomaría demasiado tiempo y dificultaría la actualización.
Una opción que consideré fue compilar el código fuente JavaScript o Java usando los compiladores .NET (JScript.NET o J #), usar Reflector para dar salida como VB.NET, y finalmente degradar el código VB.NET manualmente a VBA, dándome una pureza Solución VBA. Después de tener problemas para compilar con cualquier compilador .NET, abandoné esta ruta.
Asumiendo que podría haber obtenido una biblioteca .NET en funcionamiento, también podría haber usado ExcelDna ( http://www.codeplex.com/exceldna ), un complemento de código abierto de Excel para facilitar la integración del código .NET.
Mi última idea fue alojar un objeto de Internet Explorer, enviar el código fuente de JavaScript y llamarlo. Incluso si hiciera que esto funcionara, supongo que sería muy lento y desordenado.
ACTUALIZACIÓN: ¡Solución encontrada!
Utilicé el método WSC descrito a continuación por la respuesta aceptada. Tuve que cambiar el código de WSC un poco para limpiar los diffs y devolverme una matriz de matrices compatible con 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();
}
Registré el WSC y funcionó bien. El código en VBA para llamarlo es el siguiente:
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
(Traté de mantener un objWMIServicio global único y objDiff para no tener que crear / destruir estos para cada celda, pero no parecía hacer una diferencia en el rendimiento).
Luego escribí mi macro principal. Toma tres parámetros: un rango (una columna) de valores originales, un rango de valores nuevos y un rango donde el diff debe volcar los resultados. Se supone que todos tienen la misma cantidad de filas, no tengo ninguna verificación de errores importante aquí.
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
Las siguientes tres líneas aceleran la actualización sin estropear el modo de cálculo preferido del usuario más adelante:
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
Borrar los difs anteriores, si los hay, es importante:
Erase diffs
Esta prueba es un atajo visual para mis usuarios, así que está claro cuando no hay ningún cambio:
ElseIf OriginalValue = NewValue Then
difftext = "No change."
Erase diffs
Else
Combine todo el texto como el valor de celda delta, si el texto fue idéntico, insertado o eliminado:
diffs = GetDiffs(OriginalValue, NewValue)
For idiff = 0 To UBound(diffs)
thisDiff = diffs(idiff)
difftext = difftext & thisDiff(1)
Next
End If
Debe establecer el valor antes de comenzar el formateo:
DeltaCell.value2 = difftext
Call FormatDiff(diffs, DeltaCell)
Next
Application.ScreenUpdating = True
Application.Calculation = CalcMode
End Sub
Aquí está el código que interpreta los dif y formatea la celda delta:
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
Hay algunas oportunidades para la optimización, pero hasta ahora está funcionando bien. Gracias a todos los que ayudaron!
Mi sugerencia sería que hagas lo que hagas, lo envuelves en un contenedor COM. VBA funciona mejor con objetos COM para que pueda compilar como un Componente .NET y luego exponer como un objeto COM utilizando la funcionalidad de interoperabilidad de .NET.
Como alternativa, también podría considerar el uso de objetos de Windows Scripting Host para ejecutar un archivo Javascript y devolverle el resultado.
Windows Scripting Engine le permitirá ejecutar la biblioteca de JavaScript. Funciona bien en mi experiencia.
El enfoque más simple puede ser incorporar la lógica Javascript diff en un componente COM directamente usando Javascript. Esto es posible a través de algo llamado " Componentes de Windows Script ".
Aquí hay un tutorial sobre cómo crear WSCs .
Un componente de script de Windows es un componente COM que se define en el script. La interfaz del componente es a través de COM, lo que significa que es compatible con VBA. La lógica se implementa en cualquier lenguaje compatible con Windows Scripting Hosting, como JavaScript o VBScript. El WSC se define en un solo archivo XML, que integra la lógica, el componente ID de clase, los métodos, la lógica de registro, etc.
También hay una herramienta disponible para ayudar a crear una CSM . Básicamente se trata de un tipo de asistente que le hace preguntas y rellena la plantilla XML. Yo mismo, comencé con un archivo .wsc de ejemplo y lo edité a mano con un editor de texto. Es bastante auto explicativo.
Un componente COM definido de esta manera en el script (en un archivo .wsc) se puede llamar al igual que cualquier otro componente COM, desde cualquier entorno que pueda bailar con COM.
ACTUALIZACIÓN : Me tomé unos minutos y produje el WSC para GoogleDiff. Aquí está.
<?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>
Para usar esa cosa, debes registrarla. En Explorer, haz clic derecho sobre él y selecciona "Registrarse". o, desde la línea de comando: archivo regsvr32: / c: / scripts / GoogleDiff.wsc
No intenté usarlo desde VBA, pero aquí hay algún código de VBScript que usa el componente.
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