Seite 3 von 4

Verfasst: 30.04.2006 21:50
von mk-soft
@schic,

etwas nicht safearray und bstr gebastelt.

Code: Alles auswählen

; UNICODE

Structure pData
  StructureUnion  
    bVal.b[0]; AS BYTE            ' VT_UI1
    iVal.w[0]; AS INTEGER         ' VT_I2
    lVal.l[0]; AS LONG            ' VT_I4
    fltVal.f[0]; AS SINGLE        ' VT_R4
    dblVal.d[0]; AS DOUBLE        ' VT_R8
    boolVal.w[0]; AS INTEGER      ' VT_BOOL
    scode.l[0]; AS LONG           ' VT_ERROR
    cyVal.l[0]; AS LONG           ' VT_CY
    date.d[0]; AS DOUBLE          ' VT_DATE
    bstrVal.l[0]; AS LONG         ' VT_BSTR
    punkVal.l[0]; AS DWORD        ' VT_UNKNOWN
    pdispVal.l[0]; AS DWORD       ' VT_DISPATCH
    parray.l[0]; AS DWORD         ' VT_ARRAY|*
  EndStructureUnion
EndStructure


Structure SAFEARRAYBOUND
  cElements.l ;ULONG
  lLbound.l ;LONG
EndStructure


Procedure CreateSafeArray(VT_TYPE.w , Lbound.l, Elements.l)

  rgsabound.SAFEARRAYBOUND
  
  rgsabound\lLbound = Lbound
  rgsabound\cElements = Elements
  *psa = SafeArrayCreate_(VT_TYPE, 1, rgsabound)
  
  ProcedureReturn *psa
  
EndProcedure

;-Test

; Anlegen

Dim Tabelle.s(100)

*psa = CreateSafeArray(#VT_BSTR, 0, 100)

; Füllen
If *psa
  If SafeArrayAccessData_(*psa, @*value.pData) = #S_OK
    Debug "Füllen..."
    For i = 0 To 99
      Text.s = "Value " + Str(i)
      *value\bstrVal[i] = SysAllocStringLen_(text, Len(text))
    Next i
  EndIf
EndIf

; Zum Testen Kopieren

SafeArrayCopy_(*psa, @*psa2)

; Element 10 ändern
text.s = "Hallo Welt"
*value = SysAllocStringLen_(text, Len(text))
Element = 10
SafeArrayPutElement_(*psa, @Element, *Value)


Debug "Ausgabe 2"
If SafeArrayAccessData_(*psa2, @*tabelle.pData) = #S_OK
  For i = 0 To 99
    Debug PeekS(*tabelle\bstrVal[i])
  Next i
EndIf

Debug "Ausgabe 1"
For i = 0 To 99
  SafeArrayGetElement_(*psa, @i, @*result)
  Debug PeekS(*Result)
Next i

SafeArrayUnaccessData_(*psa)
SafeArrayUnaccessData_(*psa2)
FF :wink:

Verfasst: 30.04.2006 22:17
von schic
Danke mk-soft, bist ein Genie :D

Code: Alles auswählen

    If j = 7
      tmp\vt = #VT_BSTR
      txt.s = Str(i*j) + " blabla"
      tmp\bstrVal = SysAllocStringLen_(txt, Len(txt))
    EndIf
und es klappt

Gruß schic

Verfasst: 01.05.2006 12:23
von Kiffi
@mk-soft & schic,

erstmal vielen Dank für Eure Mühen! Die Excel-Demo funktioniert soweit
perfekt. :allright:

Doch mein geliebtes XML-Objekt will nicht so, wie ich wohl will.

Code: Alles auswählen

*myObject.IDispatch = CreateObject("Microsoft.XMLDOM")
If *myObject = 0
  Debug LastMessage
Else
  Debug "OK"
  *myObject\Release()
EndIf
Hier bekomme ich in LastMessage 'CoGetClassObject: ErrorCode
$80040154' zurückgeliefert.

Dass es generell funktionieren sollte, zeigt folgender VBS-Code, der auf
meinem Rechner anstandslos ausgeführt wird:

Code: Alles auswählen

Set myXML = CreateObject("Microsoft.XMLDOM")
strXML="<hallo>123</hallo>"
myXML.LoadXml strXML
MsgBox myXML.XML
Was tun?

Danke & Grüße ... Kiffi

Verfasst: 01.05.2006 14:07
von schic
@Kiffi
probier´s mal mit #CLSCTX_ALL anstatt #CLSCTX_LOCAL_SERVER bei

Code: Alles auswählen

CoGetClassObject_(@CLSID, #CLSCTX_ALL, #Null, @IID_IClassFactory, @pCf)
Bei mir gibt´s damit keine Fehlermeldung mehr.

Gruß schic

Verfasst: 01.05.2006 14:54
von schic
@Kiffi
Dein VBS-Code umgesetzt:

Code: Alles auswählen

; VBS: Set myXML = CreateObject("Microsoft.XMLDOM")
; PB:
myXML.IDispatch = CreateObject("Microsoft.XMLDOM")

; VBS: strXML="<hallo>123</hallo>"
;PB:
Dim varArr.Variant(1)
varArr(0)\vt = #VT_BSTR
varArr(0)\bstrVal = Ansi2Uni("<hallo>123</hallo>")
; VBS: myXML.LoadXml strXML
; PB
AutoWrap(#DISPATCH_METHOD, myXML, "LoadXml", 1)

; VBS: pvResult = myXML.XML
; PB:
VariantInit_(pvResult)
AutoWrap(#DISPATCH_PROPERTYGET,  myXML, "XML", 0)
; ...
Debug pvResult\vt
Debug "XML = " + PeekS(uni2ansi(pvResult\bstrVal))
Gruß schic

PS.: #CLSCTX_LOCAL_SERVER | #CLSCTX_INPROC_SERVER scheint am universellsten zu funktionieren.

Verfasst: 01.05.2006 17:06
von Kiffi
Hallo schic,

> Dein VBS-Code umgesetzt:

joh, könnte was werden... ;-)

Örzmal vielen Dank für die Übersetzung! :-D

OK, nach einigen Anpassungen funktioniert nun das CreateObject()
anstandslos. Bekomme ich jedoch das XML noch nicht korrekt ausgegeben.

Code: Alles auswählen

Debug pvResult\vt ; Hier steht 'ne 8 drin
Debug "XML = " + uni2ansi(pvResult\bstrVal) ; ist leider leer
Hat das was mit dem weiter oben diskutierten Unicode und bstrVal zu tun?

Ratlos ... Kiffi

Verfasst: 01.05.2006 18:28
von mk-soft
Morgen,
habe alles in ComHelper.pb zusammengepackt. Mit Funktion ReleaseObject(*Object) erweitert. Somit reicht es mit Pointern zuarbeiten ohne den Type anzugeben.

Code: Alles auswählen

*pXlApp = CreateObject("Excel.Application", #CLSCTX_LOCAL_SERVER)
If *pXlApp = 0
  Debug LastMessage
  End
EndIf
...
ReleaseObject(*pXlApp)
Der Parameter #CLSCTX ist Optional. Default ist #CLSCTX_ALL

ComHelper.pb

Code: Alles auswählen

;-TOP
; Kommentar     : DCOM Object Helper
; Author        : mk-soft
; Second Author : 
; Datei         : ComHelper.pb
; Version       : 1.01
; Erstellt      : 01.05.2006
; Geändert      : 01.05.2006

; -------------------------------------------------------------------

;- Konstanten
#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

; -------------------------------------------------------------------

;- Strukturen

; -------------------------------------------------------------------

; Globale Variablen
Global LastError.l
Global LastMessage.s

; -------------------------------------------------------------------

Procedure CreateObject(Object.s, CLSTYPE.l = #CLSCTX_ALL)

  LastError = 0
  LastMessage = ""
  
  hr = CoInitialize_(0)
  If  hr <> #S_OK And hr <> #S_FALSE
    LastError = hr
    LastMessage = "Error CoInitialize: ErrorCode " + Hex(hr)
    End
  EndIf
  
  hr = CLSIDFromProgID_(Object, CLSID.GUID)
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CLSIDFromProgID: ErrorCode " + Hex(hr)
    ProcedureReturn 0
  EndIf
  
  hr = CoGetClassObject_(@CLSID, CLSTYPE, #Null, ?IID_IClassFactory, @pCf.IClassFactory)
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CoGetClassObject: ErrorCode " + Hex(hr)
    ProcedureReturn 0
  EndIf
  

  hr = pCf\CreateInstance(#Null, ?IID_IDispatch, @*Object.IDispatch)
  pCf\Release()
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CreateInstance: ErrorCode " + Hex(hr)
    ProcedureReturn 0
  Else
    ProcedureReturn *Object
  EndIf
  
EndProcedure

; -------------------------------------------------------------------

Procedure ReleaseObject(*Object.IDispatch)

  If *Object
    *Object\Release()
  EndIf
  
EndProcedure

; ---------------------------------------------------------

;- DataSection IID

DataSection
  
  IID_NULL: ; {00000000-0000-0000-0000-000000000000}
  Data.l $00000000
  Data.w $0000, $0000
  Data.b $00, $00, $00, $00, $00, $00, $00, $00 
  
  IID_IUnknown : ; {00000000-0000-0000-C000-000000000046}'
  Data.l $00000000
  Data.w $0000, $0000, $C000
  Data.b $00 , $00 , $00, $00 , $00 , $46
  
  IID_IDispatch:
  Data.l $00020400
  Data.w $0000, $0000
  Data.b $C0,$00,$00,$00,$00,$00,$00,$46

  IID_IClassFactory:
  Data.l $00000001
  Data.w $0, $0
  Data.b $C0, $0, $0, $0, $0, $0, $0, $46
  
EndDataSection
FF :wink:

Verfasst: 01.05.2006 18:50
von schic
@Kiffi
das hat mit dem Unicode-Zeugs zu tun.
Ich habe die Compileroption Unicode-exe wieder abgeschaltet.
Das war mir zu diffus. Kann´s nicht wirklich nachvollziehen
wann ein String ein Unicode und wann ein ANSI ist. Und wenn
ich´s nicht schnalle lasse ich das erst mal.

schaut dann mit mk-soft´s ReleaseObject (Dank für die Hilfe) so aus:

Code: Alles auswählen


;- 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 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

Structure ObjType
  lpObj.l
  strObjNam.s
EndStructure

;- Globals
;Global pApp.IDispatch
Global pvResult.Variant
Global IID_NULL.GUID
Global LastError.l
Global LastMessage.s 
;}

;- Arrays Linked Lists
Global Dim varArr.Variant(0)
Global NewList ObjectList.ObjType()
;}

Procedure.l Ansi2Uni(ansistr.s) ; Converts normal (Ansi) string to Unicode
  lenA.l = Len(ansistr)
  lenW = MultiByteToWideChar_(#CP_ACP, 0, ansistr, lenA, 0, 0)
  If (lenW > 0) ; Check whether conversion was successful
    unicodestr = SysAllocStringLen_(0, lenW)
    MultiByteToWideChar_(#CP_ACP, 0, ansistr, lenA, unicodestr, lenW)
    result = unicodestr
    ProcedureReturn result
  Else
    ProcedureReturn 0
  EndIf
EndProcedure 

Procedure.s Uni2Ansi(unicodestr.l) ; Converts Unicode to normal (Ansi) string
  lenA = WideCharToMultiByte_(#CP_ACP, 0, unicodestr, -1, 0, 0, 0, 0);
  ansistr.s = Space(lenA)
  If (lenA > 0)
    WideCharToMultiByte_(#CP_ACP, 0, unicodestr, -1, @ansistr, lenA, 0, 0);
  EndIf
  ProcedureReturn ansistr
EndProcedure 


Procedure.l 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 " + name
  ;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

Procedure.l CreateObject(app.s)

  LastError = 0
  LastMessage = ""
 
  hr = CoInitialize_(0)
  If  hr <> #S_OK And hr <> #S_FALSE
    LastError = hr
    LastMessage = "Error CoInitialize: ErrorCode " + Hex(hr)
    End
  EndIf
 
  CLSID.GUID
  hr = CLSIDFromProgID_(Ansi2Uni(app), @CLSID)
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CLSIDFromProgID: ErrorCode " + Hex(hr)
    ProcedureReturn 0
  EndIf
 
  pCf.IClassFactory
  
  IID_IClassFactory.GUID
  IID_IClassFactory\data1 = $1
  IID_IClassFactory\data4[0] = $C0
  IID_IClassFactory\data4[7] = $46
  
  hr = CoGetClassObject_(@CLSID, #CLSCTX_LOCAL_SERVER | #CLSCTX_INPROC_SERVER, #Null, @IID_IClassFactory, @pCf)
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CoGetClassObject: ErrorCode " + Hex(hr)
    ; REGDB_E_KEYMISSING 	        0x80040152 	Could not find the key in the registry
    ; REGDB_E_INVALIDVALUE 	      0x80040153 	Invalid value For registry
    ; REGDB_E_CLASSNOTREG 	      0x80040154 	Class not registered
    ; REGDB_E_IIDNOTREG 	        0x80040155 	Interface not registered
    ; REGDB_E_BADTHREADINGMODEL 	0x80040156 	Threading model Entry is not valid
    ; CO_E_NOTINITIALIZED	        0x800401F0
    ProcedureReturn 0
  EndIf
 
  riid.GUID
  riid\data1 = $20400
  riid\data4[0] = $C0
  riid\data4[7] = $46
  
  hr = pCf\CreateInstance(#Null, @riid, @*pApp.IDispatch)
  pCf\Release()
  If hr <> #S_OK
    LastError = hr
    LastMessage = "Error CreateInstance: ErrorCode " + Hex(hr)
    ; S_OK 	                      00000000 	0
    ; S_FALSE 	                  00000001 	1
    ; E_PENDING 	                8000000A 	2147483658
    ; E_NOTIMPL 	                80004001 	2147500033
    ; E_NOINTERFACE 	            80004002 	2147500034 The object that ppvObject points to does not support the interface identified by riid.
    ; E_POINTER 	                80004003 	2147500035
    ; E_ABORT 	                  80004004 	2147500036
    ; E_FAIL 	                    80004005 	2147500037
    ; E_UNEXPECTED 	              8000FFFF 	2147549183
    ; CLASS_E_NOAGGREGATION 	    80040110 	2147746064 The pUnkOuter parameter was non-NULL and the object does not support aggregation
    ; CLASS_E_CLASSNOTAVAILABLE 	80040111 	2147746065
    ; CLASS_E_NOTLICENSED 	      80040112 	2147746066
    ; E_ACCESSDENIED 	            80070005 	2147942405
    ; E_HANDLE 	                  80070006 	2147942406
    ; E_OUTOFMEMORY 	            8007000E 	2147942414
    ; E_INVALIDARG 	              80070057 	2147942487
    ProcedureReturn 0
  Else
    AddElement(ObjectList())
    ObjectList()\lpObj = *pApp
    ObjectList()\strObjNam = app
    ProcedureReturn *pApp
  EndIf

EndProcedure

Procedure ReleaseObject(*Object.IDispatch)

  If *Object
    *Object\Release()
  EndIf
 
EndProcedure 

; VBS: Set myXML = CreateObject("Microsoft.XMLDOM")
; PB:
myXML.IDispatch = CreateObject("Microsoft.XMLDOM")

; VBS: strXML="<hallo>123</hallo>"
;PB:
Dim varArr.Variant(1)
varArr(0)\vt = #VT_BSTR
varArr(0)\bstrVal = Ansi2Uni("<hallo>123</hallo>")
; VBS: myXML.LoadXml strXML
; PB
AutoWrap(#DISPATCH_METHOD, myXML, "LoadXml", 1)

; VBS: pvResult = myXML.XML
; PB:
VariantInit_(pvResult)
AutoWrap(#DISPATCH_PROPERTYGET,  myXML, "XML", 0)
; ...
Debug pvResult\vt
Debug "XML = " + uni2ansi(pvResult\bstrVal)

ReleaseObject(myXML)
Mit den CLSCTX-Dingens funktioniert #CLSCTX_LOCAL_SERVER | #CLSCTX_INPROC_SERVER bei allen Objekten die ich bisher
getestet habe. Andere Kombinationen dagegen nicht immer.

Bei mir liefert das:

getting IDs of LoadXml
hr GetIDsOfNames: 0
dispID from LoadXml = 63
hr Invoke: 0
##########
getting IDs of XML
hr GetIDsOfNames: 0
dispID from XML = 27
hr Invoke: 0
##########
8
XML = <hallo>123</hallo>

Gruß schic

Verfasst: 01.05.2006 19:06
von schic
hmm
ich bin die ganze Zeit am grübeln ob es wohl eine Möglichkeit gibt,
in PB (oder FASM) festzustellen welcher Typ eine Variable ist.

Also

Code: Alles auswählen

  If VarTyp(*Var) = Long
    tmp\vt = #VT_I4
    tmp\lVal = PeekL(*Var)
  ElseIf VarTyp(*Var) = String
    tmp\vt = #VT_BSTR
    tmp\bstrVal = Ansi2Uni(*Var) 
  EndIf
Wie weiß eigentlich ein compiliertes Programm von seinen Typen?
Wenn dazu jemand eine Idee hat...

Gruß schic

Verfasst: 01.05.2006 19:53
von mk-soft
Hinweis:

Am besten immer UNICODE. In Purebasic sind dann alle Strings in UNICODE. Wenn dann eine API aufgerufen wird die ANSi verwendet, dann wandelt der Compiler dieses automatisch um. Alle DCOM-Objekte mit Strings benötigen so wieso immer Unicode. Warum dann selber immer Umwandel? Besser gleich alles in Unicode.

FF :wink: