vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
vb@rchiv Offline-Reader - exklusiv auf der vb@rchiv CD Vol.4  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: Entwicklungsumgebung · VB-IDE allgemein   |   VB-Versionen: VB611.07.05
Veränderung der Größe des Stapelspeichers

Änderung des Stack-Size beim Linken und Anforderung einer MAP-Datei

Autor:   Manfred BohnBewertung:     [ Jetzt bewerten ]Views:  21.057 
ohne HomepageSystem:  WinNT, Win2k, WinXP, Win7, Win8, Win10, Win11kein Beispielprojekt 

Stapel (engl.: Stack): Ein festgelegter Speicherbereich, der von Visual Basic verwendet wird, um lokale Variablen und Argumente zwischen Prozeduraufrufen zwischenzuspeichern (VB-Dokumentation).

Bei jedem Aufruf einer Funktion wird Platz auf dem Stapel für die Parameter der Funkton und für die lokalen Variablen benötigt und beim Verlassen der Funktion automatisch wieder freigegeben.

Mögliche Ursachen für einen Überlauf des Stapelspeichers:

Bei rekursiven Funktionsaufrufen (d.h. eine Funktion ruft sich selbst auf oder ruft eine zweite Funktion die ihrerseits die erste wieder aufruft usw.) kann es zu einer langen Schlange im Stapel kommen. Der Fehler „Nicht genügend Stapelspeicher" zeigt an, dass der Stapelbereich voll ist. Falls in diesem Fall statt lokaler Variablen modulglobale Variable verwendet werden, "schont" das den Stapelspeicher. Allerdings greifen dann alle rekursiven Aufrufe auf die gleichen Variablen zu - ein Effekt, der häufig nicht gewünscht wird. Ein bekanntes Beispiel für rekursive Programmierung ist der Quicksort-Algorithmus.

Ein andere Ursache für das Anwachsen des Stapelspeicher-Bedarfs sind sogenannte Ereignisketten: Wenn in einer Ereignisbehandlungs-Routine auf bestimmte Steuerelemente-Eigenschaften zugegriffen wird, werden automatisch andere Ereignisse ausgelöst, deren Behandlungsroutinen ihrerseits Ereignisse provozieren (Kaskadeneffekt). Man sollte deshalb vor der Abarbeitung von Code zur Ereignisbehandlung eine formular-globale boolsche Variable auf 'true' setzen und danach wieder auf 'false'. In allen 'sensiblen' Ereignisroutinen (z.B. 'Change'-Ereignisse) ist am Anfang diese Variable abzufragen: Ist sie auf 'true' gesetzt, wird nichts gemacht.

Da VB-Programme standardmäßig mit einem genügend großen Stapel (1 Megabyte) ausgestattet werden, weisen Stapelprobleme meist auf Fehler in Rekursion oder Ereignisbehandlung hin.

Die VB-Dokumentation enthält leider nur wenige Hinweise bezüglich der Verwendung des Stapels:

  • lokale Variable, die als STATIC deklariert werden, benötigen nur wenig Stapelspeicher
  • Strings variabler Länge benötigen weniger Stapelspeicher als Strings fester Länge
  • verschachtelte Aufrufe in Folge häufiger DOEVENTS-Anweisungen können den Stapel stark beanspruchen

Manipulation der Größe des Stapelspeichers:
Die Größe des Stapelspeichers, den ein Programm verwenden kann, wird beim Linken festgelegt. Beim Start eines Programms wird die vom Linker verzeichnete Größe des Stapelspeichers für die Anwendung reserviert. Eine dynamische Anpassung der Größe des Stapels während der Programmausführung ist nicht möglich.

Wenn die Stapelgröße eines Programms geändert werden soll, muss beim Linken die Option 'STACK' gesetzt werden.
Um den Linkeraufruf der VB-IDE zu manipulieren, wird das Verfahren angewendet, das bereits im Tipp 1289 'Erstellen von Compiler-Listing-Dateien' zur Ergänzung einer Kompiler-Option herangezogen worden ist:

  1. Erstellen Sie ein neues Standard-Exe-Projekt und fügen Sie ein Standardmodul ein
  2. Entfernen Sie das Formular 'Form1' aus dem Projekt.
  3. Kopieren Sie den beigefügten Quellcode in das Standardmodul.
  4. Übersetzen Sie das Projekt.
  5. Gehen Sie in das Stammverzeichnis von VB6 (meist: "C:\Programme\Microsoft Visual Studio\VB98" oder ähnlich) und benennen Sie die Datei 'LINK.EXE' (das ist der Linker!) in 'VBLINK.EXE' um.
Kopieren Sie die unter 4.) erstellte EXE-Datei in das VB6-Verzeichnis und nennen Sie sie 'LINK.EXE'.

