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-09-29
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 durchscale
teilen. - 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:
-F
in cURL = Form-Parts (Body), nicht Header. AlsosetParameter()
/AddParameter()
stattSetHeader()
fürapiKey
etc. - Datei-Part: Eine dedizierte Datei-API (
setFileParameter("file","hamburg.txt")
) ist robuster als Kurzformen; sie setzt Boundary,filename
undContent-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.filename
undContent-Type
korrekt). - 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:
ENTEREXIT
beendet die Maske nach dem letzten GET.DEFAULT
-Buttons fangen ENTER oft sofort ab.ENTERMOVE
erzeugt 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 … RECOVER
abfangen. - 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
System32
oderSysWOW64
. - 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 →
WindowRect
kann größer als der Bildschirm wirken. - Messmethoden:
GetWindowRect
≠GetClientRect
;DwmGetWindowAttribute
liefert oft andere Bounds als Screenshots.
Schnelle Tipps
- Manifest auf
PerMonitorV2
setzen (<dpiAwareness>PerMonitorV2</dpiAwareness>
). - Immer die Monitor-Workarea verwenden (
GetMonitorInfo
→rcWork
). - Fenstergröße mit
AdjustWindowRectExForDpi
anpassen, 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.json
per 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:-1
New-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.LOG
existiert, 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 NEXT
Alternative 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.