Clever-Excel-Forum

Normale Version: 64-bit, CopyMemory ValPtr...
Du siehst gerade eine vereinfachte Darstellung unserer Inhalte. Normale Ansicht mit richtiger Formatierung.
Guten Tag
Leider geht das jetzt auch nicht mehr mit 64-bit/Windows 11.00. Der Fehler ist dieses VarPtr. Gibt es hier auch eine andere Lösung für CopyMemory? Vielen Dank für Eure Hilfe.
Gruss
Stefan


Public Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As LongPtr)

Public Function Append(Value As String) As clsStringBuilder
Dim NewUBound As Long
Dim CapacityUBound As Long
    On Error GoTo Failed
    NewUBound = mUBound + LenB(Value)
    If NewUBound > UBound(mString) Then
        CapacityUBound = UBound(mString) * 2 + 1
        If NewUBound > CapacityUBound Then CapacityUBound = NewUBound * 2 + 1
        ReDim Preserve mString(0 To CapacityUBound)
    End If
    CopyMemory VarPtr(mString(mUBound + 1)), StrPtr(Value), LenB(Value)
    mUBound = NewUBound
    Set Append = Me
    Exit Function
Failed:
    Stop
    Resume
End Function
...damit es sowohl als auch funktioniert:

Code:
#If Win64 Or VBA7 Then
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
        Destination As Any, _
        Source As Any, _
        ByVal Length As LongPtr)

#Else
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
        Destination As Any, _
        Source As Any, _
        ByVal Length As Long)

#End If

...oder schau mal hier nach Quicksort
Guten Tag Ralf

Vielen Dank für den Hinweis, doch das mit dem QuickSort habe analog lösen können und die Weiche brauche ich tatsächlich nicht mehr, weil bei uns jetzt 64-bit ist. Doch leider habe ich das Problem mit CopyMemory und dort nicht mit der Deklaration, sondern leider mit diesem "VarPtr", welches jetzt nicht mehr vertragen wird.

Ich habe zwar versucht mit (Der letzte Wert sollte "long" sein?):
  
'CopyMemory VarPtr(mString(mUBound + 1)), StrPtr(Value), LenB(Value)
    CopyMemory (mString(mUBound + 1)), ByVal StrPtr(Value), CLngPtr(LenB(Value))

Doch das geht auch nicht. Wie kriege ich bloss dieses VarPtr wegen, dass mit 64-bit/Windows 11 nicht mehr geht?
Danke für jeden Hinweis.

gruss Stefan
(06.02.2024, 15:41)Stefan1 schrieb: [ -> ]Public Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As LongPtr)

Public Function Append(Value As String) As clsStringBuilder
Dim NewUBound As Long
....
....
    CopyMemory VarPtr(mString(mUBound + 1)), StrPtr(Value), LenB(Value)
    mUBound = NewUBound
....

Also, VarPtr ist ja der 1. übergebene Parameter an CopyMemory
Und so, wie es aussieht, ist die Variable VarPtr nicht deklariert. Deklariere sie mal mit Dim VarPtr as Variant
Zitat:Und so, wie es aussieht, ist die Variable VarPtr nicht deklariert. Deklariere sie mal mit Dim VarPtr as Variant
Ermsthaft jetzt

Obwohl, vielleicht ist das ein Workaround
Code:
    Dim d As LongPtr
    d = VarPtr(mString(mUBound + 1))
    CopyMemory d, StrPtr(Value), LenB(Value)
und ähnlich auch für  StrPtr(Value), also eher 
Code:
    Dim d As LongPtr, s As LongPtr
    d = VarPtr(mString(mUBound + 1))
    s = StrPtr(Value)
    CopyMemory d, s, LenB(Value)
Andererseits soll für beide Funktionen gelten
Zitat:Returns a LongPtr on a 64 bit version and a Long on a 32 bit version.

Und zum Schluß: 
geht nicht ist natürlich keine Fehlerbeschreibung! 
Und woher hast Du den Code kopiert. Selber hast Du den ja nicht geschrieben!

PS CopyMemory ist natürlich eine "gefährliche" Aktion und kann mindestens Excel in den Abgrund ziehen.
Guten Tag Ralf
Vielen Dank für Deine Bemühungen. Ja, das habe ich nicht selber geschrieben. Es entstammt, so meine ich, einem sehr schnellen Code für das Herunterschrieben in eine Textdatei aus der Tabelle und hat sich in der Tat sehr bewährt. Nun kommt Windows 11/64-bit und alles ist vorbei.

Ich habe noch folgendes versucht und auch ohne ByVal:

Dim d As LongPtr, s As LongPtr, q As Long
    d = VarPtr(mString(mUBound + 1))
    s = StrPtr(Value)
    q = LenB(Value)
   
    'CopyMemory VarPtr(mString(mUBound + 1)), StrPtr(Value), LenB(Value)
    'CopyMemory (mString(mUBound + 1)), ByVal StrPtr(Value), CLngPtr(LenB(Value))
    CopyMemory ByVal d, ByVal s, q

