Laden...

Forenbeiträge von retnug Ingesamt 122 Beiträge

30.11.2012 - 11:55 Uhr

@dboe: Genau das ist es! Wenn ich beim ServiceHost (der Windows-Dienst) die Einstellung ändere, dann funktioniert es.

Vielen Dank!

23.11.2012 - 09:07 Uhr

Läuft das Projekt im IIS ?

Nein.

und die Clients verlieren die Verbindung...

Diejenigen Clients, die sich nicht mit dem Service verbinden können, haben ja noch keine Verbindung. Und bei den Clients, die bereits verbunden sind, ist alles OK.

21.11.2012 - 12:58 Uhr

Hallo,

ich habe eine WCF-3.5-Anwendung, die an sich prima funktionert. Ein Service, der als lokaler Dienst gehostet wird, führt periodisch Datenbankabfragen durch und transferiert Daten an angemeldete Clients. Die Clients werden dabei in einer Collection von BenutzerSession-Objekten gehalten:

BenutzerSession GetNewBenutzerSession(string hostBenutzer, string clientBenutzer, string rechnerName, string clientID)
{
	BenutzerSession result;
	{
		OperationContext currentOperationContext = OperationContext.Current;
		ICallbackContract callbackContract = currentOperationContext.GetCallbackChannel<ICallbackContract>();
		result = new BenutzerSession(hostBenutzer, clientBenutzer, rechnerName, clientID, callbackContract);
	}

	return result;
}

In ICallbackContract sind die Aufrufe enthalten, die der Client zum Service sendet. U. a. die Anmeldung am Service per ClientStart().

BenutzerSession dient im Wesentlichen nur zur Haltung von Anmeldedaten. Wichtig ist hier eine Instanz von ICallbackContract. Dieses Interface definiert die Aufrufe, die der Service zum Client sendet.

Wenn es für einen bestimmten angemeldeten Benutzer etwas zu tun gibt, dann wird dessen Client-Instanz über die Variable benutzerSession ermittelt und danach

ICallbackContract callbackContract = benutzerSession.CallbackContract;
callbackContract.ShowMessage("Bla");

aufgerufen.

Bisher dienten die Erläuterungen nur zur Demonstration und haben nichts mit meinem Problem zu tun.

Das ganze klappt normalerweise prächtig: Frühmorgens wird der Dienst gestartet und im Laufe des Tages melden sich die Benutzer an und erhalten eventuell auch ihre eigenen Daten.

Das Problem ist aber folgendes: Wenn der Service bereits einige Stunden läuft, und diverse Clients sich dort angemeldet (per ClientStart in der Proxy-Klasse) haben und bedient wurden, dann hängen die Clients, die sich später am Tag anmelden wollen beim Aufruf von ClientStart in der Proxy-Klasse. Es gibt auch keinen Timeout, sondern es passiert einfach gar nichts mehr.

Ich habe das Projekt abgespeckt hier angehängt. Über Lösungsmöglichkeiten würde ich mich sehr freuen.

Gruß retnug

17.10.2012 - 10:28 Uhr

Sorry - wusste nicht, dass du das per ChannelFactory<T> machst 🙂...

Anscheinend...! 🤔

Demnach musst du aber am Client dementsprechend auch die Limits einstellen (Analog zum Service nur unter dem Knoten Client im Configuration Tool)

Ja.

16.10.2012 - 14:41 Uhr

Danke für die Erleuchtung! Service-References nutze ich aber nicht, ich habe alles was ich brauche (hoffentlich) in den app.config-Dateien.

16.10.2012 - 13:07 Uhr

Ich denke, ich habe den Fehler gefunden: Da der Service von einem Windows-Dienst gehostet wird, dort aber in dessen app.config keine Angaben zum dataContractSerializer zu finden waren, habe ich dies nun ergänzt. Es scheint zu funktionieren.

und eventuell die Referenz am Client updaten. ? Was ist damit gemeint?

15.10.2012 - 12:24 Uhr

Leider kein Fortschritt! Es kommt immer noch der selbe Fehler.

und eventuell die Referenz am Client updaten. ? Was ist damit gemeint?

10.10.2012 - 13:34 Uhr

Danke, aber bei welcher Konfiguration muss ich das ergänzen? Ich hatte das im Vorfeld schon bei Client und Service probiert, indem ich bei beiden den Behavior hinzugefügt hab. Aber der Fehler kommt immer noch:

Client:

<configuration>
	<system.serviceModel>
	         <behaviors>
			<endpointBehaviors>
				<behavior name="ObjectGraphBehavior">
					<dataContractSerializer maxItemsInObjectGraph="2147483647" />
				</behavior>
			</endpointBehaviors>
		</behaviors>
		<bindings>
			<netTcpBinding>
				<binding name="NetTcpBinding_ITestService" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">                    
					<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />                       
					<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />                       
					<security mode="Transport">
						<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
						<message clientCredentialType="Windows" />
					</security>
				</binding>
			</netTcpBinding>
		</bindings>
		<client>
			<endpoint address="net.tcp://localhost:57650/TestService"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ITestService" contract="ITestService" name="NetTcpBinding_ITestService">
				<identity>
					<servicePrincipalName value="TestService" />
				</identity>
			</endpoint>
		</client>
	</system.serviceModel>
</configuration>

Service:

<configuration>
	<system.serviceModel>
		<behaviors>
			<serviceBehaviors>
				<behavior name="TestServiceMeta">
					<serviceMetadata httpGetEnabled="true" />
				</behavior>
				<behavior name="ObjectGraphBehavior">
					<dataContractSerializer maxItemsInObjectGraph="2147483647" />
				</behavior>
			</serviceBehaviors>
		</behaviors>
		<bindings>
			<wsDualHttpBinding>
				<binding name="wsDualBinding" />
			</wsDualHttpBinding>
		</bindings>
		<services>
			<service behaviorConfiguration="TestServiceMeta" name="my.TestService.TestService">
				<endpoint address="TestService" binding="wsDualHttpBinding" bindingConfiguration="wsDualBinding" name="TestServiceEndpoint" contract="my.TestService.ITestService" />    
				<host>
					<baseAddresses>
						<add baseAddress="http://localhost:57650" />
					</baseAddresses>
					<timeouts closeTimeout="00:02:00" openTimeout="00:02:00" />
				</host>
			</service>
		</services>
	</system.serviceModel>
</configuration>

Wie ist die genaue Vorgehensweise?

