Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi AnsiStringArray aus DLL an Excel-VBA (https://www.delphipraxis.net/200498-ansistringarray-aus-dll-excel-vba.html)

Andreas13 25. Apr 2019 22:21

Delphi-Version: XE5

AnsiStringArray aus DLL an Excel-VBA
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Community,

ich möchte Euch erneut um Hilfe bitten! Bei folgender Aufgabenstellung komme ich leider nicht weiter:

Aus einer Delphi-DLL möchte ich einen StringVektor an Excels VBA übergeben. Dazu habe ich eine funktionierende Lösung entwickelt, die folgendermaßen aussieht:

Delphi XE5 Professional
Zielplattform: 32-Bit-DLL
Excel: 2016


Delphi-Quellcode:
library Delphi_String_for_Excel_Test;
uses
  System.ShareMem,
  System.SysUtils,
  System.Classes,
  System.AnsiStrings
;

{$R *.res}

Type
  ExcelLongInt = Int32;   // {4 Bytes}


CONST
  AnsiString_Vektor_GLOBAL: Array [0..3] of AnsiString = ('1: Erster String',
                                                          '2: Das ist der ZWEITE String!',
                                                          '3: ...und hier ist der DRITTE!',
                                                          '4: Schlußlicht-String...');


                                                         
Procedure AnsiString_To_Excel(S: PAnsiChar; i: ExcelLongInt); StdCall;
// Aufgabe -> Einen Delphi-AnsiString-Vektor ZEILENWEISE an Excel-VBA übertragen

//  S: PAnsiChar   : zu übertragender Strings
//  i: ExcelLongInt : der i-te String im globalen Vektor

VAR
  P: AnsiString;

Begin
  P:= AnsiString_Vektor_GLOBAL[i];                // Rückgabewert
  System.AnsiStrings.StrPLCopy(S, P, Length(P));  // Excels String-Array wird überschrieben
End;{AnsiString_To_Excel}
{-----------------------}

Exports
  AnsiString_To_Excel

;

begin
end.
Auf Excels Seite sieht der Code wie folgt aus:

VBA-Code:
Delphi-Quellcode:
Option Explicit

Private Declare Sub AnsiString_To_Excel Lib "Delphi_String_for_Excel_Test.dll" _
(ByVal A As String, ByVal i As Long)
' Es funktioniert!

Public Function GetDelphiStrings(Anzahl As Long)
' Zeilenweiser Aufruf der DLL-Routine AnsiString_To_Excel
' Es funktioniert!

Dim Vektor() As String
Dim NULStr As String
Dim i As Long

  i = 1 'Wichtig: sonst kommen u.U. sinnlos hohe Werte beim Start und der Vektor in der DLL steigt aus: RangeCheckError!
  ReDim Vektor(1 To Anzahl, 1 To 1) ' SpaltenVektor anpassen
  NULStr = String(100, vbNullChar) 'Excel MUSS den Speicherbereich zur Verfügung stellen: in der DLL wird dieser nur gepatcht!
 
  For i = 1 To Anzahl
    Vektor(i, 1) = NULStr
    AnsiString_To_Excel Vektor(i, 1), i - 1 ' !!!  -1: Delphi: NULL-basiert!
  Next
 
  GetDelphiStrings = Vektor
End Function
Der Schönheitsfehler dieser Lösung besteht darin, daß ich die Strings des StringVektors ZEILENWEISE einlese, d.h. von VBA aus wird die DLL-Routine AnsiString_To_Excel n-mal aufgerufen.

Meine Idee für eine Verbesserung sieht folgendermaßen aus:

VBA-Code:
Delphi-Quellcode:
Private Declare Sub AnsiStringVektor_To_Excel Lib "Delphi_String_for_Excel_Test.dll" _
(ByVal A As String, ByVal n_Anzahl As Long)
' Es funktioniert NICHT!

Public Function GetDelphiStringVektor(Anzahl As Long)
' EINMALIGER Aufruf der DLL-Routine AnsiStringVektor_To_Excel
' Es funktioniert!

Dim Vektor() As String
Dim NULStr As String
Dim i As Long

  i = 1 'Wichtig: sonst kommen u.U. sinnlos hohe Werte beim Start und der Vektor in der DLL steigt aus: RangeCheckError!
  ReDim Vektor(1 To Anzahl, 1 To 1) ' SpaltenVektor anpassen
  NULStr = String(100, vbNullChar) 'Excel MUSS den Speicherbereich zur Verfügung stellen: in der DLL wird dieser nur gepatcht!
 
  For i = 1 To Anzahl
    Vektor(i, 1) = NULStr
  Next
 
  AnsiString_To_Excel Vektor(1, 1), Anzahl
 
  GetDelphiStringVektor = Vektor
End Function
Und die DLL habe ich um Folgendes erweitert:

Delphi-Quellcode:
 Procedure AnsiStringVektor_To_Excel(S: PAnsiChar; n_Anzahl: ExcelLongInt); StdCall;
// Aufgabe -> Einen Delphi-AnsiString-Vektor auf EINMAL an Excel-VBA übertragen

// S: PAnsiChar          : Erstes Element des zu zu übertragenden StringVektors
// n_Anzahl: ExcelLongInt : Gesamtzahl der Strings im StringVektor


VAR
  Vektor: Array of PAnsiChar ABSOLUTE S;
  P    : AnsiString;
  i    : Integer;
 
Begin
  For i:= 1 To n_Anzahl Do             // Excel: 1-basiertes Array
  Begin
    P:= AnsiString_Vektor_Global[i-1]; // -1: Delphi: 0-basiertes Array    

    System.AnsiStrings.StrPLCopy(Vektor[i], P, Length(P));             // --> Access violation
    // System.AnsiStrings.StrCopy(PAnsiChar(Vektor[i]), PAnsiChar(P)); // --> Access violation
  End;
End;{AnsiStringVektor_To_Excel}
{-----------------------------}
...

Exports
  ...
, AnsiStringVektor_To_Excel
Leider erhalte ich aus der Procedure AnsiStringVektor_To_Excel stets "Access violation".

Im gezippten Anhang befinden sich folgende dre einsatzferige Dateien:

- Delphi_String_for_Excel_Test.dpr
- Delphi_String_for_Excel_Test.dll
- Delphi_String_for_Excel_Test.xlsm (s. Benutzungshinweise)

Was mache ich falsch? Geht es überhaupt durch nur EINEN EINZIGEN Aufruf oder nur ZEILENWEISE?

Vielen Dank für Eure Hilfe im Voraus!

Gruß

Andreas

hoika 26. Apr 2019 05:54

AW: AnsiStringArray aus DLL an Excel-VBA
 
Hallo,
übergib doch die Strings auf einmal mit einem definierten Trenner (#9 z.B.)
und drösel dir das in Excel wieder auseinander.

Das ABSOLUTE zerschießt dir hier bestimmt den Speicher.
Du musst für S speicher reservieren.

EWeiss 26. Apr 2019 05:56

AW: AnsiStringArray aus DLL an Excel-VBA
 
Na ja zu testen ist da nix..
Wäre vielleicht sinnvoll wenn du auch den VBA Quelltext hochladen würdest.
Danach könnte ich mir das mal anschauen.

Habe keine Lust das alles selbst von Hand zu machen.

Zitat:

übergib doch die Strings auf einmal mit einem definierten Trenner (#9 z.B.)
Er sollte überhaupt keinen String verwenden sondern PAnsiChar oder vergleichbares.. meine Meinung.
Und ja ein Trenner wäre hier am sinnvollsten mache ich auch nicht anders.

Helfen könnte dann so etwas..

Code:
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _
    ByVal lpString As Long _
) As Long

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal length As Long _
)
Code:
Public Function VBStrFromAnsiPtr(ByVal lpStr As Long) As String

Dim bStr() As Byte
Dim cChars As Long

    On Error Resume Next
        ' Get the number of characters in the buffer
        cChars = lstrlen(lpStr)
        If cChars Then
            ' Resize the byte array
            ReDim bStr(0 To cChars - 1) As Byte
            ' Grab the ANSI buffer
            Call CopyMemory(bStr(0), ByVal lpStr, cChars)
        End If
        ' Now convert to a VB Unicode string
        VBStrFromAnsiPtr = StrConv(bStr, vbUnicode)

End Function
Bsp.

Code:
Dim mylist     As String
Dim LngPointer As Long
Dim IntI       As Integer
Dim uB         As Integer
Dim alist()    As String

LngPointer = MeineVbaFunction(MeinDelphiPAnsiChar As Any include Delemiter und Rückgabe As Long) und auf der Delphi Seite als PAnsiChar
       
mylist = VBStrFromAnsiPtr(LngPointer)
alist = Split(mylist, ",")

If mylist > "" Then
  uB = UBound(alist)
  For IntI = 0 To uB
    list.AddItem alist(IntI)
  Next
End If
gruss

EWeiss 26. Apr 2019 07:32

AW: AnsiStringArray aus DLL an Excel-VBA
 
Habe dir das mal schnell zusammen geklopft..
Alle Strings (PAnsiChars) werden aus der DLL von der const AnsiString_Vektor_GLOBAL an VBA(VB) geschickt.
ShareMem ist wohl hier nicht nötig..

Das ist alles..

Delphi-Quellcode:
library Delphi_String_for_Excel_Test;

uses ShareMem, SysUtils, Classes;

{$R *.res}

const
   AnsiString_Vektor_GLOBAL: Array [0..3] of PAnsiChar =
     ('1: Erster String',
      '2: Das ist der ZWEITE String!',
      '3: ...und hier ist der DRITTE!',
      '4: Schlußlicht-String...');

function PAnsiChar_To_Excel(Delimiter: AnsiChar = ';'): PAnsiChar; stdcall;
var
  IntI: Integer;
  erg: AnsiString;
begin

  for IntI := 0 to 3 do
  begin
    erg := erg + AnsiString(AnsiString_Vektor_GLOBAL[IntI]) + Delimiter;
    erg := copy(erg, 1, length(erg));
  end;
  result := PAnsiChar(erg);
end;

exports
   PAnsiChar_To_Excel;

begin

end.
VBA..
Module..
Code:
Public Declare Function PAnsiChar_To_Excel Lib "Delphi_String_for_Excel_Test" ( _
    Optional ByVal Delimiter As Byte = 59 _
) As Long
Form..
Code:
Option Explicit

Private Sub cmdGet_Click()

Dim IntI      As Integer
Dim flist     As String
Dim uB        As Integer
Dim LngP      As Long
Dim alist()   As String
Dim Delimiter As Byte

    ListInput.Clear
    Delimiter = 124 ' >>> Default ohne Angabe = 59 = ";"
   
    LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|"

    flist = VBStrFromAnsiPtr(LngP)
    alist = Split(flist, "|")
   
    If flist > "" Then
        uB = UBound(alist)
        For IntI = 0 To uB
            ListInput.AddItem alist(IntI)
        Next
    End If
   
End Sub
gruss

hoika 26. Apr 2019 09:24

AW: AnsiStringArray aus DLL an Excel-VBA
 
Hallo,
muss bei Split nicht ';' rein?

peterbelow 26. Apr 2019 10:45

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Zitat von Andreas13 (Beitrag 1430988)
Hallo Community,

ich möchte Euch erneut um Hilfe bitten! Bei folgender Aufgabenstellung komme ich leider nicht weiter:

Aus einer Delphi-DLL möchte ich einen StringVektor an Excels VBA übergeben. Dazu habe ich eine funktionierende Lösung entwickelt, die folgendermaßen aussieht:

Delphi XE5 Professional
Zielplattform: 32-Bit-DLL
Excel: 2016

Sind VBA-Arrays nicht einfach OLE safearrays und VBA Strings OLE BSTR (aka Delphi Widestring)? Ist definitiv zu lange her, dass ich mich damit beschäftigt habe, aber ich glaube, dein Ansatz ist falsch. Verwende einfach VARIANT-Variablen auf beiden Seiten, das sollte einfacher sein.

EWeiss 26. Apr 2019 12:02

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Zitat von hoika (Beitrag 1431028)
Hallo,
muss bei Split nicht ';' rein?

Jo habe es getippt.. Schreibfehler aber das Sample funktioniert schon richtig.
Musst halt nur mal laden ;)

Kommt halt drauf an was ich übergebe
PAnsiChar_To_Excel ohne Delimiter dann ";" ansonsten halt das was übergeben wird.

Habe es korrigiert.

bsp:
Code:
LngP = PAnsiChar_To_Excel(124) '"|"
Zitat:

aber ich glaube, dein Ansatz ist falsch
Also meiner bestimmt nicht.

gruss

Andreas13 27. Apr 2019 18:36

AW: AnsiStringArray aus DLL an Excel-VBA
 
Hallo zusammen,

zunächst danke für die Diskussion und die wertvollen Beiträge!
Meine Frage wurde allerdings ganz schön mißverstanden: Ich dachte, die drei Demo-Dateien würden mein Ansinnen erklären. Aber ich sehe im Nachhinein, ich hätte mich klarer ausdrücken sollen.

Zitat:

Zitat von EWeiss (Beitrag 1431004)
Na ja zu testen ist da nix..
Wäre vielleicht sinnvoll wenn du auch den VBA Quelltext hochladen würdest.
Danach könnte ich mir das mal anschauen.

Es war doch alles dabei... Aber danke für Deine tolle Lösung!

Ich erstelle technische-wissenschaftliche Berechnungen in Delphi, die ich als Funktion über eine DLL in Excel einbinde. Meine „Programme“ sind daher keine eigenständigen Programme mit eigener Oberfläche, sondern laufen als „Bestandteil“ von Excel. Der Benutzer gibt seine Eingangsdaten in Excel ein, schickt die Funktion ab, und die Ergebnisse der Berechnungen erscheinen in der von ihm gewählten Platz in der Excel-Tabelle.

Das funktioniert mit Zahlenwerten seit langem sehr gut. Ab und zu müsste ich neben den berechneten Zahlenkolonnen (in Excel: „Matrix“ genannt) auch mal Texte mit ausgeben: z. B. Variablen-Bezeichnungen, oder die Ergebnisse oder Zwischenwerte mit einer hohen Genauigkeit (z.B. 100 Ziffern) als Text. Und dazu übertrage ich diese Texte von der DLL an Excel VBA und leite sie von dort in die Tabelle.

Meine oben vorgestellte Lösung funktioniert recht gut, allerdings kann ich ausschließlich AnsiStrings transferieren, obwohl Excels BSTR-Strings eigentlich WideStrings sind, wie auch Peter Below richtig festgestellt hat. Warum andere String-Typen nicht funktionieren, konnte ich bisher nicht herausfinden. Aber ich muss nicht alles verstehen…

Ich wollte lediglich die Schleife

Zitat:


For i = 1 To Anzahl
Vektor(i, 1) = NULStr
AnsiString_To_Excel Vektor(i, 1), i - 1 ' !!! -1: Delphi: NULL-basiert!
Next

in Delphi übertragen, um den Aufruf der DLL-Procedure „AnsiString_To_Excel“ nur ein einziges Mal machen zu müssen.

Aber inzwischen habe ich nach weiteren Tests eingesehen, daß mein Ansatz eine Schnapsidee war, und daß es en bloc am ganzen Vektor nicht geht, weil meine Routine nur die von Excel bereitgestellte Kopie des Strings patcht, und das Original String-Array ganz woanders im Speicher liegt, wo ich die Strings auch nicht mit Pointer-Akrobatik verändern kann.

Noch einmal danke für Eure Hilfe!

Gruß

Andreas

EWeiss 28. Apr 2019 05:15

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Es war doch alles dabei... Aber danke für Deine tolle Lösung!
Ja die Daten für Exel VBA nicht für VB ;)

Na egal läuft unter VB und sollte es auch unter VBA tun.
Zitat:

Q. What is Visual Basic for Applications?

A. Microsoft Visual Basic for Applications (VBA) is an embeddable programming environment designed to enable developers to build custom solutions using the full power of Microsoft Visual Basic.
Developers using applications that host VBA can automate and extend the application functionality, shortening the development cycle of custom business solutions.
Sollte also funktionieren.
Zitat:

obwohl Excels BSTR-Strings eigentlich WideStrings sind, wie auch Peter Below richtig festgestellt hat.
Ich mache ja letztendlich nichts anderes nur über einen kleinen Umweg..
Code:
StrConv(bStr, vbUnicode)
kommt den WideString am nächsten.
Zitat:

wo ich die Strings auch nicht mit Pointer-Akrobatik verändern kann.
Byte, Long, string sollte auch "Exel VBA" verstehen.

gruss

Andreas13 29. Apr 2019 11:37

AW: AnsiStringArray aus DLL an Excel-VBA
 
Liste der Anhänge anzeigen (Anzahl: 1)
@EWeiss

Hallo,

danke für Deine Hinweise. Ich habe versucht Deinen VBA-Code in Excel's VBA umzusetzen, bin allerdings gescheitert...
Ich bin halt kein VBA-Programmierer. Meine rudimentären VBA-Kenntnisse dienen nur dazu, die VBA-Schnittstelle zwischen Excel und Delphi zu realisieren. Mit Deinem objektorientierten VBA-Ansatz kenne ich mich leider nicht aus.
Könntest Du bitte den Code in der beiliegenden Datei ToExcel_Test_1.xlsm bei Gelegenheit evtl. so anpassen, daß es "dort" in Excel läuft?

Vielen Dank!

Gruß aus Nürnberg
Andreas

EWeiss 29. Apr 2019 15:27

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Könntest Du bitte den Code in der beiliegenden Datei ToExcel_Test_1.xlsm bei Gelegenheit evtl. so anpassen, daß es "dort" in Excel läuft?
Würde dir da gerne helfen habe aber kein VBA Exel. (Office) installiert.

gruss

EWeiss 29. Apr 2019 17:54

AW: AnsiStringArray aus DLL an Excel-VBA
 
Was ich dir aber sagen kann das mein Modul so unter Excel 64Bit nicht läuft. (Keine Ahnung was du installiert hast)
Es fehlt PtrSafe in allen Declarationen.
Dann muss die DLL von Delphi auch in 64Bit vorliegen.
Zur Kompatibilität der Daten Typen

Code:
Option Explicit

Private Declare PtrSafe Function PAnsiChar_To_Excel Lib "Delphi_String_for_Excel_Test.dll" ( _
    Optional ByVal Delimiter As Byte = 59 _
) As LongPtr

Private Declare PtrSafe Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _
    ByVal lpString As Long _
) As LongPtr

Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal length As LongPtr_
)
So sollte es aussehen.
Eventuell kommst du ja jetzt weiter.

Für die ListBox\ListenFeld könntest du dir ja ein Array erstellen ( bzw. haben wir schon alist() ) und das anstelle dessen verwenden wenn du keine UserForm verwenden möchtest.
Oder du schickst die werte von alist() direkt in deine Excel Tabelle.

https://analysistabs.com/vba-code/ex...rform/listbox/
https://www.guru99.com/creating-your...-in-excel.html

PS:
Anstelle des Button kannst du dir in Excel ein Macro erstellen was auf die cmdGet_Click() verweist und den Code dort ausführen.

gruss

EWeiss 29. Apr 2019 23:09

AW: AnsiStringArray aus DLL an Excel-VBA
 
Habe mal Excel 32Bit installiert aber keine Ahnung ob es das ist was du willst..
Ich werde da nicht so recht schlau draus.

Klick auf Button GetIt füllt die Cells in Excel
Klick auf Button CleanUp löscht die Einträge.

Ohne Form und ausgeführt in Excel!

Code:
Option Explicit

'                                              Pfad aktualisieren:
Private Declare Function PAnsiChar_To_Excel Lib "D:\Test\Delphi_String_for_Excel_Test" ( _
    Optional ByVal Delimiter As Byte = 59 _
) As Long

Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _
    ByVal lpString As Long _
) As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal length As Long _
)

Private Function VBStrFromAnsiPtr(ByVal lpStr As Long) As String

Dim bStr() As Byte
Dim cChars As Long

    On Error Resume Next
        ' Get the number of characters in the buffer
        cChars = lstrlen(lpStr)
        If cChars Then
            ' Resize the byte array
            ReDim bStr(0 To cChars - 1) As Byte
            ' Grab the ANSI buffer
            Call CopyMemory(bStr(0), ByVal lpStr, cChars)
        End If
        ' Now convert to a VB Unicode string
        VBStrFromAnsiPtr = StrConv(bStr, vbUnicode)

End Function

Private Sub CleanUp()

    Range("B1:B4").Delete XlDeleteShiftDirection.xlShiftUp

End Sub

Private Sub GetIt()

Dim flist    As String
Dim LngP     As Long
Dim alist()  As String
Dim Delimiter As Byte
Dim arTesting As Variant

    Delimiter = 124

    LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|"

    flist = VBStrFromAnsiPtr(LngP)
    alist = Split(flist, "|")

    If flist > "" Then
        arTesting = Application.WorksheetFunction.Transpose(alist)
        Range("B1:B4").Value = arTesting
    End If

End Sub
Wie du das nun schlussendlich regelst über Button oder sonst wie entscheide es selbst..
Aber der Wunsch die Strings in einem Schub nach Excel zu transponieren sollte dem entsprechen.
Delphi > VBA > Excel

Ich hoffe du kommst mit dem Rest selber klar. :)

gruss

Dennis07 29. Apr 2019 23:35

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Zitat von Andreas13 (Beitrag 1430988)
Delphi-Quellcode:
 Procedure AnsiStringVektor_To_Excel(S: PAnsiChar; n_Anzahl: ExcelLongInt); StdCall;
// Aufgabe -> Einen Delphi-AnsiString-Vektor auf EINMAL an Excel-VBA übertragen

// S: PAnsiChar          : Erstes Element des zu zu übertragenden StringVektors
// n_Anzahl: ExcelLongInt : Gesamtzahl der Strings im StringVektor


VAR
  Vektor: Array of PAnsiChar ABSOLUTE S;
  P    : AnsiString;
  i    : Integer;
 
Begin
  For i:= 1 To n_Anzahl Do             // Excel: 1-basiertes Array
  Begin
    P:= AnsiString_Vektor_Global[i-1]; // -1: Delphi: 0-basiertes Array    

    System.AnsiStrings.StrPLCopy(Vektor[i], P, Length(P));             // --> Access violation
    // System.AnsiStrings.StrCopy(PAnsiChar(Vektor[i]), PAnsiChar(P)); // --> Access violation
  End;
End;{AnsiStringVektor_To_Excel}

Naja, also so geht das ja nicht. Ein
Delphi-Quellcode:
array of PAnsiChar
ist ja kein
Delphi-Quellcode:
PAnsiChar
, sondern ein
Delphi-Quellcode:
PPAnsiChar
(Arrays sind Zeiger auf ihr erstes element, mit der Länge, dem Referenzzähler und der Typeninfo vorweg).
Delphi-Quellcode:
absolute
gibt dir zwar im gegensatz zu einem Typecast keinen Fehler aus, aber das heißt nicht, dass es so richtig ist.
Richtig muss
Delphi-Quellcode:
S
aber entweder ein
Delphi-Quellcode:
PPAnsiChar
oder
Delphi-Quellcode:
Vektor
ein
Delphi-Quellcode:
array of AnsiChar
(bzw. ein
Delphi-Quellcode:
PAnsiChar
) sein.
Auch kommt da jetzt noch folgendes hinzu: Arrays sind ja, wie eben schon gesagt, sowohl referenzgezählt, als auch typisiert und haben eine feste Länge. Gut möglich, dass dir das somit auch nochmal den Speicher zerschießt, da dafür in dem
Delphi-Quellcode:
PAnsiChar
ja gar nichts reserviert ist.

EWeiss 29. Apr 2019 23:38

AW: AnsiStringArray aus DLL an Excel-VBA
 
Sorry du kramst altes Zeug raus das mittlerweile auf anderem Wege schon gelöst ist.

gruss

Andreas13 30. Apr 2019 14:26

AW: AnsiStringArray aus DLL an Excel-VBA
 
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für Deine VBA-Lösung!
Ich habe sie so abgewandelt, daß der Text sofort in den Excel-Zellen erscheint. Dazu brauchen wir eine neue Funktion in Excel's VBA wie folgt:

VBA-Code
Delphi-Quellcode:
Public Function GetDelphiStringVektor(Anzahl As Long) ' entweder OHNE Typangabe oder As Variant, was gleichbedeutend ist, da dies eine Matrix-Funktion sein soll
' ... und so geht es direkt in die Zellen

Dim flist    As String
Dim LngP     As Long
Dim alist()  As String

Const Delimiter = 124

Dim StrVektor() As String
Dim i As Long
Dim n_Anz As Long
Dim Bis As Long

  LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|"

  flist = VBStrFromAnsiPtr(LngP)
  alist = Split(flist, "|")

  n_Anz = UBound(alist) - LBound(alist) + 1
 
  Bis = Anzahl
  If n_Anz < Anzahl Then Bis = n_Anz ' Hat denn VBA keine Funktion wie Pascal's Min?? --> Bis := Min(a_Anz, Anzahl)
 
  ReDim StrVektor(n_Anz, 0 To 0) ' SpaltenVektor

  If flist > "" Then
    For i = LBound(alist) To Bis - 1
      StrVektor(i, 0) = alist(i)
    Next
  End If

  GetDelphiStringVektor = StrVektor
End Function
Die beiden Lösungen sind zum Ausprobieren im beiliegenden Excel-File ToExcel_Test_2.xlsm vorhanden. Die DLL bleibt unverändert, daher habe ich sie nicht noch einmal hochgeladen.

Gruß, Andreas

EWeiss 30. Apr 2019 14:33

AW: AnsiStringArray aus DLL an Excel-VBA
 
Zitat:

Ich habe sie so abgewandelt
Wie ich schon sagte es bleibt dir überlassen wie du das später auslegen willst.

Aber gut wenn es jetzt funktioniert.
Mein Lösungsansatz war nur als Denkanstoß gedacht.

Zitat:

' Hat denn VBA keine Funktion wie Pascal's Min?? --> Bis := Min(a_Anz, Anzahl)
Min ist eine Excel Funktion oder?
Code:
WorksheetFunction.Min(20, 501, 561, 45, 78)
Wenn du das nicht willst dann mache dir selber was wenn du denkst es ist von nöten.
LBound = MIN
UBound = MAX

Code:
Private Function Min(ParamArray values() As Variant) As Variant
Dim i As Integer
Dim min_value As Variant

    min_value = values(LBound(values))
    For i = LBound(values) + 1 To UBound(values)
        If min_value > values(i) Then min_value = values(i)
    Next

    Min = min_value
End Function