Ab sofort werden alle Linkeraufrufe der VB-IDE von LINK.EXE abgefangen.
Zunächst wird gefragt, ob vom Linker eine MAP-Datei erstellt werden soll, die folgende Angaben enthält:

  • Zeitangabe aus dem Header der Programmdatei
  • Liste von Gruppen im Programm (jeweils Startadresse [als Abschnitt:Offset], Länge, Gruppennamen und Klasse)
  • Liste der öffentlichen Symbole (jeweils Adresse [als Abschnitt:Offset], Symbolname und definierende .OBJ-Datei)
  • Einsprungpunkt (als Abschnitt:Offset)

Vor dem Aufruf des Linkers wird dann eine VB-Input-Box angezeigt, in der die gewünschte Größe des Stapels festgelegt werden kann (in Kilobyte). Eingaben unter 250 werden auf 250 KB gesetzt, Eingaben über 5000 werden auf 5000 KB gesetzt. Bei Betätigung der Taste 'Abbrechen' und bei 'leerer' oder unplausibler Eingabe wird der Standardwert von 1 MB verwendet.

Details zum Stapel:
(Die Angaben zum Stapel-Bedarf sind durch Probieren gefundene ungefähre Werte, die für Funktions-Aufrufe innerhalb eines Standard-Exe-Projekts gelten):

  • Die Mindestgröße des Stapel beträgt ca. 250 KB; geringere Anforderungen werden vom Linker automatisch hochgesetzt.
  • Der Stapel kann durch die STACK-Option auf 5 MB hochgesetzt werden (noch größere Werte sind nicht getestet worden!)
  • Ein Funktionsaufruf (ohne Argumentliste) benötigt ca. 90 Byte auf dem Stapel (innerhalb einer Form oder einem Klassenmodul), aber nur ca. 65 Byte innerhalb eines Standardmoduls.
  • Der Stapelbedarf eines lokalen, statischen Array beträgt ca. 35 Byte und ist unabhängig von der Zahl der Array-Elemente und vom Datentyp des Array (im Hinblick auf 'integrierte VB-Datentypen') - gilt auch für String-Arrays variabler oder fester Länge und für Arrays aus benutzerdefinierten Datentypen. (Jede zusätzliche Dimension belegt etwa 10 Byte auf dem Stapel).
  • Bei dynamisch deklarierten lokalen Arrays wird nur ein Zeiger (8 Byte) auf dem Stapel abgelegt, unabhängig von Datentyp und von den Dimensionen des Array.
  • Bei Übergabe eines dynamisch deklarierten Array als Argument 'BYREF' werden ca. 100 Byte auf dem Stapel abgelegt (innerhalb Form oder Klasse) bzw. 70 Byte innerhalb eines Standardmoduls.
  • Bei Übergabe eines dynamisch deklarierten Array als Argument 'BYVAL' werden ca. 135 Byte auf dem Stapel abgelegt (innerhalb Form oder Klasse) bzw. 105 Byte innerhalb eines Standardmoduls.

Fazit:
Unter normalen Umständen gibt es keinen Grund, die Standard-Größe des 'STACK' zu ändern. Visual Basic nutzt den Stapelspeicher 'sparsam' und setzt dabei meist Zeiger ein.

' ====================================================================
' Programm, das die IDE-Linkeraufrufe abfängt und
' die Optionen hinzufügt (falls gewünscht)
 
' Schritt-Folge:
' 1. Dieses Programm übersetzen ('Sub Main')
' 2. VB6-Linker 'Link.Exe' in VBLink.EXE umbenennen
' 3. Dieses Programm muss 'Link.EXE' genannt und
'    in den VB6-Ordner kopiert werden
' ====================================================================
 
Option Explicit
 
' vereinfachte UDTs für die Prozess-Erzeugung
Private Type PROCESS_INFO
  hProcess As Long
  x(1 To 12) As Byte
End Type
 
Private Type STARTUP_INFO
  Size As Long
  Reserved As String
  Desktop As String
  Title As String
  x(1 To 28) As Byte
  Flags As Long
  Show As Integer
  y(1 To 16) As Byte
End Type
 
' API-Funktionen für die Prozess-Erzeugung
Private Declare Function CreateProcess Lib "Kernel32" _
  Alias "CreateProcessA" ( _
  ByVal AppName As Long, _
  ByVal CmdLine As String, _
  ByVal ProcAttr As Long, _
  ByVal ThreadAttr As Long, _
  ByVal InheritedHandle As Long, _
  ByVal CreationFlags As Long, _
  ByVal Env As Long, _
  ByVal CurDir As Long, _
  Startup As STARTUP_INFO, _
  Process As PROCESS_INFO) As Long
 