... wobei ich noch auf wie folgt zurückgestellt habe:
Public Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Was ist bloss "Any" und ich finde kaum ein einheitliche Deklaration für CopyMemory. "VarPtr" muss meines wissens nicht deklariert werden, weil es sich hier um eine integrierte Funktion handelt und erstaulich geht diese mit Deinem Vorschlag ohne Fehler und liefert Zahlenwerte (wie wohl erwartet).

Doch leider stürzt es wieder bei CopyMemory ab. Verflixt.

17

oh, ich irre mich. Es geht tatsächlich mit zusätzlich diesen byVal (die ich nicht ganz verstehe):

Public Function Append(Value As String) As clsStringBuilder
Dim d As LongPtr, s As LongPtr, q As Long
Dim NewUBound As Long
Dim CapacityUBound As Long
    On Error GoTo Failed
    NewUBound = mUBound + LenB(Value)
    If NewUBound > UBound(mString) Then
        CapacityUBound = UBound(mString) * 2 + 1
        If NewUBound > CapacityUBound Then CapacityUBound = NewUBound * 2 + 1
        ReDim Preserve mString(0 To CapacityUBound)
    End If
    d = VarPtr(mString(mUBound + 1))
    s = StrPtr(Value)
    q = LenB(Value)  
    CopyMemory ByVal d, ByVal s, q
    mUBound = NewUBound
    Set Append = Me
    Exit Function
Failed:
    Stop
    Resume
End Function

Gibt es einen Vorteil mit LongPtr-Deklarationen oder soll ich die eher bei Long belassen? Dazu soll "Any" passen; was ist das bloss.
Vielen Dank für Deine Bemühungen. Das war doch der richtige Weg zur Lösung. 
Ja, dieses CopyMemory ist nicht ganz ungefährlich wie ich sehe, weil ein Error-Handling hier gar nichts nützt. Der Maximalmenge des Durchlaufes ist begrenzt (die lasse ich so) und soll helfen, dass es nicht zum Überlauf kommt. Die Performance beim Speichern ist jetzt aber beeindruckend gegenüber vorher.
DLL's wurden in C geschrieben. Und Any ist in C in etwa das, was in VBA der Variant ist. Kann also alles sein, weil in C alles von Object abgeleitet wird.

Deshalb versuche (wenn Du schon d verwendest) das:
PHP-Code:
Dim d as variant
VarPtr(....) 

Und schau mal nach der Funktion VarPtr, welchen Datentyp die zurück gibt. Wenn sie einen Objecttyp zurückgibt, musst Du d setten. Also Set d = VarPtr(....)
Hallo zusammen,

hier noch ein paar Gedanken dazu von mir....

Es gibt etliche Funktionen in der API, die mit ANY arbeiten. Z.B. "GetPrivateProfileString". 

Code:

Declare PtrSafe Function GetPrivateProfileStringA Lib "kernel32"  ( _
   ByVal lpApplicationName As String, _
   ByVal lpKeyName As Any, _
   ByVal lpDefault As String, _
   ByVal lpReturnedString As String, _
   ByVal nSize As Long, _
   ByVal lpFileName As String) As Long



Als lpKeyName wird hier aber nur ein String akzeptiert, so dass die Funktion auch mit  der Deklaration ByVal lpKeyName As String funktioniert und diese Deklaration auch logischer wäre.

VarPtr und StrPtr sind m.E. Zeiger auf einen Memorybereich (Ptr=Pointer). Aufnehmende Variablen müssen daher mindestens LongPtr sein.
Während die übergebene Größe des zu kopierenden Bereichs auch mit Long funktionieren müsste und kein Zeiger sein muss.
Das ist bei der 32-Bit-Deklaration auch so.

Für CopyMemory bzw. für den Originalnamen der Funktion RtlMoveMemory heißt das für mich, es sollte für diesen Fall auch so funktionieren (ungetestet, da grad kein Beispiel zur Hand):

Declare PtrSafe Sub RtlMoveMemory Lib "kernel32" ( Destination As LongPtr, Source As LongPtr, ByVal Length As Long)

ByVal bedeutet, dass eine Übergabe als Wert erfolgt und damit die aufrufende Variable von der Sub/Funktion nicht verändert werden kann.
Manche API-Funktionsaufrufe müssen als ByVal ausgeführt werden, da es sonst zu Fehlern kommt.

Hier noch zwei reale Beispiele, das eine verwende ich oft, um einen "Lost Ribbon" wieder herzustellen, das andere dient zur Umwandlung eines Punktes in LongLong.

Code:

Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
  Dim objRibbon As Object
  CopyMemory objRibbon, lRibbonPointer, LenB(lRibbonPointer)
  Set GetRibbon = objRibbon
  Set objRibbon = Nothing
End Function



Code:

Private Function Point2LongLong(Point As POINTAPI) As LongLong
 If LenB(Point) = LenB(Point2LongLong) Then _
  CopyMemory Point2LongLong, Point, LenB(Point2LongLong)
End Function


Hier sieht man, dass als Übergabedeklaration entweder ANY angesagt ist oder eben zugeschnitten nur für den entsprechenden Einsatz.
Und da ja in beiden Fällen eine Rückantwort erwartet wird, darf hier nicht mit ByVal gearbeitet werden.

Gruß aus Hessen
Karl-Heinz