Einzelnen Beitrag anzeigen

Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
711 Beiträge
 
Delphi XE5 Professional
 
#1

AnsiStringArray aus DLL an Excel-VBA

  Alt 25. Apr 2019, 22:21
Delphi-Version: XE5
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
Angehängte Dateien
Dateityp: zip Delphi_String_for_Excel_Test.zip (351,0 KB, 3x aufgerufen)
  Mit Zitat antworten Zitat