Seite 1 von 1

String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 05.01.2011 15:33
von Olaf.Renns
Hallo,

erst einmal frohes neues Jahr an euch alle!

Mein Problem: Ich habe einen Code erstellt, in dem ich über COM OLE auf ein externes System zugreife und von dort Daten/Ergebnisse erhalte. Das klappt alles wunderbar. Diesen Code habe ich nun als DLL kompiliert und möchte diesen jetzt gern in Excel über VBA ansprechen. Bei Proceduren, die einen Long-Wert zurückgeben, läuft alles wie am Schnürchen. Proceduren, die einen String zurückgeben, habe ich entsprechend so umgebaut, dass sie einen Zeiger zurückgeben (Das habe ich über SysAllocString_ gelöst), da Excel ansonsten Problem hat den String direkt zu verarbeiten. Auch hierbei funktioniert die Rückgabe soweit problemlos, doch leider gibt es hin und wieder Proceduren, die den String mit irgendwelchen Sonderzeichen behängen:

Code: Alles auswählen

Test 7ì%
Test 7³
Name O'ÈŸù·|
Ich denke, dass es irgendwas mit unicode zu tun hat, da die DLL ohne unicode erstellt wurde und Excel VBA scheinbar unicode verwendet. Gedacht, getahn, aber leider funktioniert nach dem Umstellen der DLL auf unicde garnichts mehr. Eventuell müsste der Befehl SysAllocString_, der unicode verwendet, auf Ascii umgestellt werden. Ich habe irgendwie in Erinnerung, dass es da mal einen passenden Befehl gab. Auch habe ich mir den Befehl WideCharToMultiByte angeschaut, aber auch hier kommt es nur zu unleserlichen Stringrückgaben.

Wenn ich die DLL in PB anspreche, läuft alles ohne Probleme.

Vielleicht kann jemand helfen?

Herzliche Grüße
Olaf

Re: String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 05.01.2011 16:14
von ts-soft
Ich hab kein VBA oder Excel, weiß also nicht ob es wirklich hilft.

Eine BStr in Unicode kannst Du mit folgendem Beispiel erzeugen.
Das ist dann unabhängig von der Compilereinstellung der DLL.

Code: Alles auswählen

EnableExplicit

ProcedureDLL Return_Bstr(text.s)
  Protected length = StringByteLength(text, #PB_Unicode) + 2
  Protected *mem = AllocateMemory(length)
  Protected *result
  
  If *mem
    PokeS(*mem, text, -1, #PB_Unicode)
    *result = SysAllocString_(*mem)
    FreeMemory(*mem)
    
    ProcedureReturn *result
  EndIf
EndProcedure

Define *mem = Return_Bstr("Hallo")

Debug PeekI(*mem - SizeOf(Integer)) ; 10 bytes in Unicode
Gruß
Thomas

Re: String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 05.01.2011 22:51
von Olaf.Renns
Erstmal herzlichen Dank für deinen Kommentar. Wenn ich das ganze soweit übernehme, erhalten ich zwar stets den vollständigen String, aber dieser ist voll mit null-terminierten Zeichen, was dann in VBA so aussieht:

Code: Alles auswählen

D A S  I S T  E I N  T E S T
VBA intepretiert noch den vollständigen String, in PB jedoch ergibt es nur eine Ausgabe von
Das liegt wohl daran, dass PB das erste null-terminierte Zeichen nach D als Ende des Strings interpretiert und somit abschneidet.

Ich weiss nicht, ob das Excel String Format "OLE Bstr byte string" im Bezug auf den DLL Access einen Unterschied zu unicode strings darstellt und vielleicht da der Fehler liegt.

Re: String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 05.01.2011 23:04
von Olaf.Renns
Den Zugriff auf das externe System habe ich jetzt mal testweise auf COMate von srod umgestellt. Laut Hilfe verwenden die Befehle je nach Compiler Setting Ascii oder Unicode. Da ich meine DLL in Ascii erstellt habe, werden die Rückgaben wohl auch als Ascii interpretiert. Das nur als Information. Vielleicht hilft es, das Problem einzugrenzen.

Re: String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 05.01.2011 23:09
von ts-soft
Die Windows API erzeugt nur einen Unicode BStr. für 32-Bit Anwendungen.
Einen Ansi BStr. kannste aber mit folgendem Code nachbilden:

Code: Alles auswählen

EnableExplicit

ProcedureDLL ReturnAnsiBstr(text.s)
  Protected length = StringByteLength(text, #PB_Ascii) + 1 + SizeOf(Integer)
  Protected *mem = AllocateMemory(length)
  
  If *mem
    PokeI(*mem, Len(text))
    PokeS(*mem + SizeOf(Integer), text, -1, #PB_Ascii)
    
    ProcedureReturn *mem + SizeOf(Integer)
  EndIf
EndProcedure

Define.s Text = "Hallo"

Define *mem = ReturnAnsiBstr(Text)

Debug PeekS(*mem, -1, #PB_Ascii)    ; sollte "Hallo" ergeben
Debug PeekI(*mem - SizeOf(Integer)) ; sollte 5 ergeben
Gruß
Thomas

Re: String-Rückgabe aus PB DLL in VBA über SysAllocString

Verfasst: 06.01.2011 12:08
von Olaf.Renns
Hallo Thomas,

deine letzte Variante scheint zu funktionieren. Zwar bietet Excel VBA mit dieser Variante keine automatische Konvertierung mehr an (Mit der anderen Variante hat Excel den als Long zurückgegebenen Bstr direkt erkannt und als String dargestellt) aber mit einer kleinen einfachen Procedur (die auch schon in PB notwendig war, um die Speicheradresse auszulesen) lässt sich das alles nun wunderbar darstellen:

Für diejenigen, die es vielleicht brauchen:

Hier die Procedur für Excel VBA, um den String aus dem Speicher zu lesen ...

Code: Alles auswählen

Private Declare Function lstrcopy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, ByVal lpString2 As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long

Public Function ReadMemoryAddress(ByVal lpStrPointer As Long) As String
  Dim nLen As Long
  Dim sBuffer As String
  nLen = lstrlen(lpStrPointer) * 4
  sBuffer = Space$(nLen)
  lstrcopy sBuffer, lpStrPointer
  If InStr(sBuffer, vbNullChar) > 0 Then
    sBuffer = Left$(sBuffer, InStr(sBuffer, vbNullChar) - 1)
  End If
  ReadMemoryAddress= sBuffer
End Function
(Hierbei ist zu beachten, dass die Funktionen alle mit dem Typ Long deklariert wurden!)

.... und hier die Variante für PB:

Code: Alles auswählen

Procedure.s ReadMemoryAddress(Puffer)
  If Puffer
    ProcedureReturn PeekS(Puffer,-1,#PB_Ascii)
  Else
    ProcedureReturn ""
  EndIf
EndProcedure
Ich danke Dir sehr herzlich für deine Hilfe!