10.10.2012 - 12:59 Uhr

Hallo, ich habe ein WCF-3.5-Projekt, das auch prinzipiell funktioniert: Eine Service-Klasse, die von einem Windows-Dienst gehostet wird und einen (d. h. viele) Clients, die über Proxy-Klassen mittels wsDualHttpBinding mit dem Service verbunden sind.

Hier meine Konfigurationen:

Service:

<configuration>
	<system.serviceModel>
		<behaviors>
			<serviceBehaviors>
				<behavior name="TestServiceMeta">
					<serviceMetadata httpGetEnabled="true" />
				</behavior>
			</serviceBehaviors>
		</behaviors>
		<bindings>
			<wsDualHttpBinding>
				<binding name="wsDualBinding" />
			</wsDualHttpBinding>
		</bindings>
		<services>
			<service behaviorConfiguration="TestServiceMeta" name="my.TestService.TestService">
				<endpoint address="TestService" binding="wsDualHttpBinding" bindingConfiguration="wsDualBinding" name="TestServiceEndpoint" contract="my.TestService.ITestService" />    
				<host>
					<baseAddresses>
						<add baseAddress="http://localhost:57650" />
					</baseAddresses>
					<timeouts closeTimeout="00:02:00" openTimeout="00:02:00" />
				</host>
			</service>
		</services>
	</system.serviceModel>
</configuration>[/pre]


Servicehost (Dienst):[pre]<configuration>
	<system.serviceModel>
		<behaviors>
			<serviceBehaviors>
				<behavior name="TestServiceMeta">
					<serviceMetadata />
				</behavior>
			</serviceBehaviors>
		</behaviors>
		<services>
			<service behaviorConfiguration="TestServiceMeta" name="my.TestService.TestService">
				<endpoint address="TestService" binding="netTcpBinding" contract="my.TestService.ITestService" />
				<endpoint address="Mex" binding="mexTcpBinding" contract="IMetadataExchange" />
				<host>
					<baseAddresses>
						<add baseAddress="net.tcp://localhost:57650" />
					</baseAddresses>
				</host>
			</service>
		</services>
	</system.serviceModel>
</configuration>

Client:

<configuration>
	<system.serviceModel>
		<bindings>
			<netTcpBinding>
				<binding name="NetTcpBinding_ITestService" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">                    
					<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />                       
					<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />                       
					<security mode="Transport">
						<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
						<message clientCredentialType="Windows" />
					</security>
				</binding>
			</netTcpBinding>
		</bindings>
		<client>
			<endpoint address="net.tcp://localhost:57650/TestService"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ITestService" contract="ITestService" name="NetTcpBinding_ITestService">
				<identity>
					<servicePrincipalName value="TestService" />
				</identity>
			</endpoint>
		</client>
	</system.serviceModel>
</configuration>

Wenn aber die Anzahl der Zeilen 65535 übersteigt, dann kommt:> Fehlermeldung:

System.ServiceModel.CommunicationException: {"Fehler beim Deserialisieren von Parameter ekz.HostToExcelService:datenZeilen. Die InnerException-Nachricht war &quot;Die maximale Anzahl der Elemente in einem Objektgraph, die serialisiert bzw. deserialisiert werden können, beträgt &quot;65536&quot;. Ändern Sie den Objektgraph oder erhöhen Sie das MaxItemsInObjectGraph-Kontingent. &quot;. Weitere Details finden Sie unter &quot;InnerException&quot;."}

InnerException: System.Runtime.Serialization.SerializationException: {"Die maximale Anzahl der Elemente in einem Objektgraph, die serialisiert bzw. deserialisiert werden können, beträgt &quot;65536&quot;. Ändern Sie den Objektgraph oder erhöhen Sie das MaxItemsInObjectGraph-Kontingent. "}

-> Wie kann ich die maximale Anzahl an Zeilen erhöhen? Ich welcher Konfiguration muss ich was ändern?

Danke im Voraus!

28.09.2012 - 12:53 Uhr

Aber gerade das SystemEvents.SessionEnding/Ended-Ereignis fehlt in deiner Liste doch.

Stimmt! Ich habe leider das erste Post von Th69 misinterpretiert. Ich hatte mich in dessen Link an WM_QUERYENDSESSION festgebissen und unbewusst SystemEvents.SessionEnding/Ended ignoriert.

Beides funktioniert: Sowohl SystemEvents.SessionEnding/Ended mit und ohne zusätzlicher Form, als auch Auswertung von CloseReason bei einer Form.

-> Ergo: Ich verwende SystemEvents.SessionEnded und erspare mir die Form.

Danke an alle und ein schönes Wochenende!

28.09.2012 - 08:59 Uhr

Trotz Application.Run() war bei mir unter Windows XP SP3 nichts zu machen. Ich habe versucht, mich bei folgenden Events einzuklinken und eine Log-Ausgabe (DB und Datei) zu machen:
Application.ApplicationExit
Application.ThreadException
Application.ThreadExit
AppDomain.CurrentDomain.DomainUnload
AppDomain.CurrentDomain.ProcessExit
AppDomain.CurrentDomain.UnhandledException
-> hat alles nichts genutzt.

Also habe ich mir eine Form besorgt, die im Programm zuerst sichtbar und danach unsichtbar gemacht wird und auf QUERYENDSESSION reagiert. Was auch User Th69 angeregt hat und ich auch schon in anderen Programmen verwendet habe.

Die Form:

public partial class MainForm : Form
{
	public MainForm()
	{
		InitializeComponent();//durch IDE erzeugt, mit standardmäßiger MainForm.Designer.cs

		FormClosing += new FormClosingEventHandler(Form_FormClosing);
	}

	protected override void WndProc(ref Message m)
	{
		const int WM_QUERYENDSESSION = 0x11;

		if (m.Msg == WM_QUERYENDSESSION)
		{
			Program.ExitPending = true;
		}

		base.WndProc(ref m);
	}

	void Form_FormClosing(object sender, FormClosingEventArgs e)
	{
		if (Program.ExitPending)
		{
			e.Cancel = false;
			Program.RegisterExit("Form_FormClosing");
		}
		else
		{
			e.Cancel = true;
			Hide();
		}

		base.OnClosing(e);
	}
}

Im Programm gibt es eine Variable ExitPending mit Initialwert false. Diese wird auf true gesetzt, wenn man manuell das Programm beendet (Contextmenü im Notifyicon) oder wenn die Form die Message QUERYENDSESSION bekommt. Letzteres passiert beim System-Shutdown, wobei dann Form_FormClosing aufgerufen wird. Da ExitPending mittlerweile auf true steht, kann das Programm die gewünschten Aufräumarbeiten (hier: Logging in RegisterExit()) durchführen.

26.09.2012 - 09:35 Uhr

@Th69: Danke für die Antwort. Damit komme ich aber nicht weiter, weil ich nun mal keine Form in der Anwendung habe. Nach der Erzeugung des NotifyIcons (und natürlich anderer Sachen) rufe ich Application.Run() auf.

26.09.2012 - 08:53 Uhr

Hallo,
ich habe unter C# 3.5 eine Anwendung mit einem NotifyIcon (ohne Form). Ich hätte gerne, dass diese noch Code ausführen kann (z. B. Datenbankeinträge machen), wenn der Rechner runtergefahren wird.

Was anscheinend bei mir nicht geht ist:
Application.ApplicationExit
AppDomain.CurrentDomain.DomainUnload
AppDomain.CurrentDomain.ProcessExit
AppDomain.CurrentDomain.UnhandledException

Zumindest mal wurden die entsprechenden Datenbank-Einträge nicht gemacht.

Welche Möglichkeiten hat man, um ein System-Shutdown mitzubekommen?

13.07.2012 - 09:30 Uhr

"abc" oder auch der ctor ohne Argumente

13.07.2012 - 08:53 Uhr

Ich habe mich leider falsch ausgedrückt. Ich meinte nicht Objekterstellung sondern die Generierung von Inline-Code für "kleine" ctors bei "kleinen" Klassen.

Dass die Objekte zur Kompilierungszeit noch nicht instanziiert werden können ist ja klar. Ich hatte es nur total falsch beschreiben.


namespace CtorTest
{
	public class TestClass
	{
		public string Text;

		public TestClass()
		{
			Program.Log("ctor TestClass()");
		}

		public TestClass(string text)
			: this()
		{
			Program.Log("ctor TestClass(string text)");
			Text = text;
		}
	}

	public static class Program
	{
		public static void Log(string message)
		{
			System.Console.WriteLine(message);
		}

		static void Main(string[] args)
		{
			TestClass testClass1 = new TestClass();
			TestClass testClass2 = new TestClass("abc");

			Log("Taste drücken zum Beenden");
			System.Console.ReadLine();
		}
	}
}

12.07.2012 - 18:02 Uhr

Hallo,

bisher bin ich immer davon ausgegangen, dass die Erstellung eines Objektes mit new zur Kompilierzeit und bei einem Releasebuild bereits durchlaufen wurde, sofern der entsprechende Konstruktor zur Kompilierzeit alle notwendigen Argumente konstant übergeben bekommt.

Jetzt habe ich einen Test gemacht: Releasebuild und Logausgaben im Konstruktor. Anscheinend wird der Konstruktor doch jedesmal zur Laufzeit abgearbeitet, was dann auch selbstverständlich zu längeren Ausführungszeiten durch die Objekterstellung führt.

Wo liegt denn da mein Denkfehler?

Gruß,
Gunter

27.05.2011 - 15:21 Uhr

Hallo,

wie kann ich eine double-Variable, die sich unwesentlich von 0 unterscheidet, im nicht-wissenschaftlichen Format ausgeben? Und zwar ohne dass ich eine bestimmte Nachkommastellenzahl im Formatstring angebe.

Beispiel: Ich habe die Zahl 0.00000000015070408950617284. So sehe ich sie als Ausgabe von VS2008 im Direktfenster.

Nun soll die nicht als "1,50704089506173E-10", sondern als "0.00000000015070408950617284" erscheinen, wenn ich einen String davon erhalten will.

24.11.2009 - 13:47 Uhr

Leider rechnet DateTime.IsLeapYear() für Jahre im Julianischen Kalender (also vor 1582) falsch. Z. B. war das Jahr 1500 ein Schaltjahr. Laut DateTime.IsLeapYear() war es das nicht.

/// <summary>
/// Errechnet, ob ein Jahr ein Schaltjahr ist.
/// Im Gegensatz zur <see cref="DateTime.IsLeapYear" />-Methode 
/// wird auch die Julianische Schaltjahresregel berücksichtigt.
/// </summary>
/// <param name="year">
/// Das Jahr.
/// Für die vorchristlichen Jahre wird die astronomische, 
/// nicht die historische Zählweise vorausgesetzt. 
/// Das vor dem Jahr 1 n. Chr. liegende Jahr wird daher als Jahr 0 gezählt (astronomisch), 
/// nicht als Jahr 1 v. Chr. (historisch), 
/// das vor diesem liegende Jahr wird als Jahr -1 und nicht als Jahr 2 v. Chr. gezählt, usw.
/// </param>
/// <returns>True bei einem Schaltjahr. Sonst false.</returns>
public static bool IsLeapYear(int year)
{
	bool result;

	if (year <= 1582)
	{
		//Julian calendar

		result = (year % 4 == 0);
	}
	else
	{
		//Gregorian calendar

		result = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
	}

	return result;
}

30.10.2009 - 14:19 Uhr

Könnte mir bitte jemand die Lösung näher erläutern? Danke.

08.07.2009 - 06:54 Uhr

Hat schon mal jemand praktische Erfahrungen mit diesem Tool gemacht?

17.02.2009 - 17:11 Uhr

Vielen Dank für eure Mühen. Die vorgeschlagenen Lösungen funktionieren auch. 👍

Bloß habe ich nun gemerkt, dass mir das doch nichts nützt, da ich für jeden Einzelstring den Index des jeweils letzten Zeichens brauche. Weiterhin muss ich wissen, ob die Einzelstrings "echte" Wörter oder Trennblöcke sind (z. B. ein oder mehrere Blanks).

Also muss weiterhin der Automat herhalten... =)

30.01.2009 - 07:55 Uhr

Diese Methode soll nicht dazu dienen, Code bedingt zu kompilieren. Mit Hilfe der Feststellung der Konfiguration zur Laufzeit und mit weiteren, noch zu implementierenden, Abhängigkeiten soll letztendlich zurückgegeben werden, ob man quasi in einem logischen Test- oder Produktiv-Zustand arbeitet.

Z. B.:*Die Anwender sollen eine Warnung bekommen, wenn sie irrtümlich mit einer Debug-Konfiguration arbeiten. *Die Anwender sollen eine Warnung bekommen, wenn der Applikationsserver irrtümlich mit der Testdatenbank verbunden ist. *Die Entwickler sollen eine Warnung bekommen, wenn der Applikationsserver mit der Produktivdatenbank verbunden ist.

29.01.2009 - 15:34 Uhr

Ich muss einen ganz normalen Fließtext à la „Rotkäppchen ging in den Wald und schoss den großen, bösen Wolf.“ splitten. Ich brauche aber in dem resultierenden Wort-Array auch die Leerzeichen inkl. Satzzeichen zwischen den Wörtern als quasi "Leerwort", natürlich mit der korrekten Länge.

Das Array sollte also so aussehen:
[ 0]: "Rotkäppchen" [ 1]: &quot; &quot; [ 2]: &quot;ging&quot; [ 3]: &quot; &quot; [ 4]: &quot;in&quot; [ 5]: &quot; &quot; [ 6]: &quot;den&quot; [ 7]: &quot; &quot; [ 8]: &quot;Wald&quot; [ 9]: &quot; &quot; [10]: &quot;und&quot; [11]: &quot; &quot; [12]: &quot;schoss&quot; [13]: &quot; &quot; [14]: &quot;den&quot; [15]: &quot; &quot; [16]: &quot;großen&quot; [17]: &quot;, &quot; [18]: &quot;bösen&quot; [19]: &quot; &quot; [20]: &quot;Wolf&quot; [21]: &quot;.&quot;

Hierzu habe ich einen Endlichen Automaten geschrieben, der das bravourös löst. Aber viel zu langsam.

Hat jemand eine Idee, um so etwas mit Hilfe von .NET-Bordmitteln oder einem schnellen Algorithmus effizienter zu machen?

29.01.2009 - 09:26 Uhr

@rastalt: Nimm's bitte nicht persönlich aber ich hasse mehrere Returns in einer Methode. Deswegen kodiere ich so nicht.

Ob man, wie in diesem Beispiel, den ELSE-Fall ausprogrammiert oder schon vor dem IF eine Variable setzt ist wieder ein anderes Thema und hat mit meiner Multi-Return-Aversion nichts zu tun.

29.01.2009 - 07:19 Uhr

Hierzu stehe ich im Moment vor einem Problem oder besser: vor einer Ungewissheit.

Ich habe im VS2005 eine Projektmappe mit mehreren Projekten. In den einzelnen Projekten brauche ich an gewissen Stellen sowas:


#if DEBUG
	... mach irgendwas ...
#endif

Jetzt habe ich mir gedacht, dass ich in einer statischen Basisklasse ("ganz unten") eine Methode definiere:


public static bool IsDebugEnvironment()
{
	bool isDebug = false;

#if DEBUG
	isDebug = true;
#endif

	return isDebug;
}

und diese Methode in allen darauf aufbauenden Projekten verwende, anstatt dort jedesmal "#if DEBUG" abzufragen.

Nun habe ich das getestet und es scheint auch so, dass das richtig funktionert. Ich habe sogar ein Release-Build der Basisklasse als Verweis eingebunden und denn in der Debug-Konfiguration das Programm gestartet. Ergebnis: Die Konfiguration des Programms ("Debug") schlägt anscheinend bis in die von ihm eingebundenen Assemblies (Release-Builds) durch.

Ich bin mir aber nicht schlüssig, ob dies wirklich ein kugelsicherer Weg ist.

25.01.2009 - 00:24 Uhr

Klar, den Umweg über die Bruchzahlen muss man nicht machen. Das kam aber auch daher, weil in VB6 die rnd()-Funktion Werte zwischen 0 und 1 liefert.

Leider komme ich jetzt nicht an den alten Programmcode heran und habe tatsächlich etwas in meinem vorhergehenden Posting unterschlagen bzw. falsch aus dem Gedächtnis zitiert. Wenn ich es recht in Erinnerung habe, dann definierte ich ein Array von 1 bis 49 mit einem Bool, das ein "Gezogen"-Flag darstellt.

Für jede zu ziehende Kugel generierte ich mir dann eine Zufallszahl zwischen 0 und 1. 1 entspricht dabei der Anzahl jeweils verbleibender Kugeln. Bei der ersten Ziehung also 49. Mit dem Dreisatz kam ich dann auf die n-te aller verbleibenden Kugeln. Da n eine Ordnungszahl ist und nicht gleich der Kugelnummer, also der Lottozahl entspricht, muss man danach das o.g. Array mit einer Zählvariable abklappern und dabei diejenigen Zahlen überspringen, die bei den vorhergehenden Ziehungen gezogen wurden und das "Gezogen"-Flag auf true gesetzt bekamen.

Beispiel:
Ziehung 1: Zufallszahl liefert 0,572421; n = (int)(49*0.572421) = 28; Array durchlaufen -> als 28. noch nicht gezogene Zahl findet man 28.

Ziehung 2: Zufallszahl liefert 0,123456; n = (int)(48*0.123456) = 6; Array durchlaufen -> als 6. noch nicht gezogene Zahl findet man 6.

Ziehung 3: Zufallszahl liefert 0,987654; n = (int)(47*0.987654) = 46; Array durchlaufen -> als 46. noch nicht gezogene Zahl findet man 48.

Soweit der Algorithmus aus dem Gedächtnis. Um Effizienz und Eleganz ging's mir damals nicht, zumal die technischen Mittel gegenüber C# deutlich eingeschränkt waren (Listen, generische Listen, ...)

Aber wir sollten nun wirklich diesen Thread ruhen lassen.

24.01.2009 - 21:51 Uhr

Unabhängig von der technischen Realisierung, die Zufallszahlen an sich zu erzeugen, wäre vielleicht folgende Vorgehensweise überlegenswert (, die ich früher mal in VB6/VBA realisiert habe, aber egal...):

Erste Kugel: Man hat 49 Zahlen. Man "zieht" eine Zahl (double), die irgendwo zwischen 0 und 1 liegt. Diese rechnet man per Dreisatz auf die verbliebenen Zahlen/Kugeln um. Heißt: man bildet Intervalle, indem man den Wertebereich [0...1] in 49 Teile teilt und dann die gezogene Zahl (also der Double-Wert zwischen 0 und 1) in das entsprechende Intervall unterbringt. Die fortlaufende Nummer des Intervalls ist dann die Zahl, die als Lottozahl erscheint.

Zweite Kugel: Da man schon eine Kugel vorher aus der Ursprungsmenge weggenommen hat, bleiben 48 Kugeln übrig: Man erzeugt sich also wieder eine Zufallszahl zwischen 0 und 1 und rechnet die per Dreisatz auf das richtige Intervall um, indem man den Bereich von 0 bis 1 in diesmal 48 Intervalle unterteilt.

Dritte Kugel: siehe oben, nur dass man nun 47 Intervalle hat. Usw.

Ich finde, dass dies aufgrund der Vorgehensweise eher der "echten" Lottozahlenziehung entspricht als irgendwie bereits gezogene Zufallszahlen auf Doppelung zu überpfüfen 😉

Mal ein Beispiel:

Für die erste Kugel haben die Intervalle die Länge 1/49 = 0,020408163, also:
1: 0,0 bis 0,020408163
2: 0,020408163 bis 0,040816327
3: 0,040816327 bis 0,061224490
...
49: 0,979591837 bis 1.0
Eine Zufallszahl zwischen 0 und 1 zieht man sich dann in C# z. B. durch


double z = new Random().Next(0, 100) / 100

Wenn z für die erste Kugel den Wert 0,572421 hat, dann wäre die Kugelnummer (= Intervallnummer = Lottozahl) (int)49*0.57241 = 28.

Wenn z für die zweite Kugel 0,123456 hat, dann wäre die Lottozahl (int)48*0.123456 = 6.

Dritte Kugel: z = 0.987654 -> Lottozahl = (int)47*0.987654 = 46.

Usw. Die Ausprogrammierung überlasse ich meinen Studenten... 😁

22.01.2009 - 18:16 Uhr

Naja, ich finde nicht gerade, dass ein Entwickler die Werte von int.MinValue und int.MaxValue unbedingt parat hat (auswendig kennt?). Und selbst wenn, ich finde es schade, dass die XML-Dokumentationskommentare nicht damit umgehen können.

22.01.2009 - 10:58 Uhr

Was ich leider bei den Dokumentationsmöglichkeiten vermisse ist, dass man die Werte von Konstanten ausgeben lassen kann. In folgendem Code-Ausschnitt bräuchte ich bei 3 Überladungen in den Kommentaren die Werte von int.MinValue und int.MaxValue.


public static class ESNumber
{
	static Random _random = new Random();

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// Als Generator wird derjenige des statischen Typs <see cref="ESNumber" /> benutzt.
	/// Die inklusive untere Grenze der Zufallszahl ist -2147483648. Die inklusive obere Grenze der Zufallszahl ist 2147483647.
	/// </summary>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber()
	{
		return GetRandomNumber(_random);
	}

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// Die inklusive untere Grenze der Zufallszahl ist -2147483648. Die inklusive obere Grenze der Zufallszahl ist 2147483647.
	/// </summary>
	/// <param name="newRandom">
	/// True, wenn ein neuer, temporärer Generator benutzt werden soll.
	/// False, wenn derjenige des statischen Typs <see cref="ESNumber" /> benutzt werden soll.
	/// </param>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber(bool newRandom)
	{
		return GetRandomNumber(
			(newRandom ? new Random() : _random)
		);
	}

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// </summary>
	/// <param name="newRandom">
	/// True, wenn ein neuer, temporärer Generator benutzt werden soll.
	/// False, wenn derjenige des statischen Typs <see cref="ESNumber" /> benutzt werden soll.
	/// </param>
	/// <param name="minValue">Die inklusive untere Grenze der Zufallszahl.</param>
	/// <param name="maxValue">Die inklusive obere Grenze der Zufallszahl.</param>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber(bool newRandom, int minValue, int maxValue)
	{
		return GetRandomNumber(
			(newRandom ? new Random() : _random),
			minValue,
			maxValue
		);
	}

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// Die inklusive untere Grenze der Zufallszahl ist -2147483648. Die inklusive obere Grenze der Zufallszahl ist 2147483647.
	/// </summary>
	/// <param name="random">Der Generator.</param>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber(Random random)
	{
		return GetRandomNumber(random, int.MinValue, int.MaxValue - 1);
	}

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// Als Generator wird derjenige des statischen Typs <see cref="ESNumber" /> benutzt.
	/// </summary>
	/// <param name="minValue">Die inklusive untere Grenze der Zufallszahl.</param>
	/// <param name="maxValue">Die inklusive obere Grenze der Zufallszahl.</param>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber(int minValue, int maxValue)
	{
		return GetRandomNumber(_random, minValue, maxValue);
	}

	/// <summary>
	/// Errechnet eine Zufallszahl.
	/// </summary>
	/// <param name="random">Der Generator.</param>
	/// <param name="minValue">Die inklusive untere Grenze der Zufallszahl.</param>
	/// <param name="maxValue">Die inklusive obere Grenze der Zufallszahl.</param>
	/// <returns>Die Zufallszahl.</returns>
	public static int GetRandomNumber(Random random, int minValue, int maxValue)
	{
		return random.Next(minValue, maxValue + 1);
	}
}

22.01.2009 - 10:07 Uhr

@ths , @herbivore: Ja, das habe ich fast schon befürchtet. Ist ja eigentlich auch logisch. Und betrifft nicht nicht Überladungen sondern eigentlich jede Methode, die eine weitere Methode aufruft.

@kleines_eichhoernchen:

nicht zu penibel sein Das sehe ich auch so.

@Golo Roden: Stino == Stinknormal 😉

21.01.2009 - 13:11 Uhr

Nehmen wir an, wir haben eine Methode a(string s), die eine bestimmte Exception werfen kann und deshalb auch im Methodenkommentar eine Zeile mit dem <exception>-Tag hat.

Nehmen wir weiterhin an, wir haben eine Methode a(), die a(string) überlädt und intern a(null) aufruft. Sollte man dann für a() ebenfalls die Exception-Kommentarzeile angeben, obwohl a() nicht explizit diese Exception wirft? Oder sollte man in a() die Exception-Kommentarzeile weglassen, so wie in dem Beispiel:


/// <summary>
/// Holt einen int.
/// </summary>
/// <returns>Ein int.</returns>
int a()
{
	return a(null);
}

/// <summary>
/// Holt einen int.
/// </summary>
/// <param name="s">Ein String.</param>
/// <returns>Ein int.</returns>
/// <exception cref="MyException">Wenn ....</exception>
int a(string s)
{
	...
	throw new MyException(...);
}

31.10.2008 - 17:41 Uhr

Hallo Forum,
mit VS2005-Bordmitteln hatte ich mir vor einiger Zeit ein Setup meiner Anwendung erstellt, was auch bisher immer funktioniert hat.

Seit kurzem erhalte ich beim Starten des Setups anscheinend bei jeder Datei, die installiert werden soll eine MessageBox "Bei der Installation dieses Paketes ist ein unerwarteter Fehler aufgetreten. Es liegt eventuell ein das Paket betreffendes Problem vor. Der Fehlercode ist 2908."

Die Installation lässt sich leider nicht vorzeitig abbrechen, was ärgerlich ist da mit dem Setup tausende von Dateien installiert werden.

Trotz den ganzen Warnmeldungen werden aber anscheinend alle Dateien kopiert die Anwendung lässt sich danach starten. Sogar eine "Erfolgreich"-Meldung kommt zum Schluss des Setups.

Das Verpacken der Files für das Setup verlief übrigens ohne Probleme.

Was will mir nun der Fehler 2908 sagen und wie kann man das abstellen?

Umgebung: Win XP SP3 mit VS2005 Prof, Version 8.0.50727.762 (SP.050727-7600)

20.10.2008 - 10:31 Uhr

Stimmt. Danke!

20.10.2008 - 09:46 Uhr

Das Problem mit der "ContextMenuStrip"-Property ist für mich auch, dass die Einträge im ContextMenuStrip vom aktuellen Inhalt und Zustand der TextBox abhängen. Beispielsweise sollte ein Eintrag für "Ausschneiden" natürlich auf Enabled=False stehen, wenn gar kein Text selektiert ist.

Bei einem funktionierenden MouseUp()-Event könnte ich da ja ganz gezielt bei jedem Rechtsklick das Contextmenü neu aufbauen.

20.10.2008 - 09:27 Uhr

Der Umweg über die "ContextMenuStrip"-Property funktioniert. Trotzdem wüsste ich gerne, ob das abweichende Verhalten beim MouseUp() zwischen TextBox und RichTextBox Absicht ist oder ob das vielleicht nur bei mir so ist/erscheint.

17.10.2008 - 20:07 Uhr

Den Code sieht man im ersten Posting. Innerhalb der if-Bedingung wird halt das Kontextmenü angelegt und angezeigt. Ist aber auch für's Verständnis meines Problems egal: er läuft ja gar nicht erst ins's textBox1_MouseUp() rein.

17.10.2008 - 17:54 Uhr

Er läuft beim ersten Rechtsklick gar nicht in die Methode rein, sondern klappt das "eingebaute" Kontextmenü auf. Wähle ich darin z. B. "Einfügen", dann kommt er in die OnMouseUp()-Methode, wo dann mein eigenes Kontextmenü zusammengebaut und angezeigt wird.

17.10.2008 - 16:34 Uhr

Ich möchte gerne in TextBoxes und RichTextBoxes eigene Kontextmenüs anzeigen. Dazu habe ich ich mich in das jeweilige MouseUp-Event eingeklinkt. Leider klappt das nur sinnvoll bei der RichTextBox. Bei der TextBox kommt zuerst das eingebaute Kontextmenü ("Auschneiden, "Kopieren", usw.) und wenn man dann einen Eintrag davon auswählt, wird anscheinend erst das MouseUp-Event der TextBox ausgelöst.


void richTextBox1_MouseUp(object sender, MouseEventArgs mea)
{
	//nach Rechtsklick kommt man direkt hier rein

	base.OnMouseUp(mea);

	if (mea.Button == MouseButtons.Right) {
	}
}

void textBox1_MouseUp(object sender, MouseEventArgs mea)
{
	//nach Rechtsklick kommt man erst durch einen zweiten Rechtsklick hier rein

	base.OnMouseUp(mea);

	if (mea.Button == MouseButtons.Right) {
	}
}

Hat meine Entwicklungsumgebung nur einen schlechten Tag oder ist das ein übliches Verhalten? Und wie kann man das nun ändern?

24.09.2008 - 09:55 Uhr

Der Thread ist zwar uralt aber scheint für meine Frage gut zu passen.

Ich habe im VS2005 eine Projektmappe mit mehreren Projekten. Projekt A definiert Klassen, die im Projekt B benutzt werden. Projekt B ist ein Windows-Dienst.

Wenn ich nun meine beiden Projektmappen-Builds (Debug und Release) neu per "Batch erstellen..." erstelle, dann scheitert das Compilieren natürlich weil zum einen der Dienst noch läuft (Projekt B) und dieser auch noch eine Klasse aus Projekt A verwendet.

Wie kann ich festlegen, dass im ersten Build im Präbuildereignis von Projekt A der Dienst gestoppt wird und im letzten Build im Postbuildereignis von Projekt B der Dienst wieder gestartet wird? Weil ich ja mehrere Buildkonfigurationen kompiliere, wird der Dienst natürlich nach dem erfolgreichen Kompilieren der ersten Konfiguration leider wieder gestartet, womit dann die zweite Buildkonfiguration das Problem hat, dass der Dienst läuft.

Zusatzfrage: Ist eigentlich definiert, in welcher Reihenfolge die Buildkonfigurationen kompiliert werden? (Alphabetisch? Zuerst "Debug" und dann "Release"? Oder umgekehrt? ?()

20.08.2008 - 17:44 Uhr

@ErfinderDesRades: Das mit den unterschiedlichen Werten des Getters bzw. Setters ist natürlich etwas sonderbar. Ich habe als Vorlage dafür den Code von "vijayaprasen" aus codeproject.com.

Nichtsdestotrotz ist die NewTabPage bei mir nun gestorben. Ich bekomme es aber dann nicht mehr hin, dass für den Schließen-Button rechts etwas Platz gelassen wird. Der Aufruf von Graphics.DrawString() erfolgt zwar beim Debuggen mit dem zusätzlichen Platz aber es hat dann trotzdem keinen Effekt.

Dieses Problem ist jedoch mittlerweile obsolet: In Font GetNewFont() skaliere ich den zu ermittelnden Font einfach mit 0.93 und mit DrawString() kommt dann der benötigte Platz. Den Skalierungsfaktor stelle ich als öffentliche Property zur Verfügung, so dass für andere Schriftarten oder sonstige Zwecke der Platz im Nachhinein angepasst werden kann.

Zu der OnMouseMove()- bzw. OnDrawItem()-Geschichte: Hier bin ich im Moment hoffnungslos mit der Materie überfordert. Und deine Beispielapplikation bestärkt mich noch in dieser Meinung (das ist als Kompliment gedacht). Ich habe mich bisher mit Grafikprogrammierung noch gar nicht beschäftigt. Zudem komme ich in den nächsten Wochen auch nicht dazu, diesbezüglich weiterzuarbeiten. Zum Glück hat das bei meinen Anwendern keine hohe Priorität und evtl. verzichte ich auch ganz auf den optischen Schnickschnack und verwende nur eine statische Anzeige.

Ich habe jetzt mal ein Klasse für alle Einstellungen definiert, die auch schon zum Teil gefüllt wird. Wie ich das dann in OnDrawItem() anwende, weiß ich noch nicht. Eventuell geht's in ein paar Wochen weiter, wenn ich wieder etwas Zeit dafür habe.

20.08.2008 - 17:40 Uhr

@herbivore: Entschuldige bitte, das habe ich nicht gewusst. Andererseits bezieht sich die Fragestellung nicht auf eine bestimmte Klassenmethode sondern benötigt als "Background" etwas mehr Code. Und da das angehängte C#-Projekt nicht unbedingt sehr große Ausmaße hat, beinhaltet es nicht viel Unwesentliches, was das Verständnis erschwert. Aber da kann man natürlich anderer Meinung sein.

20.08.2008 - 14:17 Uhr

Und für allgemeines Umrechnen:


	#region Zeitspannenkonvertierung
	const int MillisecondsPerSecond = 1000;
	const int SecondsPerMinute = 60;
	const int MinutesPerHour = 60;
	const int HoursPerDay = 24;

	public static int SecondsToMilliseconds(int seconds)
	{
		return MillisecondsPerSecond * seconds;
	}

	public static int MinutesToSeconds(int minutes)
	{
		return SecondsPerMinute * minutes;
	}

	public static int HoursToMinutes(int hours)
	{
		return MinutesPerHour * hours;
	}

	public static int DaysToHours(int days)
	{
		return HoursPerDay * days;
	}

	public static int MinutesToMiliseconds(int minutes)
	{
		return SecondsToMilliseconds(MinutesToSeconds(minutes));
	}

	public static int HoursToMiliseconds(int hours)
	{
		return MinutesToMiliseconds(HoursToMinutes(hours));
	}

	public static int DaysToMiliseconds(int days)
	{
		return HoursToMiliseconds(DaysToHours(days));
	}

	public static int HoursToSeconds(int hours)
	{
		return MinutesToSeconds(HoursToMinutes(hours));
	}

	public static int DaysToSeconds(int days)
	{
		return HoursToSeconds(DaysToHours(days));
	}

	public static int DaysToMinutes(int days)
	{
		return HoursToMinutes(DaysToHours(days));
	}

	public static int MillisecondsToSeconds(int milliSeconds)
	{
		return milliSeconds / MillisecondsPerSecond;
	}

	public static int SecondsToMinutes(int seconds)
	{
		return seconds / SecondsPerMinute;
	}

	public static int MinutesToHours(int minutes)
	{
		return minutes / MinutesPerHour;
	}

	public static int HoursToDays(int hours)
	{
		return hours / HoursPerDay;
	}

	public static int MillisecondsToMinutes(int milliSeconds)
	{
		return SecondsToMinutes(MillisecondsToSeconds(milliSeconds));
	}

	public static int MillisecondsToHours(int milliSeconds)
	{
		return MinutesToHours(MillisecondsToMinutes(milliSeconds));
	}

	public static int MillisecondsToDays(int milliSeconds)
	{
		return HoursToDays(MillisecondsToHours(milliSeconds));
	}

	public static int SecondsToHours(int seconds)
	{
		return MinutesToHours(SecondsToMinutes(seconds));
	}

	public static int SecondsToDays(int seconds)
	{
		return HoursToDays(SecondsToHours(seconds));
	}

	public static int MinutesToDays(int minutes)
	{
		return HoursToDays(MinutesToHours(minutes));
	}
	#endregion

19.08.2008 - 16:38 Uhr

Hallo, ich ich mir eine eigene TabControl- bzw. TabPage-Klasse erstellt. Hauptzweck ist, dass im Kopfbereich der einzelnen Register ein Schließen-Button aktiviert werden kann. Dies klappt auch hervorragend. Das kleine Problem ist aber nun, dass durch ständiges Neuzeichnen bei jeder Mausbewegung im Kopfbereich ein Flackern auftritt.

Vielleicht hat ja jemand eine Idee, was man da tun könnte.

Weiterhin wird in der abgeleiteten TabPage-Klasse ("NewTabPage") die Property "Text" überschrieben, damit der Text nicht durch den Schließen-Button verdeckt wird. Und zwar werden im Getter mehrere Blanks angehängt. Die Frage ist nun, wieviele Blanks braucht man? Es existiert wohl eine Abhängigkeit von dem verwendeten Font des TabControls. Ich habe jetzt mal willkürlich 4 Blanks vorgegeben. Gescheitert bin ich mit der Idee, im Getter via Graphics.MeasureString() einen sinnvollen Wert zu ermitteln. Die benötigte Pixelzahl aus der Summe des rechten Offsets und der Breite der Bitmap wollte ich durch die effektive Breite eines Blanks dividieren um so die Anzahl zu erhalten. Theoretisch klappt das auch aber in der Praxis wächst der Fehler bei größer- bzw. kleinerwerdenden Schriften immer mehr an. MeasureString habe ich dabei sowohl mit als auch ohne "StringFormat.GenericTypographic" probiert.

Ich habe die Projektmappe mal angehängt. Vielen Dank für eure Aufmerksamkeit.

27.06.2008 - 08:33 Uhr

Bin ich der einzige, bei dem das so ist oder braucht sonst niemand eine Konfiguration von DLL's in seinen Anwendungen?

26.06.2008 - 14:59 Uhr

Ich habe gerade eine "entsetzliche" Feststellung machen müssen: Wenn ich User-Einstellungen in der *.config-Datei einer eingebundenen DLL ändere, dann wirkt sich das zur Laufzeit im Code innerhalb einer Methode der DLL nicht aus. Und dies jetzt wirklich ohne jedes Framework-Gedöns meinerseits. Ich habe es in einer Mini-Testanwendung mit einer eingebunden (Verweis) Mini-DLL ausprobiert.

Ich habe in der DLL ClassLibrary1.dll eine "Einstellungsdatei" CommonSettings.settings mit einem Schlüsselwertpaar key/valueX. Nach dem Kompilieren ändere ich nun in der ClassLibrary1.dll.config den value von "keyX" nach "keyY" Wenn ich nun in meiner Minimal-EXE eine in der DLL definierte Klasse instanziiere und im Konstruktor der Klasse (und damit ja zur Laufzeit in der DLL) bin, dann bringen mir weder Properties.CommonSettings.Default.PropertyValues, noch Properties.CommonSettings.Default.Properties den geänderten Wert. In meiner EXE klappt das mit dem Ändern von Werten in der *.config-Datei anstandslos.

Ist dieses Verhalten so gewollt? Das würde ja bedeuten, dass sämtliche Einstellungen in *.config-Dateien wirkungslos bleiben, sobald die zugehörige DLL bzw. Assembly nicht ausgeführt, sondern lediglich als Verweis eingebunden ist.

23.06.2008 - 14:49 Uhr

Ach Kinners! Jetzt weiss ich, was falsch läuft.

In meinem Framework werden für alle Projekte alle Einstellungen via separater app.config bzw. VS2005-Designer abgelegt und die Defaultwerte definiert. Der Zugriff darauf zur Laufzeit geschieht aber nicht generisch, sondern (gewollt!) via Durchlaufen einer System.Configuration.SettingsPropertyCollection, die von dem jeweiligen Projekt, dass die Anfrage nach einem Settingswert stellt, an eine zentrale statische Klasse "tief unten" in meinem Core-Projekt weitergeleitet und dort behandelt wird. Damit habe ich auch die Möglichkeit, Einstellungswerte an zentraler Stelle mit Datenbankinhalten für Benutzereinstellungen zu behandeln.

Bisher lautete der Code zum Auslesen eines Wertes:


static string GetDefaultvalueString(string key, Assembly assembly, bool logIfNotFound, SettingsPropertyCollection properties)
{
	string ret = ESText.EmptyString;
	bool found = false;

	//Schleife ueber alle Properties
	foreach (SettingsProperty sp in properties) {
		//Key-Namen vergleichen
		if (ESText.IsEqual(key, sp.Name)) {
			//Key gefunden

			found = true;

			//Defaultwert auslesen
			ret = (string)sp.DefaultValue;

			//Schleife beenden
			break;
		}
	}

	if (!found) {
		//Defaultwert nicht vorhanden -> Logging
		(...)
	}

	return ret;
}

Das Problem hierbei ist, dass das SettingsProperty-Objekt sp via "sp.DefaultValue" den Wert aus der app.config liefert und nicht den, der in der *.exe.config nach dem Kompilieren geändert worden ist.

Ich habe es nun so abgeändert, dass in der Schleife nur noch geprüft wird, ob der Key vorhanden ist. Falls ja wird der gewünschte Wert mit "properties[key].DefaultValue" dann gelesen:


static string GetDefaultvalueString(string key, Assembly assembly, bool logIfNotFound, SettingsPropertyCollection properties)
{
	string ret = ESText.EmptyString;
	bool found = false;

	//Schleife ueber alle Properties
	foreach (SettingsProperty sp in properties) {
		//Key-Namen vergleichen
		if (ESText.IsEqual(key, sp.Name)) {
			//Key gefunden

			found = true;

			//Defaultwert auslesen
			try {
				ret = (string)properties[key].DefaultValue;
			}
			catch {
				//Fehler beim Auslesen
				(...)
			}

			//Schleife beenden
			break;
		}
	}

	if (!found) {
		(...)
	}

	return ret;
}

Ich verzichte übrigens auf ein in's Blaue geratenes "ret = (string)properties[key].DefaultValue;" ohne vorher mit der foreach-Schleife zu testen, ob der Key überhaupt vorhanden ist. Das wäre zwar nicht unelegant, dauert aber bei nicht vorhandenen Keys durch die Exception viel länger.

20.06.2008 - 17:55 Uhr

@juetho: Page not found. Im übrigen möchte ich ja ungern die app.config zur Laufzeit ändern.

20.06.2008 - 17:53 Uhr

Ich habe nun folgendes herausgefunden:1.Wenn ich in der *.exe.config bei den <applicationSettings einen Wert verändere, dann wirkt sich das beim Programmstart tatsächlich aus. Sehr schön. Aber: 1.Bei meinem Windows-Service tut es dies nicht. Nun ist es so, dass bei einem Windows-Service das Arbeitsverzeichnis grundsätzlich "c:\Windows\system32" ist. 1.Also dachte ich mir: kopier die ganzen Files doch dahin, installier den Dienst neu und probier es aus. Aber auch dann nimmt die Anwendung (=Windows-Service) nicht die Einstellungen aus der *.exe.config.

Sehr schade.

20.06.2008 - 15:13 Uhr

so wie ich es verstanden habe, willst du eine config-Datei per Editor ändern und die Änderung soll von den Anwendung ohne Neucompilieren angezogen werden. Richtig? Wenn ja, dann siehe meine schon gegebene Antwort. Die ist dann schon auf deine Frage zugeschnitten.

Auf diese Art wäre es möglich. Allerdings greift die Anwendung beim Starten dann ja auf User-Einstellungen zurück (die vorher explizit zur Laufzeit geändert werden müssten damit sie via "Properties.Settings.Default.Save()" überhaupt gespeichert werden). Was nützt mir aber das, wenn eine bestimmte Einstellung NICHT-userspezifisch beim Programmstart gezogen werden soll UND zur Kompilierungszeit evtl. einen abweichenden Wert hat?

Wieder konkret: Auf den Rechner, auf dem der .NET-Applikationsserverdienst läuft, loggen sich die Entwickler bzw. Admins per Remote ein. Wenn ich selbst beispielsweise auf dem Rechner bin, würde meine ganz persönliche User-Einstellung beim Programmstart herangezogen werden. Das ist aber unerwünscht weil dieser Dienst nunmal mit einer Konfiguration laufen mus, die für alle User gelten. Und nicht nur für den, der den Dienst gestartet hat.

Oder soll die Änderung auch ohne Neustart der Anwendung angezogen werden? Wenn ja, dann hast du dich wirklich undeutlich ausgedrückt, weil du was von Neucompilieren geschrieben hast.

Nein, es geht nur um den Neustart. Das mit dem "Neucompilieren" bezog sich auf das grundsätzliche Verhalten, dass eben Änderungen in der *.exe.config nach dem Kompilieren sich nicht auf die Einstellungen auswirken, die die Anwendung zur Laufzeit heranzieht.