Private Declare Function WaitForSingleObject Lib "Kernel32" ( _
  ByVal Handle As Long, _
  ByVal MilliSeconds As Long) As Long
 
Private Declare Function CloseHandle Lib "Kernel32" ( _
  ByVal Object As Long) As Long
 
' Rückgabe von WaitForSingleObject
Private Const WAIT_TIMEOUT = 258&
Private Const WAIT_FAILED = &HFFFFFFFF
Private Const INFINITE = &HFFFFFFFF
 
Const cTitel As String = "VB-Link (Systemcode)"
Private Sub Main()
  ' VB-Linkeraufrufe anfangen und MAP/STACK setzen
 
  Dim rs As String, r As Long  ' Dialog-Reaktion
  Dim CmdLine As String        ' Kommandozeile
  Dim st As Long               ' Größe des Stack
  Dim t1 As Long, t2 As Long   ' Position Dateiname
  Dim exedat As String         ' Name der zu erstellenden Datei
  Dim dat As String, d() As String
 
  ' Abfrage des IDE-Linkeraufrufs
  CmdLine = Command()
 
  ' Name der Ausgabedatei des Linkers ermitteln (für Dialog)
  t1 = InStr(CmdLine, "/OUT:")
  t2 = InStr(CmdLine, "/BASE:")
  If t2 > t1 + 10 And t1 > 0 Then
    exedat = Mid(CmdLine, t1 + 6, t2 - (t1 + 6) - 2)
    d() = Split(exedat, "\")
    dat = Trim(d(UBound(d)))
  End If
 
  ' Mapfile gewünscht?
  r = MsgBox("Erstellte Datei: " + vbCrLf + dat + _
    vbCrLf + vbCrLf + "Map-File erstellen?", vbYesNo, cTitel)
  If r = vbYes Then CmdLine = CmdLine + " /MAP"
 
  ' Stacksize festlegen
  rs = InputBox("Erstellte Datei: " + vbCrLf + dat + vbCrLf + vbCrLf + _
    "Stackgröße (KB): " + vbCrLf + _
    "mindestens 250 KB", cTitel, " 1024 ")
 
  If StrPtr(rs) <> 0 And rs <> "" Then
    ' Stackgröße auslesen und in Bytes wandeln
    st = DLNG(rs) * 1024
    CmdLine = CmdLine + " /STACK:" + CStr(st)
  End If
 
  ' VB6-Linker aufrufen: umbenannt als 'VBLink.EXE'
  ShellWait App.Path + "\VBLink.exe " + CmdLine      
End Sub
Private Function DLNG(ByVal rs As String) As Long
  ' Auswertung des Rückgabe-Strings
  Dim d As Long
 
  On Error GoTo fehler
 
  d = CLng(rs)
  ' sinnvolle Stackgröße setzen (Kilobytes)
  If d < 250 Then d = 250
  If d > 5000 Then d = 5000
  DLNG = d
  Exit Function   
 
fehler:
  DLNG = 1000      ' Standardwert bei unplaus. Eingabe
End Function
Public Function ShellWait(ByVal Execute As String) As Boolean
  ' Funktion erstellt einen Prozess und wartet
  ' bis der Prozess sich meldet bzw. abgeschlossen ist
 
  Dim Prc As PROCESS_INFO                ' Infos zum erstellten Prozess
  Dim Startup As STARTUP_INFO            ' Prozess-Start-Infos
  Dim Ret As Long                        ' API-Rückgabe
 
  Const MilliSeconds As Long = INFINITE  ' unbegrenzt warten
 
  ' Initialisierung Prozess-Start
  With Startup
    .Size = Len(Startup)
    .Flags = 1
  End With
 
  ' Prozess generieren
  Ret = CreateProcess(0&, Execute, 0&, 0&, 1&, &H20&, 0&, 0&, Startup, Prc)
  If Ret = 0 Then
    MsgBox "Warnung: Linker lässt sich nicht starten", vbCritical, cTitel
    DoEvents
    Exit Function
  End If
 
  ' Kompilierung abwarten
  Ret = WaitForSingleObject(Prc.hProcess, MilliSeconds)
  If Ret = WAIT_FAILED Or Ret = WAIT_TIMEOUT Then
    MsgBox "Warnung: Linker reagiert nicht", vbCritical, cTitel
    DoEvents
  End If
 
  ' Prozess-Handle freigeben
  CloseHandle Prc.hProcess        
End Function
' ====================================================================

Dieser Tipp wurde bereits 21.057 mal aufgerufen.

Voriger Tipp   |   Zufälliger Tipp   |   Nächster Tipp

Über diesen Tipp im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Tipp, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel