Organisatorischer Hinweis:
Die hier dokumentierten Fragen wurden aus Forendiskussionen zusammengefasst und mit KI-Unterstützung beantwortet.
Es handelt sich nicht zwingend um die einzig richtige oder fehlerfreie Lösung, sondern um ergänzende Anregungen
und Zweitmeinungen.
Ziel ist auch, die eigentliche Problemstellung und die möglichen Antworten kompakt und strukturiert
darzustellen. So lässt sich für den eigenen Überblick schnell erfassen, worum es ging und welche Lösungsansätze
diskutiert wurden – eine nützliche Ergänzung zu den Foren, die den vollständigen Austausch und die Details
enthalten.
Genau – so wird es: eine nette Ergänzung zu den Foren, kein Ersatz.
Man gewinnt:
- eine strukturierte Zusammenfassung der eigentlichen Frage,
- die möglichen Antworten (Forenbeiträge + KI-Hinweis),
- einen kompakten Überblick ohne sich durch lange Threads lesen zu müssen,
- und trotzdem den Hinweis, dass die endgültige Lösung im Forum bzw. durch eigene Tests abgesichert werden sollte.
Damit entsteht eine zweite Ebene:
👉 Forum = Diskussion, Austausch, Detailtiefe.
👉 FAQ/Accordion = Übersicht + schnelle Orientierung.
Letzte Aktualisierung: 2025-11-07
Frage: Ich habe mit XbpHtmlViewer2() experimentiert, insbesondere mit den Buttons „Home“ und „Search“, die bei mir standardmäßig MSN bzw. Bing aufrufen. Kann man das ändern? Und ist es normal, dass im Verlauf nur „GET“ steht?
Antwort: Der XbpHtmlViewer2() basiert intern auf einer eingebetteten Browser-Engine (z. B. IE oder WebView2), wobei viele Steuerfunktionen wie „Home“, „Search“, „History“ nur eingeschränkt anpassbar sind – vor allem in Bezug auf eingebettete URLs und Sicherheitseinstellungen.
Statt die Viewer-Funktionen mühsam zu verbiegen, lohnt sich in vielen Fällen der Umstieg auf eine schlanke Microservice-Lösung: Du trennst GUI und Webanzeige und zeigst gewünschte Inhalte (z. B. PDF-Vorschau oder Suchseiten) direkt im Systembrowser an – z. B. mobilfreundlich und steuerbar.
Ein Beispiel: Statt eine PDF im Viewer anzuzeigen, wird sie vom Server automatisch in ein Bild (JPG) konvertiert und im Browser dargestellt – völlig ohne Toolbar, „Öffnen mit“-Dialog oder Plugin-Abhängigkeit.
Gerade für Reports, Rechnungen oder Infoscreens ist dieser Ansatz stabiler und zukunftssicherer – insbesondere auf Mobilgeräten.
Frage: Wie kann ich die Breite einer Spalte in einem xbpBrowse ermitteln, und werden diese Werte automatisch aktualisiert, wenn der Benutzer die Spaltenbreite mit der Maus verändert, sodass ich sie beim erneuten Aufbau des Browsers wiederverwenden kann?
Antwort: Die aktuelle Spaltenbreite liest du über das Column-Objekt mit CurrentSize() aus. Für die Spalte an Position nPosition liefert oBrowse:GetColumn( nPosition ):CurrentSize()[1] die Breite in Pixeln. Wenn du jeder Spalte eine eigene Variable zuweist, kannst du direkt oColumn:CurrentSize()[1] verwenden. Diese Werte werden nach manueller Anpassung per Maus aktualisiert, sodass du sie speichern und beim Neuaufbau des Browsers wieder verwenden kannst; eine Umrechnung in andere Einheiten müsstest du bei Bedarf selbst vornehmen.
Frage: Wie setze ich in eXpress++ eine einheitliche Eingabemaske um, sodass alle Labels gleich breit sind und Zeilen nicht überlappen – idealerweise mit globalen Defaults und ohne SIZE/SAYSIZE 0? Gibt es einen Demo-Source?
Antwort: Setze globale Standards mit DC_GetOptDefault() (Fonts, Abstände, Tabstop etc.), ermittle die maximale Labelbreite zur Laufzeit mit DC_GraQueryTextBox() (proportionale Schrift!), verwende diese Breite für alle DCSAY und gib den Eingabeelementen eine fixe Spaltenbreite. Leite die Zeilenhöhe aus der Eingabeschrift ab (plus Padding) und verwende sie konsistent. Trenne bei Bedarf DCSAY und DCGET (z. B. für Combobox unter SLE). Unten ein kompakter Demo-Source.
Demo-Source (eXpress++) – gemessene Labelbreite, Defaults, saubere Zeilenhöhe
/* demo_uniform_form.prg – eXpress++ Layout mit gemessener Labelbreite und Defaults */
#include "dcgui.ch"
STATIC aFields := { ;
{ "Kundennummer", "SLE", @cKdNr }, ;
{ "Name", "SLE", @cName }, ;
{ "Ort", "SLE", @cOrt }, ;
{ "Land", "COMBO", @cLand } ;
}
PROC Main()
LOCAL oDlg, oFontSay, oFontGet
LOCAL nMaxSayW := 0, nRow := 12, nCol := 12
LOCAL nPad := 8, nGetW := 260, nRowH
/* Beispiel-Variablen */
PRIVATE cKdNr := "", cName := "", cOrt := "", cLand := "AT"
LOCAL aLaender := { "AT","DE","IT","CH","SI" }
/* Proportionale Fonts */
oFontSay := XbpFont():new():create( "Segoe UI", 9 )
oFontGet := XbpFont():new():create( "Calibri" ,10 )
/* 1) Globale Defaults (wirken für nachfolgende Controls) */
DC_GetOptDefault( { ;
{ "SAYFONT" , oFontSay }, ;
{ "GETFONT" , oFontGet }, ;
{ "ROWSPACE" , 4 }, ;
{ "COLPIXELS" , 8 }, ;
{ "TABSTOP" , .T. }, ;
{ "CHECKGET" , .T. }, ;
{ "SAYOPTIONS", XBPSTATIC_TEXT_VCENTER } ;
} )
/* 2) Maximale Labelbreite messen (Pixel) */
nMaxSayW := MeasureMaxLabelWidth( aFields, oFontSay ) + 16
/* 3) Zeilenhöhe aus Eingabe-Font ableiten */
nRowH := FontPixelHeight( oFontGet ) + 8
/* 4) Dialog */
@ 0,0 DIALOG oDlg TITLE "Demo – einheitliche Eingabemaske" ;
FONT "Segoe UI" SIZE 9 ;
CLIPPER
/* 5) Felder zeilenweise aufbauen */
AEVAL( aFields, {|r|
LOCAL cCap := r[1], cTyp := r[2], uRef := r[3]
@ nRow, nCol DCSAY cCap SAYSIZE nMaxSayW, nRowH
DO CASE
CASE cTyp == "COMBO"
@ nRow, nCol + nMaxSayW + nPad DCCOMBO uRef ITEMS aLaender SIZE nGetW, nRowH
OTHERWISE
@ nRow, nCol + nMaxSayW + nPad DCGET uRef SIZE nGetW, nRowH
ENDCASE
nRow += nRowH + 4
} )
/* 6) Buttons */
@ nRow+8, nCol DCPUSHBUTTON "&OK" SIZE 90, nRowH ACTION {|| oDlg:end() }
@ nRow+8, nCol + 100 DCPUSHBUTTON "Abbrechen" SIZE 120, nRowH ACTION {|| oDlg:end() }
ACTIVATE DIALOG oDlg
RETURN
/* ------- Helpers ------- */
/* Größte Labelbreite (Pixel) aus aFields ermitteln */
FUNCTION MeasureMaxLabelWidth( aFlds, oFontSay )
LOCAL nMax := 0
AEVAL( aFlds, {|r| nMax := MAX( nMax, DC_GraQueryTextBoxWidth( r[1], oFontSay ) ) } )
RETURN nMax
/* Breite via DC_GraQueryTextBox() */
FUNCTION DC_GraQueryTextBoxWidth( cText, oFont )
LOCAL aBox := DC_GraQueryTextBox( cText, oFont ), nW := 0
IF VALTYPE( aBox ) == "A" .AND. LEN( aBox ) >= 1
nW := aBox[1]
ENDIF
RETURN nW
/* Einfache Fonthöhen-Ermittlung */
FUNCTION FontPixelHeight( oFont )
LOCAL aBox := DC_GraQueryTextBox( "Ag", oFont ), nH := 18
IF VALTYPE( aBox ) == "A" .AND. LEN( aBox ) >= 2
nH := aBox[2]
ENDIF
RETURN nH
Abkürzungs-Legende
UI/UX – Gestaltung und Benutzbarkeit von Software-Oberflächen.
SLE – Single-Line Edit, einzeiliges Eingabefeld.
MLE – Multi-Line Edit, mehrzeiliges Eingabefeld.
GC – Graphics Context, Zeichenkontext zum Messen/Rendern.
DLU – Dialog Units, dialogabhängige Maßeinheiten in Windows.
Frage: Gegeben ist Pop3client(). Datum, Betreff, Absender, Zeit sind vorhanden – wie greife ich auf den Body (eigentlichen Mailtext) zu?
Antwort (Kurzfassung): Über das Message-Objekt. Beispiel: oMsg := oPop3:GetMessage(n) → oMsg:GetContent() liefert den Body (ggf. als MIME-Struktur).
// Prinzip
oMsg := oPop3:GetMessage(n)
cBody := oMsg:GetContent() // liefert Text oder multipart-Struktur
Frage: GetContent() liefert viele Informationen. Wo ist der reine Text?
Antwort: Mails sind oft multipart. Logik: erst text/plain suchen, sonst text/html nehmen und bei Bedarf zu Text konvertieren.
FUNCTION GetBestBody(oMsg)
LOCAL cType := Upper(oMsg:ContentType())
LOCAL cBody := ""
IF Left(cType,9) == "MULTIPART"
FOR EACH oPart IN oMsg:Parts()
IF Upper(oPart:ContentType()) == "TEXT/PLAIN"
RETURN oPart:GetContent()
ENDIF
NEXT
FOR EACH oPart IN oMsg:Parts()
IF Upper(oPart:ContentType()) == "TEXT/HTML"
cBody := oPart:GetContent()
RETURN StripHtml(cBody) // HTML zu Text
ENDIF
NEXT
ELSEIF Left(cType,4) == "TEXT"
RETURN oMsg:GetContent()
ENDIF
RETURN cBody
ENDFUNC
Frage: Beim Insert in eine SQL-Tabelle gibt es wegen Kommas/Quotes Fehler. Wie löse ich das korrekt?
Antwort: Immer parametrisierte Queries nutzen (Prepared Statements). Keine String-Konkatenation.
// Beispiel mit ADO/OLE DB/ODBC
LOCAL oCon := CreateObject("ADODB.Connection")
LOCAL oCmd := CreateObject("ADODB.Command")
oCon:Open("Provider=MSDASQL;DSN=MyDSN;UID=...;PWD=...")
oCmd:ActiveConnection := oCon
oCmd:CommandText := "INSERT INTO mails (msgdate, subject, sender, body) VALUES (?, ?, ?, ?)"
oCmd:CommandType := 1 // adCmdText
// adDate=7, adVarWChar=202, adLongVarWChar=203, adParamInput=1
oCmd:Parameters:Append(oCmd:CreateParameter("p1",7, 1,0, dMsgDate))
oCmd:Parameters:Append(oCmd:CreateParameter("p2",202,1,255, cSubject))
oCmd:Parameters:Append(oCmd:CreateParameter("p3",202,1,255, cFrom))
oCmd:Parameters:Append(oCmd:CreateParameter("p4",203,1,0, cBody))
oCmd:Execute()
oCon:Close()
Feldtypen: MySQL/MariaDB: TEXT/LONGTEXT · SQL Server: NVARCHAR(MAX) · SQLite: TEXT
Charset: Aus Content-Type lesen und in UTF-8 speichern (z. B. MySQL: SET NAMES utf8mb4).
Frage in einem Satz – {KURZE-ZUSAMMENFASSUNG-DER-FRAGE}.
Kurzlösung – {KERNLÖSUNG-IN-1–2-SÄTZEN}.
- {Schritt 1}
- {Schritt 2}
- {Schritt 3}
- {… so viele wie nötig, aber prägnant}
{AUTO-LISTE-RELEVANTER-ABKÜRZUNGEN, max. 10, Form: ABK – kurzer Klartext. Nur anzeigen, wenn Abkürzungen vorkommen.}
… für kurze Codefragmente.
- Keine externen Kommentare, keine Erklärabsätze außerhalb des HTML-Blocks.
- „Abkürzungs-Legende“: Nur wenn im Text Abkürzungen vorkommen. Max. 10 Einträge, erste Vorkommen sammeln. Format: ABK – 1-Satz-Erklärung, laienverständlich. Überspringe triviale Einheiten (m, km, kg, min), außer sie sind speziell.
- Kein Inline-CSS, nur Klassen wie im Muster. IDs/Anker strikt mit q{QNUM} konsistent halten.
- Aus Topic-Verlauf nur das Relevante, Redundanzen entfernen, keine sensiblen Daten.
EINGABEN
Q-Nummer: {HIER Q-NUMMER EINFÜGEN, z. B. 24}
Topic-Verlauf (roh):
"""
{HIER DEN KOPIERTEN TOPIC-VERLAUF EINFÜGEN}
"""
AUSGABE
Gib ausschließlich den fertigen HTML-Block gemäß obigem Schema aus – keine weiteren Worte.
- Mail laden → Body mit der Logik aus Q22 ermitteln.
- Zeichensatz prüfen → nach UTF-8 konvertieren.
- Prepared Statement mit Platzhaltern (
?) aufbauen. - Parameter binden (Datum, Betreff, Absender, Body).
- Ausführen; optional in einer Transaktion für viele Inserts.
MIME – Containerformat für E-Mails mit mehreren Teilen (Text, HTML, Anhänge).
POP3 – Protokoll zum Abrufen von E-Mails vom Server.
SQL – Abfragesprache für Datenbanken; mit Parametern sicher gegen Fehler/Injection.
UTF-8 – Zeichenkodierung, wichtig für Umlaute/Emoji.
ODBC – Generische Datenbankschnittstelle unter Windows.
DSN – Vorkonfigurierter ODBC-Verbindungsname.
Frage: Eine Xbase/Harbour-EXE läuft als App, startet aber als Windows-Dienst auf Windows 11 25H2 nicht (Fehler 1053). Main() scheint nie erreicht zu werden. Was sind die typischen Ursachen und wie behebt man es zuverlässig?
Antwort (Kurzfassung): 1053 signalisiert, dass der Dienst nicht rechtzeitig antwortet. In der Praxis sind es fast immer
Arbeitsverzeichnis/DLL-Pfad, gemappte Laufwerke, blockierende INIT/DBESYS/APPSYS oder das 30-Sekunden-Timeout.
Behebe das mit festem AppDirectory, UNC-Pfaden, frühem Logging und (falls nötig) höherem ServicesPipeTimeout.
- Arbeitsverzeichnis fixen: Dienste starten aus
C:\Windows\System32. Setze per NSSMAppDirectory = C:\webserveroder wechsle im INIT PROCEDURE ins EXE-Verzeichnis. - Keine gemappten Laufwerke:
X:\existiert im Dienst nicht. Verwende UNC (\\server\share) und hänge ggf. AbhängigkeitLanmanWorkstationein:sc config MeinDienst depend= Tcpip/LanmanWorkstation. - Früh-Logging + INIT entkernen: Erste Zeile in INIT/DBESYS/APPSYS → Log schreiben, UI-Aufrufe strikt vermeiden, lange I/O auslagern.
- Timeout erhöhen (falls wirklich nötig):
HKLM\SYSTEM\CurrentControlSet\Control\ServicesPipeTimeout(DWORD, ms) z. B. 120000, danach Neustart. - Fehler schnell sichtbar machen: Mit ProcMon auf NAME NOT FOUND / ACCESS DENIED filtern; häufig fehlende DLL oder falscher Pfad.
- ServiceFailureActions: automatische Neustarts konfigurieren, um transienten Startproblemen zu begegnen.
- Bitness & Runtimes: 32-Bit-EXE → 32-Bit-DLLs; Redistributables bereitstellen oder statisch linken.
- Defender/AV-Ausnahmen: App-Ordner kurzzeitig ausschließen, um On-Access-Blockaden auszuschließen.
Minimal-Snippet (Harbour) für dienstsicheren Start:
INIT PROCEDURE EarlyInit()
LOCAL cExe := hb_FNameDir( hb_argv(0) )
IF !Empty(cExe)
FChDir(cExe)
hb_cwdSet(cExe)
ENDIF
hb_MemoWrit("C:\webserver\service.log", Time()+" init ok"+hb_eol(), .T.)
RETURN
Best-Practice Setup (NSSM):
nssm install MeinDienst "C:\webserver\meinservice.exe"
nssm set MeinDienst AppDirectory "C:\webserver"
nssm set MeinDienst AppStdout "C:\webserver\out.log"
nssm set MeinDienst AppStderr "C:\webserver\err.log"
sc config MeinDienst start= delayed-auto
sc config MeinDienst depend= Tcpip/LanmanWorkstation
Wann ist es sicher gefixt? Wenn das Log aus INIT erscheint, Main() schreibt „enter“, und der Dienst binnen 30 s den „running“-Status erreicht – ohne UI-Aufrufe, ohne gemappte Laufwerke, mit eindeutigen Pfaden.
SCM – Service Control Manager; Windows-Komponente zum Starten/Überwachen von Diensten.
NSSM – Non-Sucking Service Manager; Wrapper zum Betreiben beliebiger EXEs als Dienst.
UNC – Universal Naming Convention; Netzwerkpfad wie
\\Server\Freigabe\....WOW64 – Windows-Schicht zum Ausführen von 32-Bit-Apps auf 64-Bit-Windows.
AV – Antiviren-Software; kann Start/Dateizugriffe verzögern oder blockieren.
RTL – Runtime Library; Laufzeitbibliotheken (z. B. VC Redistributables).
Frage: Ich möchte ein Delphi-Programm schreiben, das über das Internet auf ein DataDictionary zugreift (MySQL/MariaDB oder ADS). Brauche ich dafür einen Virtual Server, und gibt es eine schnelle, sichere Lösung für einen Testbetrieb bis Ende Januar?
Antwort (Kurzfassung): Es gibt zwei bewährte Wege. Klassisch geht es mit einem VPS und hart abgesichertem DB-Port. Moderner und für Tests oft schneller ist ein Cloudflare Tunnel mit Zero-Trust und MFA, bei dem kein Port öffentlich ist.
- Variante A – VPS (klassisch): Virtuellen Server mieten (z. B. df.eu/AWS), MySQL/MariaDB oder ADS installieren, Firewall strikt, TLS aktivieren, nur nötige DB-User. Zugriff über Host/IP und Port (MySQL 3306, ADS 6262); Portweiterleitung nur mit strengen Regeln.
- Variante B – Cloudflare Tunnel (empfohlen): Domain bei Cloudflare, Zero-Trust aktivieren, auf Server/PC
cloudflaredinstallieren, TCP-Tunnel auf lokalen DB-Port legen, in Cloudflare Access MFA erzwingen. Client verbindet sich viacloudflared access tcplokal auf127.0.0.1:PORT.
Welche Option wählen? Für schnellen, sicheren Testbetrieb Variante B (Tunnel + MFA). Wenn ohne Zusatztool gearbeitet werden soll, Variante A – aber nur mit konsequenter Härtung (TLS, Firewall, Least-Privilege).
Hinweise: Bei ADS zusätzlich Internet-Rechte im DD und Benutzerkonten freischalten. Exponierte DB-Ports vermeiden; Audit-Logs prüfen; Sessions kurz halten.
DD – „Data Dictionary“; Metadaten-Verzeichnis einer Datenbank mit Rechten und Definitionen.
ADS – Advantage Database Server; dateibasierter DB-Server mit DataDictionary-Konzept.
VPS – Virtueller Privat-Server; gemietete virtuelle Maschine im Rechenzentrum.
AWS – Amazon Web Services; Cloud-Anbieter für Server/Infra.
MFA – Multi-Faktor-Authentifizierung; Login mit zusätzlichem Sicherheitsfaktor.
TLS – Transport Layer Security; Verschlüsselung für Netzwerkverbindungen.
DB – Datenbank; System zur strukturierten Speicherung und Abfrage von Daten.
TCP – Verbindungsorientiertes Transportprotokoll; Basis vieler Netzwerkdienste.
Frage: Beim Parsen einer XML-Datei mit Chilkat.Xml werden Umlaute zerstört (z. B. „Beh lter“/„Beh?lter“ in :Content). Wie erhalte ich korrekte Sonderzeichen?
Antwort (Kurzfassung): In der ActiveX-Variante liefert Chilkat ohne Einstellung ANSI-Strings zurück. Setze loXml:Utf8 := 1 vor dem Laden und sorge für einen korrekten encoding-Header oder lade mit der passenden Quell-Kodierung. Danach optional in die Ziel-Codepage wandeln.
* Minimalfix
loXml := CreateObject('Chilkat.Xml')
loXml:Utf8 := 1 // vor dem Load!
IF ! loXml:LoadXmlFile(::cXmlDateiName)
// Fehlerbehandlung ...
ENDIF
cUtf8 := loXml:Content // korrekt mit Umlauten
cAnsi := cUTF8ToAnsi(cUtf8) // nur falls nötig
- Header prüfen:
<?xml version="1.0" encoding="utf-8"?>(oder korrekte tatsächliche Kodierung). - Nicht-UTF-8 Quelle:
loXml:Utf8 := 1+LoadXmlFile2(::cXmlDateiName, "windows-1252")bzw."iso-8859-1". - Ohne Header: Datei mit
Chilkat.StringBuilder:LoadFile(path, "windows-1252")laden undloXml:LoadSb(). - Entities:
äetc. werden mit obigen Einstellungen automatisch korrekt decodiert.
Status: 2025-10-21 gelöst. Ursache: fehlendes/ignoriertes UTF-8-Flag und/oder falscher XML-Header. Fix: Utf8=1 + korrekter Header bzw. LoadXmlFile2 mit passender Quell-Kodierung.
Hinweis: Immer zuerst interne Repräsentation auf UTF-8 bringen, erst am Randsystem in ANSI/OEM konvertieren.
Kurzfassung
- Was bedeutet 7022? Der B-Baum des Index ist zu tief/unbalanciert (zu viele Levels), meist wegen sehr langer Keys, vieler Inserts/Updates oder korruptem Index.
- Was hilft sofort?
Reindex– am besten online (ADS baut im Hintergrund neu, hängt nach und tauscht am Ende um). - Was verhindert das Wiederkehren? Kompaktere Keys (Padding reduzieren), ggf. größere Index Page Size, regelmäßiges Reindex bei hoher Änderungsrate.
Warum passiert das?
Pro Indexseite passen nur begrenzt viele Einträge (Fan-out ≈ Seitengröße ÷ Schlüsselbreite). Breite Keys wie PADR(UPPER(FIRMA),80) verringern den Fan-out ⇒ mehr Ebenen. Viele Inserts in derselben Sortierregion verursachen zusätzliche Splits; irgendwann erreicht ADS das Level-Limit → 7022.
Online-Reindex (ohne alle Benutzer abzumelden)
-- Verfügbare System-Prozeduren anzeigen
SELECT * FROM system.systemprocedures;
-- Klassisches Reindex (DD-Tabelle)
EXECUTE PROCEDURE sp_Reindex('MeineTabelle');
-- Falls vorhanden in deiner Version: Online-Reindex
EXECUTE PROCEDURE sp_ReindexOnline('MeineTabelle'); -- Signatur in system.systemprocedures prüfen
Hinweis: Online-Reindex legt eine neue Indexdatei an, pflegt Änderungen nach und tauscht am Ende atomar aus.
Offline-Reindex (Wartungsfenster)
- Alle Benutzer raus, Backup der DBF/CDX.
- ARC: Table Maintenance → Reindex oder
sp_Reindex('...'). - Kommt 7022 wieder: Reindex mit größerer Page Size (siehe ADS-Doku zu Index Page Size / AdsIndexPageSize).
Key-Ausdrücke optimieren (Beispiele)
* Schlecht (breit, viel Padding)
INDEX ON PADR(UPPER(FIRMA),80) + DTOS(DATUM) + STR(NR,10) TAG BIG
* Besser (kompakter, gleiche Sortierlogik)
INDEX ON LEFT(RTRIM(UPPER(FIRMA)),30) + DTOS(DATUM) + STR(NR,8) TAG N_FIRMA_DAT_NR
- Datum immer
DTOS()(8 Zeichen). - Zahlen mit
STR(n, festeBreite)möglichst klein halten. - Lange Kombi-Keys in mehrere kurze Tags aufteilen.
Checkliste „7022 beheben“
- Backup erstellen.
SELECT * FROM system.systemprocedures WHERE name LIKE 'sp_Reindex%';– Signatur prüfen.- Online-Reindex ausführen (oder Offline im Fenster).
- Falls nötig: Page Size erhöhen und/oder Keys kürzen.
- Optional: regelmäßiges Reindex einplanen (z. B. monatlich bei hoher Änderungslast).
Feinheiten
- ADS trimmt Keys nicht automatisch:
PADR(...,80)bleibt 80 B lang. - Einzelne Keys werden nie „gesplittet“; gesplittet werden Seiten. Leaf-Seiten sind doppelt verkettet (Range-Scans sind schnell).
Frage: Ein XbpBrowse wird wie ein AChoice genutzt. Nach dem Öffnen dauert die Positionierung; der User tippt bereits und drückt Return. Wegen asynchroner Pfeile via PostAppEvent(xbeP_Keyboard,…) landet der falsche Datensatz. Wie verhindert man das – und wie beschleunigt man das Browse bei großen Fonts/vielen Spalten?
Antwort (Kurzfassung)
- Nie Pfeile posten.
PostAppEvent()konkurriert mit User-Events. Synchron navigieren: DB bewegen, Browse refreshen, dann:down()/:applyKey()ohne Queue. - „Stable-Gate“ setzen. Während der Navigation Eingaben blocken, Event-Queue leeren, erst danach Return zulassen.
- Weniger zeichnen + kleiner Font. Sichtbare Spalten begrenzen, teure Zellen vermeiden, eigenen (kleineren) Font nur im Browse setzen.
Synchron positionieren (robust)
STATIC lNavigating := .F.
PROCEDURE BrowseGotoStable( oBrw, nTargetRec )
LOCAL nE, u1, u2, o
lNavigating := .T.
oBrw:disableUpdate()
IF nTargetRec > 0
dbGoto( nTargetRec ) // oder: ordSeek( cKey )
ENDIF
oBrw:refreshCurrent() // bei großen Sprüngen: :refreshAll()
oBrw:enableUpdate()
SysRefresh()
// Alte gepostete Events verwerfen (keine "Return"-Altlasten)
DO WHILE AppEvent( @nE, @u1, @u2, @o, 0 ) != 0
ENDDO
lNavigating := .F.
RETURN
FUNCTION OnKey( oBrw, nKey )
IF lNavigating
RETURN 0 // Eingaben ignorieren, bis stabil
ENDIF
IF nKey == xbeK_RETURN
RETURN DoAcceptIfStable( oBrw )
ENDIF
RETURN oBrw:applyKey( nKey ) // synchron, kein PostAppEvent()
„Mitte treffen“ ohne Queue-Rennen
/* oBrw = XbpBrowse, nRec = Ziel-RECNO(), nVis = sichtbare Zeilen */
PROCEDURE CenterOn( oBrw, nRec, nVis )
LOCAL nTopRec := Max( 1, nRec - Int( Max(nVis,10) / 2 ) )
LOCAL nDown := nRec - nTopRec
LOCAL nE, u1, u2, o
lNavigating := .T.
oBrw:disableUpdate()
dbGoto( nTopRec )
oBrw:refreshAll()
IF nDown > 0
oBrw:down( nDown ) // synchron bewegen
ENDIF
oBrw:enableUpdate()
SysRefresh()
DO WHILE AppEvent( @nE, @u1, @u2, @o, 0 ) != 0
ENDDO
lNavigating := .F.
RETURN
Performance-Killer & sichere Fixes
- Weniger zeichnen: Nur benötigte Spalten anlegen/anzeigen; breite Texte kürzen; OwnerDraw/Bitmaps sparsam.
- Atomic Paint:
:disableUpdate()→ bewegen →:refreshCurrent()/:refreshAll()→:enableUpdate()→SysRefresh(). - Fonts/DPI: Systemfont nicht ändern. Stattdessen:
Optional monospace (Fixed-Pitch) für messfreie Breiten, falls Layout passt.LOCAL oF := XbpFont():new():create( "Arial", 0, -9 ) // ~9pt oBrw:setFont( oF ) // kleiner, schneller - Große Sprünge vs. kleine Moves: Große Sprünge mit
:refreshAll(), danach fein mit:down(n). Keine Pfeil-Events posten. - Event-Queue unter Kontrolle: Während Navigation Return/Keys ignorieren und nach dem Refresh gepufferte Alt-Events verwerfen.
Quick-Win-Messung
LOCAL t := Seconds()
oBrw:refreshAll()
? "Refresh:", Seconds() - t
Praxis-Checkliste
- Spalten straffen und teure Zellen reduzieren.
- Kleinen eigenen Font via
setFont()nur im Browse setzen. - Navigation kapseln mit
:disableUpdate()…:enableUpdate(). - Synchron positionieren (
dbGoto/ordSeek→:refresh…→:down()). - „Stable-Gate“ (Flag + Queue leeren) vor Akzeptieren von Return.
- Messen (vorher/nachher) und nur wirksame Maßnahmen behalten.
Thread-Update (2025-10-20):
- (Nachtrag #1): Eingaben werden bereits verworfen; Hauptproblem bleibt die Performance bei großen Fonts und langen/breiten Listen.
- Konsequenz: Obige „Weniger zeichnen + kleiner Font + synchrone Navigation“-Kombi priorisieren;
PostAppEvent()vermeiden.
Hinweis: API-Namen wie :down(), :applyKey(), :refreshCurrent()/:refreshAll(), :disableUpdate()/:enableUpdate() sind stabil. Bibliotheks-spezifische „Cache/Autosize/Virtual“-Extras bitte einzeln testen und nur bei messbarem Gewinn übernehmen.
Frage: Kann man aus Xbase++ die Bildschirmauflösung temporär reduzieren (z. B. von 1920×1080), damit die App nicht abgeschnitten wird, und später wieder zurückstellen? Der Kunde möchte die Systemauflösung für andere Programme nicht ändern.
Antwort (Kurzfassung): Nicht die Auflösung ist das Problem, sondern die Windows-DPI-Skalierung (125 %/150 %). „Auflösung pro App ändern“ ist nicht sinnvoll. Es gibt zwei praktikable Wege:
- Sofort & ohne Code: Eigenschaften → Kompatibilität → Hohe DPI-Einstellungen ändern und „Hohe DPI-Skalierung überschreiben“ aktivieren, Skalierung durch: „System (Erweitert)“. Verhindert Abschneiden, ggf. leicht unscharf.
- Sauber & zukunftssicher: App als Per-Monitor-DPI-aware deklarieren und Größen/Fonts am DPI-Faktor ausrichten:
- Manifest:
<dpiAware>true</dpiAware>+<dpiAwareness>PerMonitorV2</dpiAwareness>. - DPI ermitteln (z. B.
GetDpiForWindow()),scale = dpi / 96.0, alle Koordinaten/Schriftgrößen durchscaleteilen. - Bei Monitorwechsel neu skalieren (Per-Monitor-V2).
- Manifest:
Hinweise: Framework-Scaling (z. B. eXpress++) kann das bereits kapseln. PDF/Rendering-Unschärfen treten nur bei der „System (Erweitert)“-Variante auf, nicht bei echter DPI-Awareness.
Status: Stand 2025-10-08. Empfehlung: Kurzfristig Kompatibilitäts-Scaling, mittelfristig Manifest PerMonitorV2 + konsequente DPI-Skalierung in der App.
Optional: Beispiel-Manifest und eine kleine Xbase++/WinAPI-Hilfsroutine zum DPI-Faktor können bei Bedarf ergänzt werden.
Frage: Wie setze ich einen multipart/form-data-Upload wie im cURL-Beispiel um (-F apiKey, -F title, -F description, -F [email protected])? Reicht HttpClient:setAcceptType("multipart/form-data")?
Antwort (Kurzfassung): Nein. Accept betrifft nur die erwartete Antwort. Für den Upload müssen die Formfelder als Body gesetzt und die Datei als eigener Multipart-Part übertragen werden. In Xbase++ erledigst du das mit httpRequest:setParameter() für Textfelder und httpRequest:setFileParameter() (oder äquivalent) für die Datei; der Client erzeugt dann den korrekten multipart/form-data-Body inkl. Boundary automatisch.
- Klarstellung:
-Fin cURL = Form-Parts (Body), nicht Header. AlsosetParameter()/AddParameter()stattSetHeader()fürapiKeyetc. - Datei-Part: Eine dedizierte Datei-API (
setFileParameter("file","hamburg.txt")) ist robuster als Kurzformen; sie setzt Boundary,filenameundContent-Type. - Method/URL:
setMethod("POST")und Ziel-URL in:new(...)oder via Request-Objekt setzen.
Minimalbeispiel Xbase++ (HttpClient)
LOCAL oHC := HttpClient():new("https://url.net/database/saveWithApiKey")
oHC:setMethod("POST")
oHC:httpRequest:setParameter( "apiKey" , "APIKEY" )
oHC:httpRequest:setParameter( "title" , "Hamburg" )
oHC:httpRequest:setParameter( "description", "Meine Komplette Liste" )
// Datei-Part → erzeugt automatisch multipart/form-data inkl. Boundary
oHC:httpRequest:setFileParameter( "file", "hamburg.txt" )
LOCAL cBody := oHC:send()
? oHC:getStatusCode(), oHC:httpResponse:statusText
// ? cBody // optional anzeigen
Praxis-Update (funktionierende Kurzvariante mit @)
oHC := HttpClient():new("https://url.net/database/saveWithApiKey")
oHC:httpRequest:setMethod("POST")
oHC:httpRequest:setContentType("multipart/form-data") // Boundary wird von der Lib ergänzt
oHC:httpRequest:AddParameter("apiKey", "APIKEY")
oHC:httpRequest:AddParameter("file", "@hamburg.txt") // Datei-Part per @-Kurzschreibweise
oHC:httpRequest:AddParameter("title", "Hamburg")
oHC:httpRequest:AddParameter("description", "Meine Komplette Liste")
cResult := oHC:send()
nStatus := oHC:getStatusCode()
- Wenn verfügbar, ist
setFileParameter()robuster als die@-Kurzform (setzt u. a.filenameundContent-Typekorrekt). - Dateipfad absichern:
AddParameter("file", "@" + FExpand("hamburg.txt")). - Antworttyp optional setzen (z. B. JSON):
setAcceptType("application/json").
Manueller multipart-Body (Schema)
--BOUNDARY\r
Content-Disposition: form-data; name="apiKey"\r
\r
APIKEY\r
--BOUNDARY\r
Content-Disposition: form-data; name="title"\r
\r
Hamburg\r
--BOUNDARY\r
Content-Disposition: form-data; name="description"\r
\r
Meine Komplette Liste\r
--BOUNDARY\r
Content-Disposition: form-data; name="file"; filename="hamburg.txt"\r
Content-Type: text/plain\r
\r
<Datei-Binärdaten>\r
--BOUNDARY--\r
Hilfreich: cURL → Code-Generator
Zum schnellen Gegencheck lassen sich cURL-Befehle in Beispielcode (FoxPro, C#, PowerShell, …) umwandeln – praktisch zur Verifikation der benötigten Parts.
Praxis-Update (funktionierende Kurzvariante mit @ – bestätigt)
oHC := HttpClient():new("https://url.net/database/saveWithApiKey")
oHC:httpRequest:setMethod("POST")
oHC:httpRequest:setContentType("multipart/form-data") // Boundary wird von der Lib ergänzt
oHC:httpRequest:AddParameter("apiKey", "APIKEY")
oHC:httpRequest:AddParameter("file", "@" + FExpand("hamburg.txt")) // Datei-Part per @
oHC:httpRequest:AddParameter("title", "Hamburg")
oHC:httpRequest:AddParameter("description", "Meine Komplette Liste")
cResult := oHC:send()
nStatus := oHC:getStatusCode() // z.B. 200
FExpand()vermeidet Probleme mit Arbeitsverzeichnis/relativen Pfaden.- Optional:
setAcceptType("application/json"), wenn JSON erwartet.
Verifikation: Kommt der Dateiinhalt wirklich an?
Kurztest-Endpoint am Server:
<?php
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'fields' => $_POST,
'file' => isset($_FILES['file']) ? [
'name' => $_FILES['file']['name'],
'type' => $_FILES['file']['type'],
'size' => $_FILES['file']['size'],
'sha256' => hash_file('sha256', $_FILES['file']['tmp_name']),
] : null,
], JSON_PRETTY_PRINT);
- Mit obigem Code testweise posten.
- Hash lokal prüfen:
certutil -hashfile hamburg.txt SHA256. - Größe/Hash vergleichen → Inhalt ist identisch angekommen.
Was bedeutet HTTP 200? Was sollte als Antwort kommen?
HTTP-Status 200 „OK“ heißt: Der Server hat die Anfrage angenommen und eine Antwort geliefert. Nicht garantiert ist damit, dass die Aktion fachlich erfolgreich war (z. B. Datei valide gespeichert). Viele APIs signalisieren Business-Erfolg zusätzlich im Body (z. B. JSON).
- 200 OK: Erfolg mit Body (z. B. Metadaten, Message). Business-Fehler können dennoch als 200 mit
{"success":false}kommen – API-spezifisch. - 201 Created: Ressource angelegt. Oft mit
Location-Header und/oder{"id": ...}im Body. - 202 Accepted: Verarbeitung asynchron angenommen; späteren Status pollen.
- 204 No Content: Erfolg ohne Body.
- Typische Fehler: 400/422 (Payload ungültig), 401/403 (Auth/ACL), 409 (Konflikt), 413 (zu groß), 415 (Media Type), 429 (Rate Limit), 5xx (Serverfehler).
Empfohlene Antwort-Inhalte bei Uploads (Beispiele):
{
"success": true,
"id": "f_8a3d",
"fileName": "hamburg.txt",
"size": 12345,
"mime": "text/plain",
"sha256": "ab12...fe90",
"message": "Upload gespeichert"
}
Client-Check (Pattern):
// Erwartetes Format deklarieren (falls JSON):
oHC:httpRequest:setAcceptType("application/json")
cBody := oHC:send()
nStatus := oHC:getStatusCode()
IF nStatus >= 200 .AND. nStatus < 300
// JSON parsen und auf success/id/checksum prüfen (API-spezifisch)
// Zusätzlich: Größe/Hash mit lokaler Datei vergleichen, falls geliefert
ELSE
// Fehlerpfad: nStatus, evtl. Fehlermeldung aus Body/Headers loggen
ENDIF
Praxis: Für „wirklich erfolgreich“ brauchst du entweder ein explizites success:true,
einen eindeutigen Beleg (z. B. id, Location-Header, sha256) oder die bestätigte
Metadaten-Echo-Antwort des Servers. Nur 200 ohne Aussage im Body ist oft zu wenig.
Status: Ergänzt am 2025-10-07 mit „Praxis-Update“ nach erfolgreichem Test der @-Kurzvariante. Empfehlung bleibt: bei Gelegenheit auf Datei-API (setFileParameter) umstellen.
Hinweis: Nur wenn die Library keinen Datei-Part kennt, muss der Multipart-Body samt Boundary manuell gebaut werden.
Frage:
Kann man es so einstellen, dass man mit ENTER nicht nur durch alle Eingabefelder läuft,
sondern nach dem letzten Feld automatisch zu den Buttons springt und danach wieder vorne beim ersten Feld anfängt –
also ein kompletter Fokus-Ring mit ENTER?
Antwort:
Ja, mit DCGETOPTIONS ENTERMOVE wird ENTER wie TAB behandelt.
Damit läuft der Fokus durch alle GETs und Buttons im Tab-Ring und springt am Ende wieder zurück an den Anfang.
Alternativ kann man am letzten GET mit bKeyDown oder bPostValid den Fokus explizit
auf einen Button setzen. Ein minimales Beispiel:
FUNCTION Demo()
LOCAL cName := "", cPhone := ""
LOCAL oDlg, oBtnOk, oBtnCancel, oLast
DEFINE DIALOG oDlg TITLE "Demo"
@ 1, 2 SAY "Name" GET cName
@ 2, 2 SAY "Telefon" GET cPhone OBJECT oLast
@ 4, 2 BUTTON "&OK" OF oDlg ACTION oDlg:End( IDOK ) OBJECT oBtnOk DEFAULT
@ 4,12 BUTTON "&Abbruch" OF oDlg ACTION oDlg:End( IDCANCEL ) OBJECT oBtnCancel
// ENTER wie TAB, Fokus läuft im Ring
DCGETOPTIONS ENTERMOVE
// Letztes GET: ENTER abfangen → Fokus auf OK-Button
oLast:bKeyDown := {|o,n| If( n==VK_RETURN, ( oBtnOk:SetFocus(), 0 ), NIL )}
DCREAD GUI OF oDlg BUTTONS oBtnOk, oBtnCancel
RETURN NIL
Hinweis:
Das Verhalten hängt von den Optionen ab:
ENTEREXITbeendet die Maske nach dem letzten GET.DEFAULT-Buttons fangen ENTER oft sofort ab.ENTERMOVEerzeugt den Fokus-Ring, kann aber je nach TabOrder variieren.
Frage: Bei einem Kunden mit ESET treten beim Einsatz von LoadFromUrl() (Phil) Fehler auf, stets bei dllExecuteCall. Callstack zeigt PCRE_BASE:PCRE_FULLINFO → RegExp():Exec() → ChopUrlIntoLittleBits(). Welche ESET-Einstellung ist zu prüfen?
Antwort (Kurzfassung): Sehr wahrscheinlich kein direkter ESET-Bug, sondern ein Stabilitäts-/Ressourcenproblem im PCRE/RegExp-Pfad (insbesondere, wenn Regex instanz-/threadübergreifend genutzt wird oder der Input Binär-/Sonderzeichen enthält). Workarounds/Optionen:
- Regex vermeiden: URL ohne PCRE parsen (z. B. eigener Parser statt
ChopUrlIntoLittleBits()mit RegExp). - Alternativen nutzen: In Xbase++ 1.90 ASINET-Variante von
LoadFromUrl()(andere Syntax) oder in neueren VersionenHttpClient. - RegExp sicher machen: Keine geteilten
RegExp()-Instanzen zwischen Threads; Eingaben begrenzen/validieren; Fehler mitBEGIN SEQUENCE … RECOVERabfangen. - ESET nur testweise/gezielt: Falls nötig, Prozess/URL in ESET vom HTTPS-/Protokoll-Scan ausnehmen; aber primär Ursache im Code suchen.
Status: Der Thread wurde am 2025-09-30 als erledigt markiert; das Problem trat „ohne nachvollziehbaren Grund“ nicht mehr auf. Es gab parallel auch PDF-Erstellungsprobleme. Vermutung: Ressourcen-/Speicherproblem.
Hinweis: Ein drop-in, regexfreier URL-Parser steht bereit und eliminiert die PCRE-Stelle vollständig.
Frage:
Mir wird angezeigt, dass die Variable GETLIST public sein soll, obwohl ich sie nur als LOCAL deklariert habe. Muss das so oder kann das weg?
Antwort:
GETLIST ist historisch eine PUBLIC-Variable, die von Clipper/FiveWin für die klassische READ-Logik genutzt wird.
Viele Bibliotheken initialisieren sie mit PUBLIC GetList := {}.
Wenn du in einer Funktion ein LOCAL GetList definierst, überschreibt diese lokale Definition die globale.
Der Hinweis erscheint, weil die globale Variante trotzdem existiert.
Praktisch gilt: Entfernen solltest du die PUBLIC GetList nicht, weil Framework-Funktionen sie erwarten.
Verwende in deinem Code entweder einen anderen Variablennamen oder belasse es bei LOCAL GetList – das ist innerhalb der Funktion sicher.
Eine PUBLIC GetList selbst anzulegen ist nicht empfehlenswert.
Weitere von Alaska automatisch angelegte PUBLIC-Variablen:
GetList– Array für aktive GET-Objekte (klassische Eingabelogik).PromptList– Array für PROMPT/CHOICE-Menüs (Menüsteuerung).AppObject– globales Application-Objekt der Runtime.oDesktop– Referenz auf den Desktop (Fenster-Umgebung für GUI-Controls).
Hinweis: Diese Variablen sind immer vorhanden, auch wenn du sie nicht selbst deklarierst. Für sauberen Code empfiehlt es sich, eigene Variablen nicht gleich zu benennen, um Überschneidungen zu vermeiden.
Frage:
Wie wird in Fortras der Empfänger bzw. Entladeort angegeben?
Antwort:
Der Empfänger wird mit dem Block CNE (Consignee) beschrieben.
Darin stehen Name, Straße, PLZ, Ort und Land der Entladestelle.
Falls der tatsächliche Entladeplatz vom Empfänger abweicht, kann zusätzlich ein ADR-Block mit TYP=ULP (Unload Place) oder ein LOC-Segment genutzt werden.
Damit bleibt klar: CNE = Auftragsempfänger, ULP/LOC = konkrete Abladestelle.
Frage:
Beim Start erscheint die Meldung Fatal RTL installation error – unknown linking mode.
Die EXE wurde lokal erstellt, dennoch bricht sie beim Start mit diesem Fehler ab.
Was bedeutet die Meldung und wie kann man das Problem beheben?
Antwort:
Die Fehlermeldung weist auf ein Versions- oder Modus-Mismatch zwischen der EXE und den Runtime-DLLs hin.
Typische Ursachen sind:
- Falsche DLL im Suchpfad: Eine ältere oder inkompatible RTL-DLL (z. B.
XPPRT1.DLL) wird zuerst gefunden. - Bitness-Mismatch: 32-Bit-EXE trifft auf 64-Bit-DLL oder umgekehrt.
- Gemischte Build-Stände: EXE wurde mit neuerem Service Pack erzeugt, DLLs stammen noch aus einer älteren Version.
- Cache-/Projektreste: Alte OBJ/LIB-Dateien im Projektordner können falsche Link-Informationen hinterlassen.
- Fehlende Rechte: In seltenen Fällen verhindert ein Start ohne Administratorrechte, dass die Runtime korrekt initialisiert werden kann.
Schnelle Tipps
- Alle alten OBJ/LIB-Dateien löschen und das Projekt komplett neu bauen.
- Die passenden RTL-DLLs side-by-side zur EXE legen, nichts in
System32oderSysWOW64. - Mit Tools wie „Dependency Walker“ prüfen, welche DLL tatsächlich geladen wird.
- Sicherstellen, dass EXE und RTL-DLL denselben Build-Stand und dieselbe Bitness haben.
- Auf einer sauberen Test-VM nur EXE + passende RTL starten, um Pfadkonflikte auszuschließen.
- Falls weiterhin Fehler auftreten: Programm testweise mit Admin-Rechten starten.
Beispiel: Eine EXE wurde mit aktuellem Service Pack erstellt, findet aber beim Start noch eine ältere RTL-DLL im PATH.
Ergebnis: unknown linking mode. Lösung: DLLs im Programmordner aktualisieren und Projekt neu bauen.
Sollte das Problem bestehen bleiben, hilft oft ein Start mit Admin-Rechten, um Zugriffsprobleme auszuschließen.
Frage:
Eine App läuft einmal mit dpiAwareness im Manifest, einmal ohne – gleicher Code, gleicher Monitor (1920×1080).
Ohne dpiAware passt die App exakt, mit dpiAware bleibt ein Rand. Im Debugger werden andere Größen gemeldet
(z. B. 1920×1032 vs. 1900×1024 gemessen) und bei maximized entstehen sogar Werte größer als der Bildschirm.
Warum stimmt die Größe nicht mehr und wieso zeigt maximized() unterschiedliche Resultate?
Antwort:
Das Verhalten ist normal und hängt mit DPI-Awareness, Fensterrahmen und der Windows-Workarea zusammen:
- DPI-Virtualisierung (ohne dpiAware): Koordinaten werden in 96-DPI-Logik virtualisiert → scheinbar passend, aber nicht echte Pixel.
- Echte Pixel (mit dpiAware): Rahmen, Titelzeile und unsichtbare Resize-Border (~7–8 px) kommen hinzu → sichtbarer Rand.
- Work-Area vs. Monitor-Area: 1920×1080 ist die Gesamtfläche; die Work-Area ist kleiner (Taskleiste abgezogen). Maximized berechnet daraus inkl. Rahmen →
WindowRectkann größer als der Bildschirm wirken. - Messmethoden:
GetWindowRect≠GetClientRect;DwmGetWindowAttributeliefert oft andere Bounds als Screenshots.
Schnelle Tipps
- Manifest auf
PerMonitorV2setzen (<dpiAwareness>PerMonitorV2</dpiAwareness>). - Immer die Monitor-Workarea verwenden (
GetMonitorInfo→rcWork). - Fenstergröße mit
AdjustWindowRectExForDpianpassen, damit der Client exakt die Work-Area füllt. - Kompatibilitäts-Overrides prüfen/löschen (Registry/EXE-Eigenschaften), da Windows den dpiAware-Status pro EXE cachen kann.
Beispiel: 1920×1032 (Client ohne Taskleiste), 1900×1024 (sichtbar ohne unsichtbare Border) sowie 1936×1048/1926×1038 (maximized inkl. Rahmen/DWM-Outsets) sind konsistent und erklärbar.
Kurzfassung: Ein Nutzer berichtet, dass der Login auf einen FTPS-Server klappt, aber Directory leer bleibt. Ursache ist fast immer die Firewall: FTP/FTPS nutzt neben Port 21 (Kontrollkanal) zusätzliche Datenports für Listings/Transfers.
Hintergrund
- Active Mode: Server sendet Daten von Port 20 → oft von Firewalls/NAT geblockt.
- Passive Mode: Server öffnet zufällige hohe Ports (Passiv-Range). Diese Range muss am Server fest definiert und in der Firewall eingehend erlaubt werden.
- Explizites FTPS (AUTH TLS): gleicher Mechanismus, aber TLS verschlüsselt die Steuerbefehle → klassische FTP-ALGs können nicht „mitlesen“. Daher ist eine sauber konfigurierte Passive-Port-Range Pflicht.
Schnelle Lösungen
- Wenn FTP/FTPS bleiben muss:
- Am Server eine feste Passive-Range setzen (z. B.
50000-51000) und diese in der Server-Firewall freigeben. - Client auf Passive Mode stellen.
- Falls vorhanden: FTPS explicit (Port 21) nutzen, nicht „implicit“ (Port 990), und die gleiche Passive-Range öffnen.
- Am Server eine feste Passive-Range setzen (z. B.
- Empfohlen für Updates/Downloads: auf HTTPS (Port 443) umstellen. Ein Port, überall offen, einfacher Betrieb, moderne TLS-Stacks.
Minimaler Wechsel auf HTTPS (Beispiel-Plan)
- Update-Paket +
manifest.jsonper HTTPS bereitstellen (Version, Datei, SHA-256). - Client lädt Manifest, lädt Paket via HTTPS, prüft Hash, installiert.
// manifest.json (Server)
{
"version": "1.2.3",
"file": "app-1.2.3.zip",
"sha256": "64-hex-digits-hier"
}
Praxis-Tipp: Für FTPS passiv immer Server-und Edge-Firewall (und ggf. Cloud-Security-Group) identisch konfigurieren. Für den Umstieg auf HTTPS genügt Port 443 + ein gültiges Zertifikat (z. B. Let's Encrypt).
Kurzfassung: Defender/SmartScreen/Smart App Control hat die ML-Heuristik verschärft. Unsignierte oder frisch veränderte EXE/DLLs werden heuristisch als verdächtig eingestuft und teils sofort quarantänisiert oder gelöscht.
Nachhaltige Lösung
Code-Signing (EXE + eigene DLLs) mit RFC-3161 Timestamp.
signtool sign /fd SHA256 /tr http://timestamp.example.tld /td SHA256 /a path\to\MyApp.exe
signtool verify /pa /all path\to\MyApp.exe
Workarounds (nur testweise)
- Defender-Ausnahme für Datei/Ordner
- Smart App Control zu Diagnosezwecken deaktivieren
- False Positive an Microsoft melden
RunShell() startet EXE nicht?
nError := RunShell(cArgs, "C:\Pfad\MyApp.exe", .f., .f.)
IF nError == 0
Prot("Start ok")
ELSE
Prot("Start fehlgeschlagen (" + DosErrorMessage(nError) + ")")
ENDIF
Runtimes signieren? Nur mit ausdrücklicher Erlaubnis des Rechteinhabers; eigene Artefakte sind ok.
Iterativ vorgehen: Minimales Snippet → Prompt für Tests/Docs/Refactor → kleines Ziel (CLI/CGI) liefern → nächste Stufe. Prompts versionieren, Outputs prüfen, Modellgrenzen beachten.
- „prompts/“ im Repo pflegen
- Diff/Hash prüfen, nicht blind übernehmen
- Kontextgröße & Halluzinationen berücksichtigen
Minimal-Stack: kleines Harbour-CGI/FCGI liefert JSON; statische HTML/JS-Seite rendert Tabelle mit fetch(). Auth zuerst Basic/Bearer, später ausbauen.
Kurzbeschreibung: Ein vierter Benutzer startet das Programm, und plötzlich hängt eine bereits laufende Instanz an einem anderen Arbeitsplatz. Ursache? Meist Netzwerk- oder Locking-Probleme – kein Lizenzthema.
Problem
- In einem Windows-11-Pro Netzwerk (Server als VM/Proxmox) treten sporadisch Hänger auf.
- Wenn mehrere Benutzer die Anwendung starten (z. B. 3 aktiv, der 4. kommt dazu), friert nicht der neue Start, sondern eine bereits laufende Instanz auf einem anderen PC ein.
- Nach Beenden der zusätzlichen Instanz läuft alles wieder normal.
- Keine Fehlermeldung, andere Programme bleiben unbeeinflusst.
Fazit / mögliche Lösungen
- Kein Lizenzproblem, sondern sehr wahrscheinlich SMB-/Locking-Konflikte (Oplocks/Leases) in Kombination mit Windows 11 Pro als „Server“.
- Kurzfristig: EXE/DLL lokal starten (nur Daten im Netz); Opportunistic Locking/Leasing am betroffenen Share deaktivieren; DB-Verzeichnis vom Virenscanner/Indexdienst ausschließen.
- UNC-Pfade: Programme direkt über UNC (
\\server\share\…) zu starten kann die Problematik verschärfen. Besser: lokale Kopie der EXE oder gemapptes Laufwerk verwenden. Bei DBFs ist UNC neutraler, für EXE/DLL aber problematisch. - Netzwerk-Timeouts (Autodisconnect): Standardwert 15 Min kann Handles kappen. Lösung:
oder z. B. 480 Min (8 h). Registry/PowerShell:net config server /autodisconnect:-1New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' ` -Name 'autodisconnect' -PropertyType DWord -Value 0xFFFFFFFF -Force Restart-Service LanmanServer -Force - Langfristig: Einsatz eines Windows Server (2019/2022) oder Umstieg auf AppServer/Terminalserver, um DBF-Sharing-Effekte zu vermeiden.
Beispielausgabe: net config server
Servername \\SERVERTEST
Softwareversion Windows Server 2012 R2 Foundation
Unsichtbarer Server Nein
Max. angemeldete Benutzer 30
Max. offene Dateien pro Sitzung 16384
Sitzungsruhezeit (Min) 15
Erläuterung der Werte
- Max. angemeldete Benutzer: 30 – Limit der Foundation-Edition.
- Max. offene Dateien pro Sitzung: 16384 – ausreichend hoch.
- Sitzungsruhezeit: 15 Min → Autodisconnect. Kann Leerlaufsitzungen trennen und Programme zum Hängen bringen.
Admin-Diagnose: SetCancel, ALT+C & Watchdog
- SetCancel(): nur im Admin-/Dev-Modus
.T., ansonsten.F.. So können Tech-Admins gezielt Ctrl+Break nutzen, normale User nicht. - ALT+C → XPPFATAL.LOG: erzeugt beim Abbruch einen Callstack aller Threads. Auch wenn der Inhalt mager wirkt, ist er oft die erste Spur. Logs beim Programmstart automatisch sichern oder an den Support senden.
- Watchdog-/Hotkey-Ansätze: zyklisches Callstack-Logging oder ein versteckter Admin-Hotkey (z. B. Ctrl-Alt-Ins) können Live-Diagnose ermöglichen (offene Tabellen, geladene DLLs, Calltrace mit LOCALS).
- Errorhandler-Systeme: liefern mehr Infos als XPPFATAL allein (Thread, Parameter, Speicher) und können automatisch Mails/Tickets erzeugen.
Zusammenfassung & empfohlene Schritte
Die Diskussion im Forum zeigte: Ursache ist selten ein Programmfehler, sondern das Zusammenspiel von Netzwerk, SMB-Mechanismen und Indexformat. Entwickler berichteten:
- NTX vs. CDX: Viele NTX-Dateien erhöhen die Anzahl der offenen Handles und SMB-Leases → CDX mit Tags ist stabiler.
- Diagnose: Kombination aus ALT+C/XPPFATAL.LOG, Watchdog-Logs und Errorhandlern ist am effektivsten.
- Praxis: Beim Programmstart prüfen, ob eine
XPPFATAL.LOGexistiert, und ggf. automatisch sichern oder an den Support senden. - Externe Ursachen: Auch Hardware-/Treiberprobleme oder VM-Konfiguration können Hänger verursachen.
Empfohlene Reihenfolge für die Praxis:
- EXE/DLL lokal starten, nur Daten im Netz.
- Autodisconnect deaktivieren oder hochsetzen.
- NTX vermeiden, auf CDX mit Tags umstellen.
- Watchdog + ALT+C/XPPFATAL.LOG nutzen, Logs beim Start sichern.
- Langfristig: Windows Server oder AppServer-Architektur.
Hinweis: Diese technische Zusammenfassung wurde von „docchatty“ (ChatGPT) erstellt und für Clipper2Web neutral aufbereitet.
Frage: Ich habe Paare var1string/var1numerisch, var2string/var2numerisch, var3string/var3numerisch. Mit MIN() finde ich den kleinsten Zahlenwert – wie finde ich den zugehörigen String?
Antwort (3 praxiserprobte Wege)
- Wenig Werte (2–3): IF-Kette → schnellste Runtime
LOCAL nMin := var1numerisch LOCAL cMin := var1string IF var2numerisch < nMin ; nMin := var2numerisch ; cMin := var2string ; ENDIF IF var3numerisch < nMin ; nMin := var3numerisch ; cMin := var3string ; ENDIF ? "Min:", nMin, "→", cMin - Kompakt:
MIN()+IIF()(bei Gleichstand gewinnt der erste Treffer)LOCAL nMin := MIN( MIN(var1numerisch, var2numerisch), var3numerisch ) LOCAL cMin := IIF(nMin == var1numerisch, var1string, ; IIF(nMin == var2numerisch, var2string, var3string)) - Viele Werte: Array + lineare Suche (ohne Sortierung) → O(n), schneller als sortieren
LOCAL aWerte := { ; { var1numerisch, var1string }, ; { var2numerisch, var2string }, ; { var3numerisch, var3string } } LOCAL nMin := aWerte[1,1], cMin := aWerte[1,2], i FOR i := 2 TO LEN(aWerte) IF aWerte[i,1] < nMin nMin := aWerte[i,1] cMin := aWerte[i,2] ENDIF NEXTAlternative ohne FOR:
AEval(aWerte,{|x| nMin:=Min(nMin,x[1]), cMin:=IIF(nMin==x[1],x[2],cMin)})
Hinweise
- Gleichstand (Ties): IF- und
IIF()-Variante liefern den ersten passenden String. Für „alle mit Minimalwert“ einfach beim Durchlauf die Treffer in ein Array sammeln. - Sortierung (
ASort()): Nur nötig, wenn du die Gesamtreihenfolge brauchst. Für „nur Min“ ist die lineare Suche schneller.
Kontext: Zusammenfassung aus einer deutschsprachigen Xbase-Diskussion. Für kleine N → IF; für große N → lineare Suche; für hübsche Darstellung → Sortierung.