Windows API


ProgrammierenVista / Windows 7Windows APIMartin Richter - Mo 25 Apr 2011 20:34

Ich habe für ein Projekt Manifeste zur Verwendung von Registration-Free COM Module gebaut.

Diese COM-Module wurden über ein Manifest in einer EXE geladen. Natürlich hatte jedes der COM Module wieder ein eigenes Manifest, die ich entsprechend angepasst habe. Jede COM-Class, die verwendet wird, muss ja in dem Manifest der DLL-Assembly aufgeführt werden.

Eigentümlich war, dass ich nach Änderungen der Manifeste und auch nachdem das COM Modul komplett neu gelinkt wurde, dennoch die EXE ihr Ladeverhalten nicht geändert hat. Manche COM-Klassen wurden nicht gefunden. Sobald ich aber den Rechner neu gestartet hatte funktionierte ab dann alles wie gewünscht und das geänderte Manifest schien nun wirksam zu sein.

Da gibt es einen Cache dachte ich mir. Und nach einiger Recherche im Internet stieß ich auf den folgenden interessanten Artikel von Junfeng Zang:
Windows Vista Sxs Activation Context Cache

Wie man lesen kann, wird bei jedem erfolgreichen Einlesen einer Anwendung und dem erfolgreichen Laden aller Manifeste und DLLs, die ganzen ermittelten Daten in einen Cache gespeichert. Da ich die EXE aber nicht geändert habe, wurden auch die untergeordneten Manifeste nicht neu gelesen, auch wenn diese geändert wurden.

Durch das Ändern des Datums der EXE werden die Cacheeinträge ungültig, und danach werden alle Aktivierungskontexte der Anwednung und aller anderen Assemblies neu geladen.

C++ProgrammierenVista / Windows 7VS 2010Windows APIMartin Richter - Sa 26 Mrz 2011 20:51

Bei der Umstellung eines Projektes von VS-2005 auf VS-2010 lief unsere Software im Testfeld ohne Probleme, bis auf einem Rechner, auf dem gezielt immer wieder der selbe Fehler auftrat.

Der Stackdump zeigte aber immer wieder eine unterschiedliche Absturzadresse. Glücklicherweise konnten wir durch Crash-Dumps einigermaßen lokalisieren was passierte. Eigentümlicherweise zeigte sich der Crash immer beimAufruf einer bestimmten Windows-Prozedur:

Eigentümlicherweise lief das selbe Programm mit VS-2005 kompiliert auf allen Maschinen. Also vermutete ich einen Compilerfehler! Aber wie kann ein Compilerfehler nur auf einer Maschine zu einem Problem werden?

Aber ich lag komplett daneben.
Nach der Analyse des Codes stellte sich heraus das DEP die Ursache war, und das der Crash nur auf der einizgen Maschine auftrat, auf der DEP unterstützt wurde und eingeschaltet war.

Die anderen Rechner im Testfeld unterstützen DEP nicht:

Und auf diesen trat der Fehler nicht auf.

Weitere Analyse zeigte, dass ein spezieller Code aus einer Fensterklasse, die ich von einer Libary übernommen hatte ein spezielles Windows Subclassing mit einem Thunking ähnlich wie ATL machte, jedoch wurde der Speicher nicht korrekt als ausführbar markierte.
Folgerichtig krachte es. D.h. die paar Codezeilen, die gerade mal einen JMP und das laden eines Registers durchführten wurden von DEP als illegal betrachtet und es kam zum Crash.

Und das eigentümliche, dass der Code mit VS-2005 kompiliert lief, war auch schnell erklärt.
Als das Projekt von VS-2005 in VS-2010 übernommen wurde, wurden auch die Optionen für DEP (/NXCOMPAT) und auch ASLR (/DYNAMICBASE) eingeschaltet ❗

Das ist eigentlich nicht nett, denn es hat schon einige weitreichende Konsequenzen für die Software.
Also aufgepast bei der Konvertierung von Programmen und genau darüber nachgedacht ob man DEP und ASLR wirklich für seine Software will ❗

ProgrammierenSoftwareVistaVista / Windows 7Windows 7Windows APIMartin Richter - Fr 21 Jan 2011 20:05

Es waren einmal die Zeiten in denen man in C:\WINDOWS\TEMP einfach mal eine Datei anlegen konnte und jeder darauf zugreifen konnte.
Seit Windows Vista hat sich ja einiges getan was Sicherheit betrifft, besonders auch die Rechtevergabe auf Dateien, die im C:\Windows\Temp Ordner angelegt werden.

Ein Programm  im Rentenalter (es ist gerade mal so um die 16 Jahre alt 😉 geschätzt) erzeugte in C:\WINDOWS\TEMP eine Datei, mit der bestimmte Zugriffe abgesichert wurden. Darunter auch wenn mehrere Instanzen des Programms auf einem Rechner liefen. Das funktionierte prima. Die Datei wurde unter einem festen Namen angelegt und nicht entfernt, nachdem das Programm beendet wurde.

Seit Vista gibt es aber nun ein kleines Problem.:
Seit Windows Vista darf in C:\WINDOWS\TEMP immer noch jeder User Dateien erzeugen. Auf diese Dateien hat er auch vollen Zugriff. Aber auf diese Dateien hat kein anderer Nutzer mehr Zugriff… 😮 … und selbst ein Admin muss erst hier erst den Besitz übernehmen, wenn er die Datei nicht erzeugt hat.

Und nun hat diese kleine alte Programm den folgenden Effekt:

  • Benutzer A meldet sich an.
  • Benutzer A startet das Programm Er arbeitet damit und die temporäre Datei wird in C:\WINDOWS\TEMP angelegt.
  • Benutzer A meldet sich ab und beendet das Programm.
  • Benutzer B meldet sich an.
  • Benutzer B startet das Programm und … bekommt eine Fehlermeldung mit einem „Access denied!“.

Mit den neuen Rechten, die auf dem C:\WINDOWS\TEMP Verzeichnis liegen, kann der zweite Benutzer auf diese Datei nicht mehr zugreifen auf die eben nur der Erzeuger Zugriff hat, und der ist eben Benutzer A.

PS: Es Frage mich keiner warum C:\WINDOWS\TEMP aus GetWindowsDir und angehängtem Text TEMP zusammengesetzt wurde. Vermutlich um zu umgehen, dass ein privates temporäres Verzeichnis benutzt wird. Tja und CSIDL_APPDATA war dem damaligen Entwickler (evtl. noch unter Windows 3.1) nicht bekannt.

C++MFCProgrammierenWindows APIMartin Richter - Sa 04 Dez 2010 17:46

Wenn man eine Windowsnachricht bearbeitet dann ruft man in den meisten Fällen die Funktion der Elternklasse auf. Was aber, wenn man die Nachricht nicht nur entgegennehmen will, sondern sie „verändert“ an die Basisfunktion weitergeben will. Was dann?
Kein Problem denkt man. Man ändert die Parameter eben und gibt diese neuen Werte an die Basisklasse weiter.

Nehmen wir mal als Beispiel ein Eingabe Control, das sich möglichst intelligent verhalten soll. D.h. in diesem Fall, wenn ein Datum eingegeben wird, dann sollte auch bei der Eingabe auf numerischen Ziffernbock, das Komma in einen Punkt gewandelt werden, oder bei englischem Datumsformat in einen Slash…

Na OK. Man überschreibt als CMyWnd::OnChar. Man prüft ob ein bestimmtes Zeichen ankommt und wenn es mir passt, dann ändere ich den Parameter und gebe diesen dann an die Basis Funktion weiter.

:Eeek: aber was ist das? Der Code hat keine Wirkung? Egal was wir an die Basisfunktion übergeben, es ändert sich nichts. Warum?

Die Antwort liegt in dem für mich etwas eigentümlichen Design der MFC. Alle CWnd Nachrichten Routinen rufen letzten Endes immer genau ein und die selbe Funktion auf: CWnd::Default(); Aber was macht CWnd::Default? Es nimmt die ursprünglichen Parameter, die Windows mal gesendet hat und übergibt die an die Window-Proc des Fensters. D.h. alle tollen Manipulationen an der WM_CHAR Nachricht sind weg in dem Moment in dem CWnd::OnChar aufgerufen wird.

Was also tun, wenn man nun aber wirklich die WM_CHAR Nachricht manipulieren will?
Eigentlich nicht schwer. Man macht das Gleiche, dass eben auch CWnd::Default macht. Man ruft DefWindowProc mit den passenden wParam und lParam Werten auf.

Aber jetzt wird es dumm ❗ Damit umgehen wir alle anderen geerbten Funktionalitäten des Controls. Ich habe keine Ahnung was die Entwickler der MFC in der Anfangszeit dazu getrieben hat immer CWnd::Default aufzurufen? War es Ihnen zu kompliziert wieder aus den Parametern wParam und lParam zusammen zu bauen?

CommunityProgrammierenWindows APIMartin Richter - Fr 26 Nov 2010 22:22

Vor Jahren habe ich für die microsoft.public.de.vc FAQ den folgenden Beitrag geschrieben:
Warum klappt meine ComboBox im DropDown-Stil in einem Dialog nicht auf?

Beim Erstellen einer ComboBox in einem Dialog Template muss auch die Größe mit angegeben werden, die die ComboBox haben soll, wenn Sie denn aufgeklappt wird. Dies kann auf zwei Methoden geschehen.

Methode 1: ComboBox aus der Werkzeugleiste einfach durch einen Mausklick einsetzen. Anschließend auf den „DropDown“-Schalter klicken und nun die gewünschte Größe einstellen.

Methode 2: ComboBox durch Ziehen eines Rechteckes auf dem Dialog einsetzen. In diesem Fall wird die Größe gleich korrekt bestimmt. Nachträgliche Änderung der Größe erfolgt dann wieder durch anklicken des „DropDown“-Schalters.

Anmerkung: Die Größe einer ComboBox mit dem Stil CBS_DROPDOWN und CBS_DROPDOWNLIST im NICHT aufgeklappten Zustand kann beim Erzeugen nicht verändert werden. Diese Größe bestimmt Windows automatisch. Die Größe die bei CreateWindow/CreateWindowEx angegeben wird ist immer die Größe des Control im aufgeklappten Zustand. Nachdem ein gültiger Windowshandle auf die ComboBox existiert kann mit CComboBox::SetItemHeight die Höhe der Items bzw. des Editfelds der ComboBox verändert werden.

Jetzt habe ich entdeckt, dass dieser Beitrag eigentlich überflüssig geworden ist, seit dem es COMCTL32 in der Version 6.0 gibt.
Wenn ein Manifest für die 6.0 Version der Common Controls vorhanden ist, dann bestimmt die COMCTL32 DLL automatisch selbst anhand der Höhe des Monitors und der Position der ComboBox wie groß der DropDown-Bereich sein kann.

Aufgefallen ist mir das, als ich in einer RC-Datei sah, dass eine ComboBox mit der Höhe gerade einmal 20 DLUs angegeben wurde. Da in der RC Datei normalerweise immer nur die DropDown-Höhe eingetragen ist, fragte ich mich warum bisher niemandem aufgefallen war, dass diese ComboBox, nicht aufklappt. Ein kurzer Test, zeigte allerdings, dass alles normal war und die Box, den halben Monitor in der Höhe einnahm.
Ein weiter Test mit und ohne Manifest zeigte mir dann schnell, dass sich das Standardverhalten von Comboboxen offensichtlich verändert hat.

Nachtrag (07.01.2011):
Nur die neuen Common Controls ab Vista und Windows 7 verhalten sich wie oben beschrieben. Windows XP (einschließlich SP3) verhält sich noch gemäß der MSDN WinAPI Doku. D.h. die Höhe wird nicht automatisch angepasst. Man kann sich eben auf nichts verlassen.

C++MFCProgrammierenVS 2010Windows APIMartin Richter - Sa 06 Nov 2010 20:32

Wer Double Buffering benötigt und die MFC nutzt, der kennt auch CMemDC. Vermutlich eine der meist genutzten und kopierten Klassen, die auf CodeProject und CodeGuru vorgestellt wurden.
http://www.codeproject.com/KB/GDI/flickerfree.aspx
http://www.codeguru.com/cpp/misc/misc/flickerfreedrawing/article.php/c389/Flicker-free-drawing-using-memory-DC.htm

Ich habe meine Erweiterung hier im Blog vorgestellt und die liegt normalerweise in einem separaten Namespace, wie alle meine Tool-Klassen.

Nicht schlecht staunte ich, als ich keinen Compilerfehler bekam obwohl ich CMemDC nutzte aber keinen Namespace angab. Siehe da: CMemDC hat in einer eigenen Implementierung den Weg in die MFC gefunden. Man findet sie in:
C:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxcontrolbarutil.h

Im Großen und Ganzen ist es die bekannte Standard-Implementierung, allerdings verfügt diese CMemDC Version auch Code, der auf Windows Vista und Windows 7, die fest im Betriebssystem verankerten Funktionen nutzt: BeginBufferedPaint, EndBufferedPaint
Siehe http://msdn.microsoft.com/en-us/library/bb773178(VS.85).aspx
Diese Funktionen werden innerhalb des Themeings von Windows Vista und Windows 7 verwendet und in dieser Funktionsgruppe ist auch Alphablending direkt verankert. (BufferedPaintSetAlpha). Ich vermute sogar, dass diese integrierten Klassen effektiver arbeiten, als die eigenen Klassen (ein Test steht noch aus), denn Windows weiß intern natürlich viel besser was wie zu puffern und zu zeichnen ist, als wir, wenn WM_PAINT aufgerufen wird.

Vielleicht ein guter Grund, die eigene CMemDC Klasse auch auf Vista/Windows 7 Funktionen zu erweitern oder die integrierte Klasse in der MFC zu verwenden.

Tipp: Übrigens hat die MFC CMemDC Klasse einen statischen Member, der es auf einfache Weise erlaubt das Double-Buffering abzuschalten (CMemDC:: m_bUseMemoryDC), dass ist besonders interessant beim Debuggen von grafischen Operationen, deren Ergebnisse man auch gleich sehen will, allerdings wird dieser Member nicht benutzt wenn das interne Windows Double-Buffering genutzt werden kann, schade eigentlich.

PS: Aber eigentlich muss man sich auch die Frage stellen, warum die Entwickler genau diesen Klassennamen verwendet haben, denn er provoziert ja auch direkt den Konflikt mit existierendem Code.

ProgrammierenSoftwareVistaVista / Windows 7WindowsWindows 7Windows APIMartin Richter - Do 16 Sep 2010 20:03

Jeder kennt von Windows Vista und Windows 7 die Junctions „C:\Programme“ oder „C:\Dokumente und Einstellungen“.
Diese Verweise erlauben es auch älteren Programmen die evtl. hardcodierte Verzeichnisnamen haben oder auch Programmen, die die alten Verzeichnisstrukturen von Windows 2000 und XP nutzen korrekt zu arbeiten.

Jetzt könnte man meinen, dass es vollkommen egal ist ob man nun „C:\Programme“ oder „C:\Program Files“ benutzt.
Aber das ist es nicht ❗ Es gibt ein paar ganz feine Unterschiede.

Ein simpler DIR auf der Befehlszeile

C:\>dir C:\Programme
 Datenträger in Laufwerk C: ist C-LAPTOP
 Volumeseriennummer: D483-432C

 Verzeichnis von C:\Programme

Datei nicht gefunden

macht das Problem offensichtlich!
Und was geht hier ab?

Die Antwort liefert ICACLS für die Junction:

C:\>icacls C:\Programme
C:\Programme Jeder:(DENY)(S,RD)
             Jeder:(RX)
             NT-AUTORITÄT\SYSTEM:(F)
             ORDEFINIERT\Administratoren:(F)

Dagegen zeigt ICACLS für das Verzeichnis selbst

C:\>icacls "C:\Program Files"
C:\Program Files NT SERVICE\TrustedInstaller:(F)
                 NT SERVICE\TrustedInstaller:(CI)(IO)(F)
                 NT-AUTORITÄT\SYSTEM:(M)
                 NT-AUTORITÄT\SYSTEM:(OI)(CI)(IO)(F)
                 VORDEFINIERT\Administratoren:(M)
                 VORDEFINIERT\Administratoren:(OI)(CI)(IO)(F)
                 VORDEFINIERT\Benutzer:(RX)
                 VORDEFINIERT\Benutzer:(OI)(CI)(IO)(GR,GE)
                 ERSTELLER-BESITZER:(OI)(CI)(IO)(F)

Während man auf „C:\Program Files“ die Rechte sieht, die man auch erwartet, liegt auf der Junction selbst eine Einschränkung:
Die Berechtigung zum „Ordner Auflisten“ wird verweigert für „Jeder“ ❗
Jeder der neu eingeführten Junctions hat diese Einschränkung.

Meines Erachtens wurde dies gemacht um beim Durchsuchen von Verzeichnisstrukturen nicht unendliche Rekursionen zu erzeugen oder Verzeichnisse oder Dateien doppelt aufzuführen.
Eine Verzeichnis Rekursion ergäbe sich zum Beispiel durch die Junction Anwendungsdaten im Verzeichnis C:\Users\Username\AppData\Local, der exakt wieder auf das Verzeichnis C:\Users\Username\AppData\Local verweist.

C++MFCProgrammierenVista / Windows 7VS 2010Windows APIMartin Richter - Sa 28 Aug 2010 10:36

In der MFC 10.0 hat sich ein Bug eingeschlichen, der sich unter Windows Vista und Windows 7  bemerkbar macht. Unter Windows XP tritt der Fehler nicht auf. Das Problem tritt in jedem Stil auf, der DWM verwendet. D.h. nicht wenn Windows klassisch ausgewählt wird.

Wenn auf einen CFormView mehrere Buttons liegen und der CFormView gerollt wird, dann kommt es unter Umständen zu Fehlern beim Neuzeichnen von Buttons. Dies schließt alle Button-Formen ein: Check-Buttons, Radio-Buttons und normale Buttons.

Das Ganze sieht nach dem Rollen in etwa so aus:

Der Text einiger Buttons erscheint nach dem Rollen als schwarze Blöcke. Es kann auch vorkommen, dass nur Teile der Buttons falsch gezeichnet werden.

Um das Problem gezielt nachzuvollziehen habe ich ein kleines Sample gebaut. Man kann durch zwei Schalter den CScrollView gezielt nach oben oder unten Rollen. Beim Rollen nach unten und bei bestimmten Fenstergrößen tritt dann der Fehler auf. Ich habe das Main-Window entsprechend beim Start in der Größe angepasst.

Das Problem liegt in einer Implementierung von WM_PRINTCLIENT in CScrollView (CScrollView::OnPrintClient), die ein Double-Buffering verwendet, dass entweder falsch ist oder sich eben mit der Standardimplementierung eines Dialoges beißt. Auf den ersten und zweiten Blick konnte ich in der Implementierung selbst keinen Fehler sehen. Deshalb vermute ich, dass sich dieses Double-Buffering mit dem auch vorhandenen Double-Buffering in den Standardimplementierungen der Dialogklasse beißt bzw. nicht korrekt berücksichtigt, dass auch Child-Windows neu gezeichnet werden müssen.

Die Lösung ist entsprechend einfach:

  • Man fügt einfach einen Handler für WM_PRINTCLIENT in seiner von CFormView abgeleiteten Klasse ein.
  • Dieser Handler ruft dann nicht die Implementierung der Basisklasse CFormView auf, sondern die Implementierung in CView (CView::OnPrintClient).
...
ON_MESSAGE(WM_PRINTCLIENT,&CScrollDialogMFCView::OnPrintClient)
...

LRESULT CScrollDialogMFCView::OnPrintClient( WPARAM wp, LPARAM lp )
{
  // Bypass the CScrollView::OnPrintClient implementation
  return CView::OnPrintClient(wp,lp);
}

Dieser Fehler ist in keiner der vorhergehenden MFC Versionen vorhanden (auch nicht in MFCNext), weil einfach kein entsprechender Handler vorhanden war..

Es stellt sich wohl bei einigen jetzt die Frage warum dieser Handler eingebaut wurde. Auch die Antwort ist einfach:
Seit Windows Vista wird stark von AnimateWindow Gebrauch gemacht und auch DWM intern scheint des öfteren WM_PRINT/WM_PRINTCLIENT zu verwenden. Entsprechend haben fast alle Klassen in der MFC entsprechende Handler ergänzt bekommen.

Das Sample kann man hier herunterladen: ScrollDialogMFC – VS-2010.
Ich habe in der stdafx.h einen define FIX_CSCROLLVIEW_PROBLEM eingebaut mit dem man den Fix einfach aktivieren und deaktivieren kann.

C++DebuggingProgrammierenVista / Windows 7Windows APIMartin Richter - Mi 23 Jun 2010 20:08

Manchmal muss man Software verstehen. D.h. auch andere Software, die man selbst nicht geschrieben hat 😉

In meinem Fall war es hier ein Client, den ich geschrieben habe, der eine andere Software startet. Diese Software verwendete interne Nachrichten zur Kommunikation, die mit RegisterWindowMessage registriert wurden. Ich wollte nun hier einen Eingriff machen, der ein Fehlverhalten unter Windows 7 und Vista vermeiden soll.

Hilfreich wäre für mich nun gewesen an den Namen der registrierten Nachrichten zu kommen. Spy++ kann es auch und der importiert auch keine mystischen Funktionen. Also muss es einfach gehen.

Und ein wenig Recherche und ein Verweis eines Community Eintrags brachte mich auf diesen Thread:
http://groups.google.it/group/microsoft.public.vc.mfc/browse_thread/thread/f83f7c12c80e4ada/460bc4c43a844a37

Siehe da GetClipboardFormatName löst das Problem. Der nachfolgende Code lieferte mir nun im Detail, was das so hin und her läuft und der Name der Nachrichten war zum Glück sprechend. Ich konnte das Problem lösen.

if (uiMsg>=0xC000)
{
  TCHAR szName[MAX_PATH];
  ::GetClipboardFormatName(uiMsg,szName,MfxCountOf(szName));
  TCHAR szOut[MAX_PATH*2];
  _stprintf(szOut,_T(__FUNCTION__) _T(" %s, wp=0x%08x, lp=0x%08x\n"),
            szName, wParam, lParam);
  OutputDebugString(szOut);
}
C++MFCProgrammierenWindows APIMartin Richter - Fr 18 Jun 2010 19:58

Wer eine dialogbasierende Anwendung mal mit etwas mehr Aufmerksamkeit debuggt oder analysiert wird feststellen, dass der Returncode der Anwendung irgendwie ziemlich zufällig ist. Beobachtet man dies genauer dann stellt man folgendes fest:

  • Beendet man die Anwendung mit der Maus (Klick auf X) oder OK/Cancel so ist der Returncode 0
  • Hält man die Strg-Taste beim Klick fest ist der Returncode 8
  • Beendet man die Anwendung mit Alt+F4 bekommen wir 2.
  • Und jedermann kann jetzt schon mal raten was passiert, wenn wir die Umschalttaste festhalten. Genau dann bekommen wir 4 als Returncode.

Die Mystik hinter dem Ganzen ist die Behandlung von (Afx)PostQuitMessage. Eigentlich sollte mit dieser Nachricht auch der Exitcode gesetzt werden, der mit WM_QUIT versendet wird. Und wenn eben bei einer MDI/SDI Anwendung alles normal läuft, dann ist diese Nachricht die letzte, die aus der Messsagequeue gezogen wird. Und was passiert in CWinApp::ExitInstance? Genau… aus dem statischen Thread Puffer für die Windowsnachrichten wird mit  AfxGetCurrentMessage die letzte Windowsnachricht (normalerweise WM_QUIT) geholt und der wParam Wert bestimmt. Dieser wird dann zurückgegeben.

Leider ist aber WM_QUIT in manchen Fällen aber nicht die letzte Nachricht, die zum Beenden eines Programms führt. Ganz besonders eben nicht bei einer dialogbasierenden MFC-Anwendung. Da ist die letzte Nachricht ist dann eben ein WM_COMMAND oder ein WM_LBUTTONUP der das Schließen der Anwendung auslöst ❗ Und der wParam Wert ist eben entsprechend dieser Nachricht belegt!

Gleiches passiert natürlich, wenn man nach dem Beenden der Messageloop noch andere interne Fenster zerstört. Auch in diesem Fall kann noch mal die interne AfxWndProc durchlaufen werden und dann wird der Returncode auch wieder verändert.

Wer also wirklich Wert auf den Returncode legt (im wahrsten Sinne des Wortes), der sollte sich nie auf den Wert verlassen, der durch CWinApp::ExitInstance zurückgegeben wird oder den Wert, den man selbst mit AfxPostQuitMessage evtl. versucht zu setzen. Eine Variable in CWinApp tut hier einen besseren Dienst. Ebenfalls sollte man ExitInstance überschreiben und immer 0 zurückgeben, wenn man sowieso keine Verwendung für den Returncode des Prozesses hat oder haben möchte.

BTW:
Die Geschichte, wie ich darauf gekommen bin ist schon eigentümlich genug.
Ich habe komplexere Batch-Dateien, die die gesamte Erstellung einer produktiv-Version regelt. Darin kommen im Problemfall auch ein paar Userinteraktionen vor. Diese werden durch Windows Anwendungen ausgelöst, die evtl. einen Dialog anzeigen. Jedem ist klar, dass ohne Dialog und ohne Fenster keine Nachricht abgearbeitet wird. Der Returncode ist also 0! Der Batch verwendet 4NT Syntax und dort kann man ON ERROR definieren und somit sofort eine Fehlerbehandlung auslösen, wenn der Returncode eines Programms nicht 0 ist.
Nun kann sich jeder schon denken was passiert ist. Die Userinteraktion wurde ausgelöst. Der Benutzer machte eine Angabe und… der Batch terminierte erstaunlicherweise mit einem Fehler… (s.o.)

« Vorhergehende SeiteNächste Seite »