Verfasst: 29.04.2006 16:17
bin seit einiger Zeit dran so etwas umzusetzen.
Das Grundgerüst steht schon. Man kann damit ohne
Interfaces zu importieren oder CLSIDs zu kennen
auf Excel Word und Co. zugreifen
Hier ein Beispiel mit Excel in PB 3.94
ist nicht so komfortabel wie das von http://disphelper.sourceforge.net/
aber dafür deutlich schneller.
Es wäre zu überlegen die Objekte in einer LinkedList mit String-Namen
und Zeiger zu sammeln. Wenn man eins weitere Male benötigt,
kann das dann aus der Liste gesucht werden.
Wie man PB-Typen (long, Word, String etc.) automatisiert und komfortabel
in Variants umwandelt, oder umgekehrt, dazu habe ich noch keine Idee.
Müsste sich aber auch realisieren lassen.
_____
schic
Das Grundgerüst steht schon. Man kann damit ohne
Interfaces zu importieren oder CLSIDs zu kennen
auf Excel Word und Co. zugreifen
Hier ein Beispiel mit Excel in PB 3.94
Code: Alles auswählen
; http://support.microsoft.com/kb/216686/EN-US/
; How To Automate Excel From C++ Without Using MFC or #import
; transfered and extend from VC++ to PureBasic from schic
Enumeration ; VARENUM
#VT_EMPTY = 0
#VT_NULL = 1
#VT_I2 = 2
#VT_I4 = 3
#VT_R4 = 4
#VT_R8 = 5
#VT_CY = 6
#VT_DATE = 7
#VT_BSTR = 8
#VT_DISPATCH = 9
#VT_ERROR = 10
#VT_BOOL = 11
#VT_VARIANT = 12
#VT_UNKNOWN = 13
#VT_DECIMAL = 14
#VT_I1 = 16
#VT_UI1 = 17
#VT_UI2 = 18
#VT_UI4 = 19
#VT_I8 = 20
#VT_UI8 = 21
#VT_INT = 22
#VT_UINT = 23
#VT_VOID = 24
#VT_HRESULT = 25
#VT_PTR = 26
#VT_SAFEARRAY = 27
#VT_CARRAY = 28
#VT_USERDEFINED = 29
#VT_LPSTR = 30
#VT_LPWSTR = 31
#VT_RECORD = 36
#VT_INT_PTR = 37
#VT_UINT_PTR = 38
#VT_FILETIME = 64
#VT_BLOB = 65
#VT_STREAM = 66
#VT_STORAGE = 67
#VT_STREAMED_OBJECT = 68
#VT_STORED_OBJECT = 69
#VT_BLOB_OBJECT = 70
#VT_CF = 71
#VT_CLSID = 72
#VT_VERSIONED_STREAM = 73
#VT_BSTR_BLOB = $FFF
#VT_VECTOR = $1000
#VT_ARRAY = $2000
#VT_BYREF = $4000
#VT_RESERVED = $8000
#VT_ILLEGAL = $FFFF
#VT_ILLEGALMASKED = $FFF
#VT_TYPEMASK = $FFF
EndEnumeration
;{-Constants
#CLSCTX_INPROC_SERVER = $1
#CLSCTX_INPROC_HANDLER = $2
#CLSCTX_LOCAL_SERVER = $4
#CLSCTX_REMOTE_SERVER = $10
#CLSCTX_ALL = (#CLSCTX_INPROC_SERVER|#CLSCTX_INPROC_HANDLER|#CLSCTX_LOCAL_SERVER|#CLSCTX_REMOTE_SERVER)
#DISPID_PROPERTYPUT = -3
#DISPATCH_METHOD = 1
#DISPATCH_PROPERTYGET = 2
#DISPATCH_PROPERTYPUT = 4
#DISPATCH_PROPERTYPUTREF = 8
;}
Structure xBRECORD
*pvRecord.l
*pRecInfo.IRecordInfo
EndStructure
Structure xVARIANT
vt.w
wReserved1.w
wReserved2.w
wReserved3.w
StructureUnion
llVal.LARGE_INTEGER
lVal.l
bVal.b
iVal.b
fltVal.f
dblVal.LARGE_INTEGER
boolVal.l
bool.l
scode.l
cyVal.l
date.l
bstrVal.l
*punkVal.IUnknown
;punkVal.l
*pdispVal.IDispatch
parray.l
pbVal.b
piVal.b
plVal.l
pllVal.LARGE_INTEGER
pfltVal.f
pdblVal.LARGE_INTEGER
pboolVal.l
pbool.l
pscode.l
pcyVal.l
pdate.l
pbstrVal.b
ppunkVal.l;.IUnknown
ppdispVal.IDispatch
pparray.l
pvarVal.l;.xVARIANT
*byref
cVal.b
uiVal.b
ulVal.l
ullVal.LARGE_INTEGER
intVal.l
uintVal.l
*pdecVal.f
*pcVal.b
*puiVal.b
*pulVal.l
*pullVal.LARGE_INTEGER
*pintVal.l
*puintVal.l
*record.xBRECORD
EndStructureUnion
EndStructure
Structure DISPPARAMS
*rgvarg.l ;array of arguments xVARIANT
;*rgvarg.Variant[0]
*rgdispidNamedArgs.l ;array of Dispatch IDs of named arguments (argument 0 index)
; rgdispidNamedArgs.l
cArgs.l ;Number of arguments
cNamedArgs.l ;Number of named arguments
EndStructure
Structure EXCEPINFO
wCode.w;
wReserved.w;
bstrSource.s;
bstrDescription.s;
bstrHelpFile.s;
dwHelpContext.l;
pvReserved.l ;
pfnDeferredFillIn.l
scode.l ;
EndStructure
Structure SAFEARRAYBOUND
cElements.l ;ULONG
lLbound.l ;LONG
EndStructure
;{-Globals
Global pApp.IDispatch
Global pvResult.xVARIANT
Global IID_NULL.GUID
;}
Dim varArr.xVARIANT(0)
Procedure AutoWrap(autoTyp.l, *pDisp.IDispatch, name.s, nArgs.l)
dispID.l
exception.EXCEPINFO
dp.DISPPARAMS
dispidNamed = #DISPID_PROPERTYPUT
;Convert to Uni
ptUniName = Ansi2Uni(name)
Debug "getting IDs of names"
;Get DISPID for name passed...
hr = *pDisp\GetIDsOfNames(IID_NULL, @ptUniName, 1, #LOCALE_USER_DEFAULT, @dispID)
Debug "hr GetIDsOfNames: " + Str(hr)
Debug "dispID from " + name + " = " + Str(dispID)
;Build DISPPARAMS
dp\cArgs = nArgs
dp\rgvarg = @varArr(0) ;ArgsArr;pArgs;*var;
;Handle special-case for property-puts!
If autoTyp & #DISPATCH_PROPERTYPUT
dp\cNamedArgs = 1
dp\rgdispidNamedArgs = @dispidNamed
EndIf
;Make the call!
hr = *pDisp\Invoke(dispID, IID_NULL, #LOCALE_SYSTEM_DEFAULT, autoTyp, @dp, @pvResult, @exception, #Null)
Debug "hr Invoke: " + Str(hr)
Debug "##########"
Select hr
Case #DISP_E_EXCEPTION
Debug "exception\scode: " + Str(exception\scode) + " -> " + exception\bstrDescription
EndSelect
; Invoke return values:
; #S_OK = 0 ; Success.
; #DISP_E_BADPARAMCOUNT = -21473525 62 ; The number of elements provided to DISPPARAMS is different from the number of arguments accepted by the method or property.
; #DISP_E_BADVARTYPE = -21473525 68 ; One of the arguments in rgvarg is not a valid variant type.
; #DISP_E_EXCEPTION = -21473525 67 ; The application needs to raise an exception. In this case, the structure passed in pExcepInfo should be filled in.
; #DISP_E_MEMBERNOTFOUND = -21473525 73 ; The requested member does not exist, or the call to Invoke tried to set the value of a read-only property.
; #DISP_E_NONAMEDARGS = -21473525 69 ; This implementation of IDispatch does not support named arguments.
; #DISP_E_OVERFLOW = -21473525 66 ; One of the arguments in rgvarg could not be coerced to the specified type.
; #DISP_E_PARAMNOTFOUND = -21473525 72 ; One of the parameter DISPIDs does not correspond to a parameter on the method. In this case, puArgErr should be set to the first argument that contains the error.
; #DISP_E_TYPEMISMATCH = -21473525 71 ; One or more of the arguments could not be coerced. The index within rgvarg of the first parameter with the incorrect type is returned in the puArgErr parameter.
; #DISP_E_UNKNOWNINTERFACE = -21473525 75 ; The interface identifier passed in riid is not IID_NULL.
; #DISP_E_UNKNOWNLCID = -21473525 64 ; The member being invoked interprets string arguments according to the LCID, and the LCID is not recognized. If the LCID is not needed to interpret arguments, this error should not be returned.
; #DISP_E_PARAMNOTOPTIONAL = -21473525 61 ; A required parameter was omitted.
; -2147024809 ; The parameter is incorrect (80070057)
FreeMemory(ArgsArr)
ProcedureReturn hr
EndProcedure
;{ get application object
CLSID.GUID
hr = CLSIDFromProgID_(Ansi2Uni("Excel.Application"), @CLSID)
Debug hr
Debug CLSID\data1 ;148736 for Excel
pCf.IClassFactory = NULL
IID_IClassFactory.GUID
IID_IClassFactory\data1 = $1
IID_IClassFactory\data4[0] = $C0
IID_IClassFactory\data4[7] = $46
hr = CoGetClassObject_(@CLSID, #CLSCTX_LOCAL_SERVER, #Null, @IID_IClassFactory, @pCf)
Debug "hr CoGetClassObject: " + Hex(hr)
riid.GUID
riid\data1 = $20400
riid\data4[0] = $C0
riid\data4[7] = $46
pApp.IDispatch
ppApp=@pApp
hr = pCf\CreateInstance(#Null, @riid, @pApp)
Debug "hr CreateInstance: " + Hex(hr)
pCf\Release()
;}
;{ make application visible
Dim varArr.xVARIANT(1)
varArr(0)\vt = #VT_I4
varArr(0)\lVal = 1
AutoWrap(#DISPATCH_PROPERTYPUT, pApp, "Visible", 1)
VariantClear_(varArr(0))
;}
;{ get workbooks object, VBA-Code: Set pXlBooks = Application.Workbooks
pXlBooks.IDispatch
VariantInit_(@pvResult);
AutoWrap(#DISPATCH_PROPERTYGET, pApp, "Workbooks", 0)
Debug "Variant Typ: " + Str(pvResult\vt)
Debug "Workbooks, pvresult\pdispVal: " + Str(pvResult\pdispVal)
Debug "##"
pXlBooks = pvResult\pdispVal
;}
;{ add a workbook, VBA-Code: pXlBooks.Add
pXlBook.IDispatch
pvResult.xVARIANT
VariantInit_(@pvResult);
AutoWrap(#DISPATCH_PROPERTYGET, pXlBooks, "Add", 0)
pXlBook = pvResult\pdispVal
;}
;{ Get ActiveSheet object, VBA-Code: Set pXlSheet = Application.ActiveSheet
pXlSheet.IDispatch
VariantInit_(@pvResult);
AutoWrap(#DISPATCH_PROPERTYGET, pApp, "ActiveSheet", 0);
pXlSheet = pvResult\pdispVal
;}
;{ fill 15 x15 Range with Values at a single blow, VBA-Code: don´t know how to do it with VBA
;Get Range object for the Range A1:O15...
pXlRange.IDispatch
varArr(0)\vt = #VT_BSTR
varArr(0)\bstrVal = Ansi2Uni("A1:O15")
pvResult.xVARIANT
VariantInit_(@pvResult)
AutoWrap(#DISPATCH_PROPERTYGET, pXlSheet, "Range", 1)
VariantClear_(@varArr(0))
pXlRange = pvResult\pdispVal
;Create a 15x15 safearray of variants...
varArr(0)\vt = #VT_ARRAY | #VT_VARIANT
Dim sab.SAFEARRAYBOUND(1)
sab(0)\lLbound = 1
sab(0)\cElements = 15
sab(1)\lLbound = 1
sab(1)\cElements = 15
varArr(0)\parray = SafeArrayCreate_(#VT_VARIANT, 2, @sab(0))
Debug "arr\parray: " + Str(varArr(0)\parray)
;Fill safearray with some values...
Dim indices.l(1)
tmp.xVARIANT
For i = 1 To 15
For j = 1 To 15
;Create entry value for (i,j)
tmp\vt = #VT_I4
tmp\lVal = i*j
;Add to safearray...
indices(0) = i
indices(1) = j
hr = SafeArrayPutElement_(varArr(0)\parray, @indices(0), @tmp)
;Debug "hr from SafeArrayPutElement: " + Str(hr)
; -2147024809 = #E_INVALIDARG ;An argument is invalid.
; -2147352565 = #DISP_E_BADINDEX ;The specified index was invalid.
; -2147024882 = #E_OUTOFMEMORY ;Memory could not be allocated for the element.
VariantClear_(@tmp)
Next
Next
;Set range with our safearray...
AutoWrap(#DISPATCH_PROPERTYPUT, pXlRange, "Value", 1)
;}
;{ put text to single cell in row 4, column 15, VBA-Code: Set pXlCells4_15 = Cells(4, 15): pXlCells4_15.Value = "COM-Test with Excel"
;arguments for column and row, cells(row,column)
;take care! arguments are handeled reverse
Dim varArr.xVARIANT(2)
varArr(0)\vt = #VT_I4
varArr(0)\lVal = 15
varArr(1)\vt = #VT_I4
varArr(1)\lVal = 4
;get cell-object
pXlCells4_15.IDispatch
pvResult.xVARIANT
VariantInit_(@pvResult)
If AutoWrap(#DISPATCH_PROPERTYGET, pXlSheet, "Cells", 2) = 0
pXlCells4_15 = pvResult\pdispVal
EndIf
;set value
Dim varArr.xVARIANT(1)
varArr(0)\vt = #VT_BSTR
varArr(0)\bstrVal = Ansi2Uni("COM-Test with Excel")
AutoWrap(#DISPATCH_PROPERTYPUT, pXlCells4_15, "Value", 1)
;}
;{ fit the width of all cells to their content, VBA-Code: Cells.EntireColumn.AutoFit
;get cells object
pXlCells.IDispatch
VariantInit_(@pvResult)
AutoWrap(#DISPATCH_PROPERTYGET, pXlSheet, "Cells", 0)
pXlCells = pvResult\pdispVal
;get EntireColumn-object
pXlEntireColumn.IDispatch
VariantInit_(@pvResult)
AutoWrap(#DISPATCH_PROPERTYGET, pXlCells, "EntireColumn", 0)
pXlEntireColumn = pvResult\pdispVal
;fit them, Cells.EntireColumn.AutoFit
AutoWrap(#DISPATCH_METHOD, pXlEntireColumn, "AutoFit", 0)
If pXlCells
pXlCells\Release()
EndIf
;}
;{ set background color to single cell, row 4, column 15, VBA-Code: pXlCells4_15.Interior.ColorIndex = 36
pXlInterior.IDispatch
VariantInit_(@pvResult)
AutoWrap(#DISPATCH_PROPERTYGET, pXlCells4_15, "Interior", 0)
pXlInterior = pvResult\pdispVal
Dim varArr.xVARIANT(1)
varArr(0)\vt = #VT_I4
varArr(0)\lVal = 36
AutoWrap(#DISPATCH_PROPERTYPUT, pXlInterior, "ColorIndex", 1)
;}
; pXlCells4_15.Select
AutoWrap(#DISPATCH_METHOD, pXlCells4_15, "Select", 0)
If pXlCells4_15
pXlCells\Release()
EndIf
varArr(0)\vt = #VT_I4
varArr(0)\lVal = 1
AutoWrap(#DISPATCH_PROPERTYPUT, pXlBook, "Saved", 1)
; AutoWrap(#DISPATCH_METHOD, pApp, "Quit", 0, 0)
; MessageRequester("Info", "Excel switched off")
;{ cleaning up
pXlInterior\Release()
pXlEntireColumn\Release()
pXlRange\Release()
pXlSheet\Release()
pXlBook\Release()
pXlBooks\Release()
pApp\Release()
CoUninitialize_()
;}
End
aber dafür deutlich schneller.
Es wäre zu überlegen die Objekte in einer LinkedList mit String-Namen
und Zeiger zu sammeln. Wenn man eins weitere Male benötigt,
kann das dann aus der Liste gesucht werden.
Wie man PB-Typen (long, Word, String etc.) automatisiert und komfortabel
in Variants umwandelt, oder umgekehrt, dazu habe ich noch keine Idee.
Müsste sich aber auch realisieren lassen.
_____
schic