Private Function Max(ParamArray values() As Variant) As Variant
Dim i As Integer
Dim max_value As Variant

    max_value = values(LBound(values))
    For i = LBound(values) + 1 To UBound(values)
        If max_value < values(i) Then max_value = values(i)
    Next

    Max = max_value
End Function

' Teste Min, Max
Private Sub cmdGo_Click()
    txtMin.Text = Format$(Min( _
        CInt("2873"), _
        CInt("4398"), _
        CInt("7846"), _
        CInt("1876"), _
        CInt("4792"), _
        CInt("3982"), _
        CInt("4863"), _
        CInt("9382")))
   
    txtMax.Text = Format$(Max( _
        CInt("2873"), _
        CInt("4398"), _
        CInt("7846"), _
        CInt("1876"), _
        CInt("4792"), _
        CInt("3982"), _
        CInt("4863"), _
        CInt("9382")))
End Sub
Code:
Const Delimiter = 124
Etwas unsauber weil du hier Delimiter ohne Angabe als Variant definierst.. ist aber Byte!
Wenn dann so!
Code:
Const Delimiter As Byte = 124
Siehe die Definition von PAnsiChar_To_Excel(bla bla.. Delimiter As Byte
Code:
Dim i As Long
Dim n_Anz As Long
Dim Bis As Long
zu
Code:
Dim i As Integer
Dim n_Anz As Integer
Dim Bis As Integer
Ein 32Bit DatenTyp ist für einfache for.. Next schleifen nicht nötig.
Zitat:

Die beiden Lösungen sind zum Ausprobieren im beiliegenden Excel-File ToExcel_Test_2.xlsm vorhanden
Habe Office wieder deinstalliert da ich es eigentlich nicht brauche und es nur eine Test Version war.

PS:
VBA ist kein Delphi daher wäre es besser du verwendest die Format Funktion des Forum
Code:
Dim x As Integer
Dim i As Integer
nicht
Delphi-Quellcode:
var
  x:Integer;
  i:Integer;
dann liest es sich besser. ;)

gruss

Andreas13 30. Apr 2019 16:08

AW: AnsiStringArray aus DLL an Excel-VBA
 
Danke, die Format-Funktion für den sonstigen Code habe ich erst jetzt gefunden...

Gruß, Andreas

EWeiss 10. Mai 2019 11:40

AW: AnsiStringArray aus DLL an Excel-VBA
 
Sorry das ich das nochmal vor krame..
Habe das vorhin nochmal gelesen und festgestellt das deine abfrage auf
Code:
  If flist > "" Then
viele zu spät kommt..

Was machst du wenn
Code:
If flist = ""
ist?

Dann wird das Array alist() nicht initialisiert und die nächste Abfrage lässt deine Anwendung abstürzen.
Code:
alist = Split(flist, "|")
n_Anz = UBound(alist) - LBound(alist) + 1
mache es direkt nach der Initialisierung von flist.

Code:
Public Function GetDelphiStringVektor(Anzahl As Long) As Variant

 Dim flist As String
 Dim LngP As Long
 Dim alist() As String

 Const Delimiter As Byte = 124

 Dim StrVektor() As String
 Dim i As Long
 Dim n_Anz As Long
 Dim Bis As Long

    LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|"

    flist = VBStrFromAnsiPtr(LngP)
    If flist > "" Then
        alist = Split(flist, "|")

        n_Anz = UBound(alist) - LBound(alist) + 1

        Bis = Anzahl
        If n_Anz < Anzahl Then Bis = n_Anz

        ReDim StrVektor(n_Anz, 0 To 0) ' SpaltenVektor
 
        For i = LBound(alist) To Bis - 1
            StrVektor(i, 0) = alist(i)
        Next

        GetDelphiStringVektor = StrVektor
    else
        GetDelphiStringVektor = ""
    End If

End Function
gruss

Andreas13 10. Mai 2019 16:12

AW: AnsiStringArray aus DLL an Excel-VBA
 
@EWeiss

Hab herzlichen Dank für die Verbesserungen!
Gruß
Andreas


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:41 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz