<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-18006460</atom:id><lastBuildDate>Fri, 08 Mar 2024 02:36:06 +0000</lastBuildDate><category>.NET</category><category>Deployment</category><category>AppDomain</category><category>Exception</category><category>Install</category><category>Script</category><title>Yariv Hammer&#39;s Code Site</title><description>Articles, Resources and Advice about software development. Especially in .NET.</description><link>http://j1hammer.blogspot.com/</link><managingEditor>noreply@blogger.com (Yariv Hammer)</managingEditor><generator>Blogger</generator><openSearch:totalResults>49</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-774950799564579441</guid><pubDate>Thu, 04 Jan 2007 18:17:00 +0000</pubDate><atom:updated>2007-01-04T15:12:30.479-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Deployment</category><category domain="http://www.blogger.com/atom/ns#">Install</category><category domain="http://www.blogger.com/atom/ns#">Script</category><title>Deploying Your .NET Application Using Inno Setup</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I deployed my application using Inno Setup. It is a free and very intuitive installation engine.&lt;br /&gt;You can download it free &lt;a href=&quot;http://www.jrsoftware.org/isinfo.php&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Basically you create a script that ends with an .iss extension using the Inno-setup editor, which is much like a Notepad, but colors the script words. The syntax of the script is very much like an ini file. There is a very good help.&lt;br /&gt;&lt;br /&gt;In this article I will demonstrate how to create a good installation to your application.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The General Section&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Use the Inno Setup help to figure out what each command do. But this is what I use:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Setup]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppName=My Application - Proffesional Edition&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppVerName=My Application - Proffesional Edition v0.95&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;OutputBaseFilename=My Application - Proffesional Edition&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;OutputDir=.\&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppComments=The Proffesional Edition is intended for computers in off-line environment.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppContact=My Name and Phone&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppPublisher=My Company&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppPublisherURL=www.MyCompany.com&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppVersion=0.95&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;VersionInfoVersion = 1.0&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;DefaultDirName={pf}\My Application&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;DisableDirPage=yes&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;DefaultGroupName=My Application&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AllowNoIcons=no&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Compression=lzma&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;SolidCompression=yes&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AllowUNCPath=no&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;UserInfoPage=no&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AppCopyright=Copyright © 2007 My Company.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;AlwaysRestart=yes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:arial;&quot;&gt;Of course&lt;/span&gt; replace your name, application, and company.&lt;br /&gt;Notice the AlwaysRestart flag.&lt;br /&gt;The {pf} is a short for Program Files.&lt;br /&gt;It will create the installation file in the same folder as the iss file (you can change that of course).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Tasks Section&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Tasks]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;desktopicon&quot;; Description: &quot;{cm:CreateDesktopIcon}&quot;; GroupDescription: &quot;{cm:AdditionalIcons}&quot;; Flags: unchecked&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;quicklaunchicon&quot;; Description: &quot;{cm:CreateQuickLaunchIcon}&quot;; GroupDescription: &quot;{cm:AdditionalIcons}&quot;; Flags: unchecked&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;DotNetFramework&quot;; Description: &quot;.NET Framework 1.1&quot;; GroupDescription: &quot;If .NET is NOT installed:&quot;;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;MDAC&quot;; Description: &quot;Access, OleDB and Jet Providers&quot;; GroupDescription: &quot;Access, OleDB and Jet Providers&quot;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;During the installation the user will have a screen where he will be prompted to check various installation tasks. In this case he will be prompted to select a Desktop Icon, a Quick Launch Icon, a .NET Framework, and an MDAC.&lt;br /&gt;&lt;br /&gt;You must install the .NET Framework on the client&#39;s computer, or he will not be able to run your .NET assembly. Here I show how to install .NET 1.1, but you might need to install .NET 2.0 as well. You also must install MDAC if you are using OleDb providers, Access databases and so on (In Windows XP you don&#39;t need to install this). You might decide to install those things without prompting the user (if they are already installed no harm can be done).&lt;br /&gt;&lt;br /&gt;Of course you can add more tasks of your own.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Dirs Section&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Dirs]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\bin&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\bin\DB&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\bin\Config&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\bin\Resources&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\bin\DB\Xml&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\DotNet&quot;; Flags: deleteafterinstall; Tasks: DotNetFramework&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{app}\MDAC&quot;; Flags: deleteafterinstall; Tasks: MDAC&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;C:\Temp\&quot;; Flags: uninsneveruninstall ;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Each directory will be created during the installation. {app} is a short for the installation folder, that is equivalent to te DefaultDirName attribute in the Setup section.&lt;br /&gt;&lt;br /&gt;The Tasks flag is used to associate a line with a task. The task will be executed ONLY IF the task is selected by the user.&lt;br /&gt;The deleteafterinstall flag makes the folder disappear after the installation. The user will not even be aware of this folder creation. I will this soon.&lt;br /&gt;&lt;br /&gt;I also create the Temp folder just in case (some applications might depend on it).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Files Section&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;This is probably the most important section. Here you list all the files you want to install.&lt;br /&gt;Here are some examples:&lt;br /&gt;First I install My application exes and dlls. After that I install the App.Config files. Then I install the Database and the Xml files. Then I install the resources. Last I install third-party dlls (simply copy the files).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Files]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;; MyApplication.exe&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\MyApplication.exe&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\MyApplication.tlb&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly regtypelib;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\MyApplication.xml&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\configuration.tbs&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;Basic Software&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Server.exe&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\BusinessObjects.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\CommonControls.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Forms.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\BasicForms.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly; Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Grid.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly; Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;Module1&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Module1Manager.exe&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Module1Viewer.exe&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;Applications Configuration Files&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\MyApplication.exe.config&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Module1Manager.exe.config&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Module1Viewer.exe.config&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Server.exe.config&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;Additional files&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\DB\Database.mdb&quot;; DestDir: &quot;{app}\bin\DB&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Config\ConfigurationFile.xml&quot;; DestDir: &quot;{app}\bin\Config&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\My Application\bin\Resources\*.bmp&quot;; DestDir: &quot;{app}\bin\Resources&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;Additional Dlls&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\IOS\STU\bin\AxInterop.SHDocVw.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\IOS\STU\bin\Interop.SHDocVw.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\IOS\STU\bin\Nini.dll&quot;; DestDir: &quot;{app}\bin&quot;; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In addition I need to Install several MFC dlls, because one of my apps is from MFC in VC6. In that case I add the following section:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;System Files&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\MFC80d.dll&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\msvcr80d.dll&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\MFC42d.dll&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\MSVCRTD.DLL&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\MFCO42D.DLL&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\WINNT\system32\MFCN42D.DLL&quot;; DestDir: &quot;{sys}&quot;; Flags: allowunsafefiles&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The {sys} folder is the System32 folder on the client&#39;s machine. It will not replace the files if newer versions exist.&lt;br /&gt;&lt;br /&gt;Next, we need to prepare the .NET Framework and MDAC installations. We will put the files in the folder of the iss file, pack the files to the installation file. When the client will run the setup, we will unpack the installation files into the folder we marked earlier to remove after installtion. This is simply to copy the installation file. It will NOT run the setup files:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;.NET Framework&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Share\installs\Developer\ Installation\dotnetfx.exe&quot;; DestDir: {app}\DotNet; Tasks: DotNetFramework; Flags: deleteafterinstall;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;MDAC&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Source: &quot;C:\Projects\IOS\STU\ Installation\MDAC_TYP2.8.EXE&quot;; DestDir: &quot;{app}\MDAC&quot;; Tasks: MDAC; Flags: deleteafterinstall;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Notice the Tasks attribute - we only unpack the files if the user asked us to. The installation files will be deleted after the installation as well. You can download those executables free from the internet.&lt;br /&gt;&lt;br /&gt;Last, note that if you need to register a COM dll, or an OCX, you should use the regserver flag.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Icons Section&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;This will create the relevant shortcuts and icons on the client&#39;s computer.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Icons]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{group}\My Application - Proffesional Edition&quot;; Filename: &quot;{app}\bin\MyApplication .exe&quot;; WorkingDir: &quot;{app}\bin&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{group}\{cm:UninstallProgram,My Application - Proffesional Edition}&quot;; Filename: &quot;{uninstallexe}&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{userdesktop}\My Application - Proffesional Edition&quot;; Filename: &quot;{app}\bin\MyApplication .exe&quot;; Tasks: desktopicon ; WorkingDir: &quot;{app}\bin&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Name: &quot;{userappdata}\Microsoft\Internet Explorer\Quick Launch\My Application - Proffesional Edition&quot;; Filename: &quot;{app}\bin\MyApplication .exe&quot;; Tasks: quicklaunchicon ; WorkingDir: &quot;{app}\bin&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Registry and Environment Variables&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Here is an example of how to install an environment variable on the client&#39;s machine. The same way applies to all registry keys.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Registry]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Root: HKLM ; Subkey: &quot;SYSTEM\CurrentControlSet\Control\Session Manager\Environment&quot;; ValueType: string; ValueName: &quot;VARNAME&quot;; ValueData: &quot;VALUE&quot;; Flags: uninsdeletevalue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Note that the running application will not be notified of the new environment variables, and will need to restart (there is a way to solve this issue).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Installing the .NET Framework, MDAC, and other installations&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The Run section allows you to run additional installations. We need to run the installation of the .NET Framework, and MDAC. We use certain flags to make this a Silent Installation.&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;[Run]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;.NET Framework 1.1&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Filename: &quot;{app}\DotNet\dotnetfx.exe&quot;; Parameters:&quot;/q:a /c:&quot;&quot;install /q&quot;&quot;&quot; ;StatusMsg: &quot;Installing Microsoft .NET Framework (this may take a few minutes)&quot;; Flags: skipifdoesntexist; Tasks: DotNetFramework; WorkingDir: {app}\DotNet; Description: Installs the DotNET Framework &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;;MDAC&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Filename: &quot;{app}\MDAC\MDAC_TYP2.8.exe&quot;; Parameters:&quot;/Q:A /C:&quot;&quot;dasetup /Q:D /N&quot;&quot;&quot;; WorkingDir:&quot;{app}\MDAC&quot;; Flags: skipifdoesntexist; Tasks: MDAC; StatusMsg: &quot;Installing Access, OleDb and JET Providers (MDAC) (This may take a few minutes)&quot;; Description: Installs MDAC&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Testing your install file&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;After the script is done, we can compile it using Inno Setup. A setup executable will be generated for us, which we should ship to the client and he should be able to install and run your application.&lt;br /&gt;&lt;br /&gt;Great, huh? Well, imagine a client installing your application only to find out that it doesn&#39;t work because you forgot a dll? What would happen if you by mistake ruined the Client&#39;s registry, or deleted his Environment Variable? What about when uninstalling you by mistake deleted the Windows folder?&lt;br /&gt;&lt;br /&gt;Don&#39;t send it anywhere until you test it. The best way to test it is by using a Virtual PC. Create yourself one Virtual PC of Windows 2000, and one for Windows XP. Create a backup before you play with it. Then run the installation on the Virtual PC. Don&#39;t forget to uninstall and see the results. You get several benefits by applying this method:&lt;br /&gt;1. You can be 100% sure that you cannot do any damage, and that everything work.&lt;br /&gt;2. If something went wrong you can fix it, delete the VPC, and try again (don&#39;t forget to backup the original VPC).</description><link>http://j1hammer.blogspot.com/2007/01/deploying-your-net-application-using.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-116793303040539802</guid><pubDate>Thu, 04 Jan 2007 17:47:00 +0000</pubDate><atom:updated>2007-01-04T10:22:07.847-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">AppDomain</category><category domain="http://www.blogger.com/atom/ns#">Deployment</category><category domain="http://www.blogger.com/atom/ns#">Exception</category><title>A Deployment Issue: &quot;the application has generated an exception that could not be handled.&quot;</title><description>&lt;span style=&quot;color:#006600;&quot;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;I built an application on my computer using .NET Framework 1.1, and it worked. This was a complicated application with a bunch of exe and dll files, app.config files, xml files. I had a server, clients, an MS-Access mdb file. I had GUI libraries, and Data access libraries.&lt;br /&gt;&lt;br /&gt;I supplied an installation file, that installed .NET Framework 1.1, MDAC 2.8, my application, and several MFC files. It worked on other machines as well.&lt;br /&gt;&lt;br /&gt;Then I had an update. I created a new installation file and shiped it to another computer.&lt;br /&gt;One of the .NET applications crashed, and gave me the very intimidating MessageBox: &lt;strong&gt;&quot;the application has generated an exception that could not be handled. Process id=0xFFFF Thread id=0xFFFF. Press OK to terminate the application, and CANCEL to debug the application.&quot;.&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;Of course I had no debugger on that machine, and basically I was stuck!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;First Tries&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;At first I thought it was a .NET Framework installation issue. So I uninstalled .NET Framework on the other computer, and re-installed it. It did not solve the issue. I checked the versions of the .NET Framework (using Administrative Tools), and it was the same as in my computer.&lt;br /&gt;&lt;br /&gt;Second, I checked that the App.Config file existed oin the folder of installation. It was there.&lt;br /&gt;&lt;br /&gt;Third, I installed Dependency Walker on the other computer, open my exe with it (no red signs appeared), pressed F7, but could not make sense of the output. It did not smell like a problem with dependencies or other dlls.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I added to the application the following code (I made it start from the Main method):&lt;br /&gt;&lt;code&gt;-------------------------------------------------&lt;br /&gt;&amp;lt;STAThread()&amp;gt; _&lt;br /&gt;Public Shared Sub Main()&lt;br /&gt;Dim currentDomain As AppDomain = AppDomain.CurrentDomain&lt;br /&gt;AddHandler currentDomain.UnhandledException, AddressOf UnhandledExceptionOccured&lt;br /&gt;Application.Run(New Form1)&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Public Shared Sub UnhandledExceptionOccured(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)&lt;br /&gt;Dim ex As Exception = CType(e.ExceptionObject, Exception)&lt;br /&gt;MessageBox.Show(ex.Message &amp; vbCrLf &amp;amp; ex.StackTrace)&lt;br /&gt;End Sub&lt;br /&gt;-------------------------------------------------&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This catches any Unhandled Exceptions, and shows a message with the stack trace.&lt;br /&gt;&lt;br /&gt;I compiled and installed this exe in the client&#39;s computer.&lt;br /&gt;&lt;br /&gt;I was amazed: There was a bug involving DBNull in one of my dlls, a bug that I have just added to my application. I could have never guessed that the problem was in that place!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;My Lesson&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;From now on I put this code in all my applications! When there is such a bug I must at least know what happened!&lt;br /&gt;&lt;br /&gt;I advice you to do the same.</description><link>http://j1hammer.blogspot.com/2007/01/deployment-issue-application-has.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-115739142078038136</guid><pubDate>Mon, 04 Sep 2006 17:26:00 +0000</pubDate><atom:updated>2006-09-04T11:47:25.033-07:00</atom:updated><title>Creating a general delegate using Generics</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I recently worked on a heavy UI application, with a client and a server. The thing that I encountered is that I often tried to access the UI from the thread of the remoting instead of the thread of the GUI. Of course I recieved an exception for that, because you must handle all the controls and graphics from the GUI thread.&lt;br /&gt;&lt;br /&gt;This made me program a lot of code like this:&lt;br /&gt;------------------------------------------------------&lt;code&gt;&lt;br /&gt;if (myControl.InvokeRequired)&lt;br /&gt;myControl.Invoke(new SomeDelegate(SomeUIMethod),new object[] {parameterToPass});&lt;br /&gt;else&lt;br /&gt;SomeUIMethod(parameterToPass);&lt;br /&gt;&lt;/code&gt;------------------------------------------------------&lt;br /&gt;Of course I had to create both the method that handles the UI, and the delegate that accept the exact same parameters as the method.&lt;br /&gt;&lt;br /&gt;This created a very messy code. The above code appeared in many places, and I had to create many small methods to handle small operations in the UI, and a delegate fro each method.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;A Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A very pretty solution was suggested to me by wildfrog in CodeGuru (&lt;a href=&quot;http://www.codeguru.com/forum/showthread.php?t=398518&quot;&gt;here&lt;/a&gt;), and I must put it in my blog.&lt;br /&gt;He suggested to use generics in order to eliminate the need to use delegates for each method.&lt;br /&gt;Here is his solution:&lt;br /&gt;---------------------------------------------------------&lt;code&gt; &lt;/code&gt;&lt;br /&gt;&lt;code&gt;public delegate void GeneralDelegate();&lt;/code&gt;&lt;br /&gt;&lt;code&gt;public delegate void GeneralDelegate&amp;lt;T1&amp;gt;(T1 t1);&lt;br /&gt;public delegate R GeneralDelegate&amp;lt;R, T1&amp;gt;(T1 t1); &lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;public delegate R GeneralDelegate&amp;lt;R, T1, T2&amp;gt;(T1 t1, T2 t2); &lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;---------------------------------------------------------&lt;br /&gt;And so on you can continue to create overloads for the delegate. You must create different delegates if you need to use void as return value. And the compiler will shout at you if you do not declare an appropriate delegate.&lt;br /&gt;&lt;br /&gt;In order to use this GeneralDelegate, I write for example:&lt;br /&gt;--------------------------------------------------------&lt;code&gt;&lt;br /&gt;public char test(bool b, int i)&lt;br /&gt;{&lt;br /&gt;return &#39;c&#39;;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;new GeneralDelegate&amp;lt;char, bool, int&amp;gt;(test);&lt;br /&gt;&lt;/code&gt;---------------------------------------------------------&lt;br /&gt;&lt;br /&gt;This is absolutely great, and saves me a lot of coding.&lt;br /&gt;I must emphasize that I &lt;strong&gt;only&lt;/strong&gt; use this when I need to invoke a small method, and I do not use this in all the places I need a delegate (in fact I try to use this as little as possible, just when I feel that putting the typed delegate seems unnecessary).</description><link>http://j1hammer.blogspot.com/2006/09/creating-general-delegate-using.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-115687895180516961</guid><pubDate>Tue, 29 Aug 2006 19:09:00 +0000</pubDate><atom:updated>2006-08-29T12:15:51.863-07:00</atom:updated><title>Generating a DataSet From an Xml Schema in 2005</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In Visual Studio 2003 the DataSet designer and the Xml Schema designer were the same. In order to generate a Typed DataSet from an Xml Schema, you had to right-click on the Xml Schema designer, and check the Generate DataSet option. Everytime you saved the schema, the DataSet was redrawn for you.&lt;br /&gt;&lt;br /&gt;In Visual Studio 2005 we have two seperate designer - one for Typed DataSets and one for Xml Schemas. The option to generate a Typed DataSet from an Xml Schema disappeared from the menus.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;How To Generate the DataSet&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In the Xml Schema designer, right-click on the designer surface and select Properties (or press F4).&lt;br /&gt;At the Property Grid find the CustomTool property and type &lt;strong&gt;MSDataSetGenerator&lt;/strong&gt;.&lt;br /&gt;The typed dataset will be generated for you when you save the schema. You can see the files in the Solution Explorer.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Thoughts&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;I have no idea why Microsoft removed the option to generate the DataSet from the menu. The new interface for it is very uncomfortable in my opinion.&lt;/span&gt;</description><link>http://j1hammer.blogspot.com/2006/08/generating-dataset-from-xml-schema-in.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-115679437056031271</guid><pubDate>Mon, 28 Aug 2006 19:35:00 +0000</pubDate><atom:updated>2006-08-28T12:46:18.116-07:00</atom:updated><title>Loading the Entire Database Into a DataSet, Including Relations</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Ironically, it is hard to load the entire database into a dataset. You either need to use many command objects, or TableAdapters/DataAdapters.&lt;br /&gt;The ralations are even harder to get from the database. You need to manually create them on the DataSet.&lt;br /&gt;&lt;br /&gt;In this article I give code to load the entire MS-Access database into an Untyped DataSet, with the relations. You can easily adapt this to SQL Server database. The code is in VB.NET.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Code&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;----------------------------------------------&lt;code&gt;&lt;br /&gt;Function getDataSetAndFill(ByRef connection As OleDb.OleDbConnection, Optional ByVal isExportSchema As Boolean = True) As DataSet       &lt;br /&gt;Dim myDataSet As New DataSet       &lt;br /&gt;Dim myCommand As New OleDb.OleDbCommand      &lt;br /&gt; Dim myAdapter As New OleDb.OleDbDataAdapter       &lt;br /&gt;myCommand.Connection = connection       &lt;br /&gt;&#39;Get Database Tables       &lt;br /&gt;Dim tables As DataTable = connection.&lt;em&gt;GetOleDbSchemaTable&lt;/em&gt;( System.Data.OleDb.OleDbSchemaGuid.Tables,   New Object() {Nothing, Nothing, Nothing, &quot;TABLE&quot;})       &lt;br /&gt;&#39;iterate through all tables       &lt;br /&gt;Dim table As DataRow       &lt;br /&gt;For Each table In tables.Rows          &lt;br /&gt; &#39;get current table&#39;s name           &lt;br /&gt;Dim tableName As String = table(&quot;TABLE_NAME&quot;)           &lt;br /&gt;Dim strSQL = &quot;SELECT * FROM &quot; &amp; &quot;[&quot; &amp;amp; tableName &amp; &quot;]&quot;           &lt;br /&gt;Dim adapter1 As New OleDb.OleDbDataAdapter(New OleDb.OleDbCommand(strSQL, connection)) &lt;br /&gt;adapter1.FillSchema(myDataSet, SchemaType.Source, tableName)&lt;br /&gt;           &#39;Fill the table in the dataset           &lt;br /&gt;myCommand.CommandText = strSQL           &lt;br /&gt;myAdapter.SelectCommand = myCommand           &lt;br /&gt;myAdapter.Fill(myDataSet, tableName)       &lt;br /&gt;Next       &lt;br /&gt;&lt;br /&gt;&#39;Add relationships to dataset&lt;br /&gt;&#39;First, get relationships names from database (as well as parent table and child table names)       &lt;br /&gt;Dim namesQuery As String = &quot;SELECT DISTINCT szRelationship, szReferencedObject, szObject FROM MSysRelationships&quot;       &lt;br /&gt;Dim namesCommand As New System.Data.OleDb.OleDbCommand(namesQuery, connection)       &lt;br /&gt;Dim namesAdapter As New System.Data.OleDb.OleDbDataAdapter(namesCommand)       &lt;br /&gt;Dim namesDataTable As New DataTable       &lt;br /&gt;mesAdapter.Fill(namesDataTable)       &lt;br /&gt;&#39;Now, get MSysRelationship from database       &lt;br /&gt;Dim relationsQuery As String = &quot;SELECT * FROM MSysRelationships&quot;      &lt;br /&gt;Dim command As New System.Data.OleDb.OleDbCommand(relationsQuery, connection)       &lt;br /&gt;Dim adapter As New System.Data.OleDb.OleDbDataAdapter(command)       &lt;br /&gt;Dim relationsDataTable As New DataTable       &lt;br /&gt;adapter.Fill(relationsDataTable)       &lt;br /&gt;Dim relationsView As DataView = relationsDataTable.DefaultView       &lt;br /&gt;Dim relationName As String       &lt;br /&gt;Dim parentTableName As String       &lt;br /&gt;Dim childTablename As String       &lt;br /&gt;Dim row As DataRow       &lt;br /&gt;For Each relation As DataRow In namesDataTable.Rows           &lt;br /&gt;relationName = relation(&quot;szRelationship&quot;)           &lt;br /&gt;parentTableName = relation(&quot;szReferencedObject&quot;)           &lt;br /&gt;childTablename = relation(&quot;szObject&quot;)          &lt;br /&gt;&#39;Keep only the record of the current relationship           &lt;br /&gt;relationsView.RowFilter = &quot;szRelationship = &#39;&quot; &amp; relationName &amp;amp; &quot;&#39;&quot;           &lt;br /&gt;&#39;Declare two arrays for parent and child columns arguments           &lt;br /&gt;Dim parentColumns(relationsView.Count - 1) As DataColumn           &lt;br /&gt;Dim childColumns(relationsView.Count - 1) As DataColumn           &lt;br /&gt;For i As Integer = 0 To relationsView.Count - 1               &lt;br /&gt;parentColumns(i) = myDataSet.Tables(parentTableName). Columns(relationsView.Item(i)(&quot;szReferencedColumn&quot;))&lt;br /&gt;childColumns(i) = myDataSet.Tables(childTablename). Columns(relationsView.Item(i)(&quot;szColumn&quot;))&lt;br /&gt;Next           &lt;br /&gt;Dim newRelation As New DataRelation(relationName, parentColumns, childColumns, False)           &lt;br /&gt;myDataSet.Relations.Add(newRelation)       &lt;br /&gt;Next       &lt;br /&gt;If isExportSchema Then           &lt;br /&gt;Dim schemaName = GetXmlSchemaFileName()           &lt;br /&gt;If File.Exists(schemaName) Then File.SetAttributes(schemaName, FileAttributes.Normal)&lt;br /&gt;myDataSet.WriteXmlSchema(schemaName)       &lt;br /&gt;End If       &lt;br /&gt;Return myDataSet   &lt;br /&gt;End Function&lt;br /&gt;&lt;/code&gt;------------------------------------------</description><link>http://j1hammer.blogspot.com/2006/08/loading-entire-database-into-dataset.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-115679148362841003</guid><pubDate>Mon, 28 Aug 2006 18:40:00 +0000</pubDate><atom:updated>2006-08-28T12:20:19.600-07:00</atom:updated><title>Loading a class dynamically</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;When you write software that can be extended, you have to allow othe rprogrammers to write their own classes, and then load those classes to your application and use them.&lt;br /&gt;For example, let&#39;s assume you program a graphical tool, and you prvide square, circle and triangle shapes. You want other users to provide any other shapes, so you let them program the shapes in their own class library and then you load the shapes into your application.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Loading classes from different assemblies&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The line of code needed to load a class that you don&#39;t know is:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;object objectFromOtherDll = Activator.CreateInstance (&quot;&lt;em&gt;assemblyName&lt;/em&gt;&quot;, &quot;&lt;em&gt;Namespace&lt;/em&gt;.&lt;em&gt;DllClass&lt;/em&gt;&quot;).Unwrap(); &lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;--------------------------------------------------&lt;br /&gt;&lt;em&gt;assemblyName&lt;/em&gt; - The name of the class library where the class is stored (without &#39;.dll&#39;).&lt;br /&gt;&lt;em&gt;Namespace&lt;/em&gt; - The full name of the namespace.&lt;br /&gt;&lt;em&gt;DllClass&lt;/em&gt; - The name of the class you want to load.&lt;br /&gt;&lt;br /&gt;Note - A file with the name &lt;em&gt;assemblyName&lt;/em&gt;.dll &lt;strong&gt;must&lt;/strong&gt; be placed in the same folder as the executable.&lt;br /&gt;&lt;em&gt;Activator&lt;/em&gt; is a class in the System namespace. &lt;em&gt;CreateInstance&lt;/em&gt; has other overloads. You might want to look in &lt;a href=&quot;http://msdn2.microsoft.com/en-us/library/system.activator.createinstance.aspx&quot;&gt;http://msdn2.microsoft.com/en-us/library/system.activator.createinstance.aspx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;But how can we know which assembly did the user provide, and what class name to use? As you saw those are strings that you need to put in your code, so it&#39;s definitely an issue. The best way is to place them in the App.Config file (or other configuration file). The programmer who supply the extension should put the names of the classes, with their namespaces and assembly names in the configuration file that you provide for your application. You will need to read the configuration file (should be easy with the System.Configuration namespace), and load the classes that were provided in the configuration.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Invoking a method of the unknown class&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Now we have an object that we know nothing about. We want to call a method of the object. We must know the name of the method. In the example of Shape, we have the Draw method.&lt;br /&gt;We can use reflection to invoke the method. First we need a using directive to the System.Reflection namespace.&lt;br /&gt;Then we need to invoke the method like this:&lt;br /&gt;--------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;em&gt;objectFromOtherDll&lt;/em&gt;.&lt;strong&gt;GetType().InvokeMember&lt;/strong&gt;(&quot;&lt;em&gt;methodName&lt;/em&gt;&quot;, BindingFlags.DeclaredOnly BindingFlags.Public BindingFlags.NonPublic BindingFlags.Instance BindingFlags.InvokeMethod, null, &lt;em&gt;objectFromOtherDll&lt;/em&gt;, null);&lt;br /&gt;&lt;/code&gt;------------------------------------------------&lt;br /&gt;More information about &lt;em&gt;InvokeMember&lt;/em&gt; can be found here: &lt;a href=&quot;http://msdn2.microsoft.com/en-us/library/de3dhzwy.aspx&quot;&gt;http://msdn2.microsoft.com/en-us/library/de3dhzwy.aspx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;objectFromOtherDll&lt;/em&gt; is the object we created with Activator. It appears twice in the statement (once as a parameter) so pay attention.&lt;br /&gt;&lt;em&gt;methodName&lt;/em&gt; is a string containing the name of the method to invoke.&lt;br /&gt;&lt;br /&gt;How do we know the method name? We could use a configuration file.&lt;br /&gt;How do we know that there is a method called &lt;em&gt;methodName&lt;/em&gt;? For example, the programmer might not have implemented the &quot;Draw&quot; method of the Shape class. We could know that by catching exception, or by using reflection to find a method called &quot;Draw&quot;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Using Interfaces&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A better way, in my opinion, is to use interfaces instead of reflection. The idea is that you supply a class library with all the interface that you require the programmer to supply to you. The programmer must implement the interface if he wants his object to be loaded in your framework.&lt;br /&gt;&lt;br /&gt;For example, in a class library called Interfaces, I add an interface called &lt;em&gt;IShape&lt;/em&gt;.&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;public interface IShape&lt;br /&gt;{&lt;br /&gt;void Draw();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------&lt;br /&gt;&lt;br /&gt;The programmer need to refernce to the Interface.dll you gave him, and each Shape must implement the IShape interface:&lt;br /&gt;-----------------------------------------&lt;code&gt;&lt;br /&gt;public class Ellipse:IShape&lt;br /&gt;{&lt;br /&gt;public void Draw()&lt;br /&gt;{&lt;br /&gt;//Do Whatever&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------&lt;br /&gt;&lt;br /&gt;The programmer will put in your configuration file the class Ellipse, and place his assembly in the folder of your application.&lt;br /&gt;&lt;br /&gt;You will load his class in the following way:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;IShape shape = Activator.CreateInstance(&quot;assemblyName&quot;, &quot;CustomShapes.Ellipse&quot;).Unwrap() &lt;em&gt;as IShape&lt;/em&gt;; &lt;/code&gt;&lt;br /&gt;&lt;code&gt;if (IShape == null) throw new Exception(&quot;Illegal class&quot;);&lt;/code&gt;&lt;br /&gt;&lt;code&gt;shape .Draw(); //No reflection needed&lt;br /&gt;&lt;/code&gt;--------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I showed how to use Activator to load classes that you don&#39;t know about in advance.&lt;br /&gt;I showed how to use reflection in order to invoke a member.&lt;br /&gt;I showed how to use interfaces to help the programmer supply the right code for you.</description><link>http://j1hammer.blogspot.com/2006/08/loading-class-dynamically.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114140421198566084</guid><pubDate>Fri, 03 Mar 2006 15:49:00 +0000</pubDate><atom:updated>2006-03-04T02:24:07.610-08:00</atom:updated><title>MultiThreading Without The Thread Class</title><description>&lt;span style=&quot;color:#006600;&quot;&gt;&lt;span style=&quot;color:#000000;&quot;&gt;In this serie of articles I show a lot of the mechanisms we have in .NET to program MultiThreaded applications and to perform asynchronous operations.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Here are the articles in this serie:&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/asynchronous-programming-in-net.html&quot;&gt;Asynchronous Programming in .NET&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/synchronizing-threads-in-net.html&quot;&gt;Synchronizing Threads in .NET &lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/multithreading-without-thread-class.html&quot;&gt;MultiThreading Without The Thread Class&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I will show several mechanisms of using threads without actually using the Thread class.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Using Timers&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;There are three types of Timers in the .NET frmawork. System.Windows.Forms.Timer is used in UI forms, and works on Windows messages. This is the least efficient timer.&lt;br /&gt;The System.Timers.Timer and the System.Threading.Timer both work with the Windows timer, and they are quite similar in performance. The System.Timers.Timer is more user-friendly, because it has a Start and Stop methods, an Interval property and so on.&lt;br /&gt;&lt;br /&gt;The System.Threading.Timer will invoke a method at a constant rate.&lt;br /&gt;------------------------------------------&lt;code&gt;&lt;br /&gt;public void myMethod(object obj)&lt;br /&gt;{&lt;br /&gt;//Do something&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void Main()&lt;br /&gt;{&lt;br /&gt;Timer t = new Timer(new TimerCallBack(myMethod), anyObject, 0, 1000);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------&lt;br /&gt;The timer can work on any method that receives an object. The second parameter will be passed to the method as a parameter (pass null if you don&#39;t need it). The Thirs argument is how much time (in milliseconds) to wait before starting the timer, and the last parameter is the period of time between to invokations.&lt;br /&gt;The threads are taken out of the Thread Pool, and they run in the background.&lt;br /&gt;When you are done ith the timer, Dispose it.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Using the ThreadPool&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;If you need to perform a lot of short tasks (a worker thread for example), and then get rid of the threads you can use the ThreadPool to improve performance. When you start your application Windows generates 25 threads for you to use (Actually sometimes, as in timers, the CLR will use threads from the pool). The number of threads in the pool cannot be configured in 2003, so be careful not to use too many threads. If you use a thread from the ThreadPool you save the time of creating and destroying the thread. You just ask for a free thread, and when you are done it goes back to the pool.&lt;br /&gt;&lt;br /&gt;Whenever you need to call the QueueUserWorkItem static method to start a thread from the thread pool.&lt;br /&gt;For example:&lt;br /&gt;------------------------------------------&lt;code&gt;&lt;br /&gt;public void myMethod(object obj)&lt;br /&gt;{&lt;br /&gt;//Do something&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void Main()&lt;br /&gt;{&lt;br /&gt;ThreadPool.QueueUserWorkItem(new WaitCallback(myMethod), anyObject);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------&lt;br /&gt;Like in the timer, you should pass a method that receives an object, and that object as second parameter.&lt;br /&gt;For another example look at &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemthreadingmutexclasstopic.asp&quot;&gt;MSDN&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Those threads are intended for short actions, that run in the backgroound, with normal priority (you cannot change any of it). You might want to pass a ManualResetEvent as the parameter to the method if you want to be able to synchronize the thread.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Asynchronous Delegates&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A reminder about the usage of delegates:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;class Class1&lt;br /&gt;{&lt;br /&gt;public static void f1(int x)&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;x; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(300);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public delegate void Fptr(int x);&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;Fptr p1 = new Fptr(f1);&lt;br /&gt;p1(50);&lt;br /&gt;&lt;/strong&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;----------------------------------------------------&lt;br /&gt;You call the delegate as you call a normal method in order to invoke it. This is called Syncronous Invocations.&lt;br /&gt;&lt;br /&gt;Instead we could call the BeginInvoke method of the delegate:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Fptr p1 = new Fptr(f1);&lt;br /&gt;p1.BeginInvoke(50,null,null);&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;----------------------------------------------------&lt;br /&gt;You will see that the Main End will ve written as the program starts, to indicate that f1 runs on a different thread. This thread is taken from the Thread Pool.&lt;br /&gt;&lt;br /&gt;If we want to return a value from the method f1, we will need to use EndInvoke:&lt;br /&gt;-------------------------------------------------------&lt;code&gt;&lt;br /&gt;public static &lt;strong&gt;int&lt;/strong&gt; f1(int x)&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;x; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(100);&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;return x;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;public delegate &lt;strong&gt;int&lt;/strong&gt; Fptr(int x);&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Fptr p1 = new Fptr(f1);&lt;br /&gt;&lt;strong&gt;IAsyncResult ar =&lt;/strong&gt; p1.BeginInvoke(50, null, null);&lt;br /&gt;Console.WriteLine(&quot;Main Sleeping&quot;);&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;&lt;strong&gt;int res = p1.EndInvoke(ar);&lt;br /&gt;&lt;/strong&gt;Console.WriteLine(&quot;Main End &quot; + res);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------&lt;br /&gt;The method f1 now returns an int. When we call BeginInvoke we are returned with an object of type IAsyncResult. We pass that object to the EndInvoke method and then we wait until the method f1 is done (If it is over when we get to the line we don&#39;t wait at all), and we receive the return value. The use of IAsyncResult is because we may start p1 many times, and the EndInvoke should know which time we want to end. The IAsyncResult uniquly identifies the invocation.&lt;br /&gt;Note that EndInvoke blocked the code. A nice propery of IAsyncResult is the IsComplete property which can be called in a loop to know if the method is over (A polling approach).&lt;br /&gt;&lt;br /&gt;Another approach might be to receive an event when the method is done. This is the best approach because the code will never be blocked.&lt;br /&gt;For example:&lt;br /&gt;------------------------------------------&lt;code&gt;&lt;br /&gt;public static&lt;strong&gt; void f1IsCompleted(IAsyncResult ar)&lt;br /&gt;&lt;/strong&gt;{&lt;br /&gt;Console.WriteLine(&quot;F1 is done&quot;);&lt;br /&gt;}&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Fptr p1 = new Fptr(f1);&lt;br /&gt;p1.BeginInvoke(50, &lt;strong&gt;new AsyncCallback(f1IsCompleted),&lt;/strong&gt; null);&lt;br /&gt;Console.WriteLine(&quot;Main Sleeping&quot;);&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------------------&lt;br /&gt;The method f1IsCompleted receives the IAsyncResult as a parameter. It is called automatically when the f1 method is completed.&lt;br /&gt;&lt;br /&gt;The last thing to see is how to retrieve the return value of the method f1. Here is one way to do it:&lt;br /&gt;----------------------------------------------------------&lt;code&gt;&lt;br /&gt;public static void f1IsCompleted(IAsyncResult ar)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;int res = ((Fptr)ar.AsyncState).EndInvoke(ar);&lt;br /&gt;&lt;/strong&gt;Console.WriteLine(&quot;F1 is done: &quot; + res);&lt;br /&gt;}&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Fptr p1 = new Fptr(f1);&lt;br /&gt;p1.BeginInvoke(50, new AsyncCallback(f1IsCompleted), &lt;strong&gt;p1&lt;/strong&gt;);&lt;br /&gt;Console.WriteLine(&quot;Main Sleeping&quot;);&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;The last parameter of BeginInvoke can be any object. This object can be retrieved as the IAsyncResult.ASyncState property in the callback method. We use this mechanism to pass the delegate, in order to call the EndInvoke after the callback is raised.&lt;br /&gt;Another way to do it was to place the Fptr p1 as a member of the class, so the f1IsCompleted method can access it.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Using Threads With User Interface&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;Here is a rule: Never access the GUI from any thread other than the GUI thread.&lt;/span&gt;&lt;br /&gt;Lets have an axample: We have a form with a ListBox and a Button. We write the following Click event handler for the Button:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;private void button1_Click(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10000 ; i++)&lt;br /&gt;listBox1.Items.Add(i);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;If you run the program and press the button you will see that the form gets stuck until the ListBox is filled with numbers. You might even get a (not responding) in the title of the form.&lt;br /&gt;The reason for this is that if you perform a long task in the event handlers of the form, you are performing it in the GUI thread. No Windows messages can be handled when the GUI thread is working on something else. For that reason you should only perform short taskd in event handlers. Long tasks should be performed in different threads.&lt;br /&gt;&lt;br /&gt;Lets look at the following fix:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;private void GenerateNumbers()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10000 ; i++)&lt;br /&gt;listBox1.Items.Add(i);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void button1_Click(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;Thread t = new Thread(new ThreadStart(GenerateNumbers));&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;t.Start();&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;If you run the program and press on the button you get a respons from the form while the ListBox is filled.&lt;br /&gt;You should never do it this way!!! We are accessing the GUI from different thread. In 2005 you will even get an exception.&lt;br /&gt;&lt;br /&gt;The correct way of doing this is by calling the Invoke method of the form. This will call a method in the GUI thread:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;private void GenerateNumbers()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10000 ; i++)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;this.Invoke(new Fptr(InsertNumberToList), new object[] {i});&lt;/strong&gt;&lt;br /&gt;Thread.Sleep(10);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public delegate void Fptr(int n);&lt;br /&gt;private void InsertNumberToList(int n)&lt;br /&gt;{&lt;br /&gt;listBox1.Items.Add(i);&lt;br /&gt;}&lt;br /&gt;private void button1_Click(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;Thread t = new Thread(new ThreadStart(GenerateNumbers));&lt;br /&gt;t.Start();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;The Invoke method receives a delegate (any delegate) and an array of parameters to pass to the delegate. We are guaranteed that the method will be executed in the GUI thread. However, without the Sleep call the GUI would be frozen, because the loop constantly updates the GUI. The Sleep will make sure that the user gets enough time to interact with the form while the loop is running.&lt;br /&gt;&lt;br /&gt;If you run the program you will notice that the ListBox is updating much slower than before. The Sleep we placed is the cause of that. This is the tradeoff we get by performing long tasks which interacts with the UI: Good response from the UI vs. Time it takes to complete the task.&lt;br /&gt;&lt;br /&gt;By the way, this method is useful while updating ProgressBars from worker threads.&lt;br /&gt;&lt;br /&gt;In 2005 we have a BackGroundWorker control that we can drag to the component tray of the form. This control can manage tasks in backgorund threads for us in a much more user-friendly way.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I showed how to perform asyncronous operations in a MultiThreaded environment without actually using the Thread class. I showed how to use timers, how to call asyncronous delegates, how to call Invoke in UI applications, and how to use the ThreadPool.</description><link>http://j1hammer.blogspot.com/2006/03/multithreading-without-thread-class.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114139508490791219</guid><pubDate>Fri, 03 Mar 2006 13:58:00 +0000</pubDate><atom:updated>2006-03-04T02:23:31.340-08:00</atom:updated><title>Synchronizing Threads in .NET</title><description>&lt;span style=&quot;color:#006600;&quot;&gt;&lt;span style=&quot;color:#000000;&quot;&gt;In this serie of articles I show a lot of the mechanisms we have in .NET to program MultiThreaded applications and to perform asynchronous operations.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Here are the articles in this serie:&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/asynchronous-programming-in-net.html&quot;&gt;Asynchronous Programming in .NET&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/synchronizing-threads-in-net.html&quot;&gt;Synchronizing Threads in .NET &lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/multithreading-without-thread-class.html&quot;&gt;MultiThreading Without The Thread Class&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In my previous article I showed how to start and control a thread. In this article I will show how to synchronize threads.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Thread Local Storage&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Lets look at the following example:&lt;br /&gt;---------------------------------------&lt;code&gt;&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;Delay = 300;&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + Delay);&lt;br /&gt;Thread.Sleep(Delay);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public void f2()&lt;br /&gt;{&lt;br /&gt;Delay = 500;&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f2: &quot; + Delay);&lt;br /&gt;Thread.Sleep(Delay);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public static int Delay = 500;&lt;br /&gt;}&lt;br /&gt;class Class1&lt;br /&gt;{&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;A a = new A();&lt;br /&gt;Thread t1 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f2));&lt;br /&gt;t1.Start();&lt;br /&gt;t2.Start();&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;Delay is a static member of A. If you will run this you will see that the last thread to change Delay will change it both for f1 and f2. The thing is that both threads share the same member, and the last one to change it wins it all.&lt;br /&gt;&lt;br /&gt;What we want really is to have one Delay for the first thread, and another Delay for the second thread. There is a built-in mechanism for that in Win32 called Thread Local Storage, and the way to do it in .NET is by using the [ThreadStatic] attribute before declaring the static member.&lt;br /&gt;&lt;code&gt;[ThreadStatic] public static int Delay = 500; &lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As you can see we initialized the Delay member with a value. This initialization only applies to the main thread. Each thread must set a value in the entry point method, or the value will be 0.&lt;br /&gt;If you run the program now you will see that each thread has its own delay.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Synchronization Mechanisms&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Two threads want to access the same variable, or the same code. Context switches can occur at any time, so if the context switch occur in a critical point, the threads can cause unexpected results. An example could be when a thread tries to decrease a variable by one, so it places it in a register, and at that point a context switch occur, another thread decreases the variable, if a context switch occurs again the variable will be decreased only once instead of twich.&lt;br /&gt;&lt;br /&gt;There are declerative ways and coding ways to perform synchronization in .NET.&lt;br /&gt;The WaitHandle class is an abstract class which provides a way to signal other threads. Three classes derive from the WaitHandle class: ManualResetEvent, AutoResetEvent and Mutex.&lt;br /&gt;The &lt;em&gt;AutoResetEvent&lt;/em&gt; class involves cases in which we want to make sure that threads will sleep until another thread finishes a task. It has two states: Set and Reset. The Set state is signalling one thread to stop waiting, and then automatically goes back to Reset state. A thread can call the WaitOne method of the AutoResetEvent object, and then it is blocked until someone calls the Set method.&lt;br /&gt;Here is an &lt;a href=&quot;http://www.bna.co.il/dotnet/code/wait.cs.txt&quot;&gt;example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;em&gt;ManualResetEvent&lt;/em&gt; is similar to AutoResetEvent. When a threads wants to block all other threads it calls the Reset method. If any other thread calls the WaitOne it will be blocked until someone calls the Set method. All the threads will be released, and the state will remain Set, until someone sets the state again to Reset.&lt;br /&gt;&lt;br /&gt;The Mutex class is used when we want to protect a resource or a critical code. We call the WaitOne at the beginning of the section, and the ReleaseMutex at the end of the section. If one thread called the WaitOne method, other threads will be blocked on that line until the thread calls the ReleaseMutex class. You should perform the ReleaseMutex operation in the finally clause. For a short and nice example look at &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemthreadingmutexclasstopic.asp&quot;&gt;MSDN&lt;/a&gt; .&lt;br /&gt;In 2005 a Semaphore class was added to the framework. There are also named mutexes, and there is an Access Control List feature.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Race Conditions&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The class &lt;em&gt;Interlocked&lt;/em&gt; provides methods to perform operations as atomic. This will prevent race conditions between threads on variables. In the 2003 version this class provides Increment, Decrement,Exchange (switches two variables), and CompareExchange. In the 2005 version other features were added.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Using Attributes to Synchronize Classes&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A class that derives from ContextBoundObject (such as ServicedComponent) can be assigned with the [Synchronization] attribute. No more than one thread will be able to access the class methods and properties. This will not block static methods and members. This is a very easy but strict mechanism. It is not suitable in many cases, for example when there is a Read and Write method. Only one thread can access the Write method, but few threads can access the Read method. This will not work with this attribute.&lt;br /&gt;The class is in the System.Runtime.Remoting.Contexts namespace.&lt;br /&gt;&lt;br /&gt;There is a way to protect a method from getting accessed bymore than one thread. Add the following attribute to the method [MethodImpl(MethodImplOptions.Synchronized)].&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Locks&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;C# has a built-in locking mechanism. The lock is per object, so if you want to prevent two threads from executing critical sections together, they should lock on the same object:&lt;br /&gt;---------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;lock(x)&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(300);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public void f2()&lt;br /&gt;{&lt;br /&gt;lock(x)&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f2: &quot; + i);&lt;br /&gt;Thread.Sleep(500);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;object x = new object();&lt;br /&gt;&lt;/code&gt;--------------------------------------------&lt;br /&gt;Only one method can perform the loop at the same time. The ither thread will have to wait until the lock is freed. You can lock on any type of object.&lt;br /&gt;&lt;br /&gt;Behind the scenes .NET uses the class Monitor.&lt;br /&gt;If you want to lock a critical section in the context of the object, you can do &lt;code&gt;&lt;strong&gt;lock(this)&lt;/strong&gt;&lt;/code&gt;.&lt;br /&gt;This will prevent two threads from accessing the critical section only when accessing the same object.&lt;br /&gt;&lt;br /&gt;If you want to lock a section in a static method, you might use &lt;code&gt;&lt;strong&gt;lock(typeof(MyClass))&lt;/strong&gt; &lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;A nice class to perform read and write operations is the ReaderWriterLock, which lets aquiring a number of reader locks, and only one writer lock.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I showed a number of ways to synchronize threads using Locks, Mutex, Manual/AutoResetEvent, and attributes. I also showed how to use the Thread Local Storage.&lt;br /&gt;&lt;br /&gt;In the next article I will discuss timers, ThreadPool, and Asynchronous delegates.</description><link>http://j1hammer.blogspot.com/2006/03/synchronizing-threads-in-net.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114133307199055511</guid><pubDate>Thu, 02 Mar 2006 20:06:00 +0000</pubDate><atom:updated>2006-03-04T02:22:09.586-08:00</atom:updated><title>Asynchronous Programming in .NET</title><description>&lt;span style=&quot;color:#006600;&quot;&gt;&lt;span style=&quot;color:#000000;&quot;&gt;In this serie of articles I show a lot of the mechanisms we have in .NET to program MultiThreaded applications and to perform asynchronous operations.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Here are the articles in this serie:&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/asynchronous-programming-in-net.html&quot;&gt;Asynchronous Programming in .NET&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/synchronizing-threads-in-net.html&quot;&gt;Synchronizing Threads in .NET &lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/03/multithreading-without-thread-class.html&quot;&gt;MultiThreading Without The Thread Class&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Here are four important classes regrading the mangement of processes and threads in the operating system:&lt;br /&gt;- AppDomain - A class that represent a .NET process, in the context of the CLR. Each .NET application is opened as an AppDomain, and later on can open several other AppDomains.&lt;br /&gt;- Thread - A class that represents a managed thread.&lt;br /&gt;- Process - A class in the System.Diagnostics namespace that represents the process from the Win32 point of view. It is mainly for monitoring process information.&lt;br /&gt;- ProcessThread - A class that represents a thread from Win32 point of view, and is used to monitor thread information.&lt;br /&gt;Each .NET application starts 9 thread normally on startup. Only one of them is the managed thread where your code runs. A .NET thread is a Win32 thread, but the other way is not true always. The following line will show you the exact number of threads in your application:&lt;br /&gt;&lt;code&gt;Console.WriteLine(Process.GetCurrentProcess().Threads.Count)&lt;/code&gt;&lt;br /&gt;You use the Process class in order to start new processes (by calling the Start method) or to receive information about other processes. The Threads property return a collection of ProcessThread objects. The mthod EnterDebugMode for example can perform the same operation as the TaskManager. ProcessThread for example can set the Processor Affinity.&lt;br /&gt;&lt;br /&gt;AppDomain contains several properties regarding the .NET running assembly, and enables to communicate between several AppDomains. We use AppDomains when we want to wrap the environment with security. It is a virtual environment to which we load assemblies and run them. We do not need to worry about releasing resources on exit (The CLR takes care of that for us). We pay with performance of copying data between the AppDomains.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Thread Class&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The Thread class contains static and member methods. We use it when we want to do things in parallel or asynchronously. The threads, unlike AppDomains, share a memory space and resources. Each thread has an entry point, which is a method without parameters which returns no value.&lt;br /&gt;Here is a small example to demonstrate:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10;i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(500);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public static void f2()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f2: &quot; + i);&lt;br /&gt;Thread.Sleep(700);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;class Class1&lt;br /&gt;{&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Thread t1 = new Thread(new ThreadStart(A.f2));&lt;br /&gt;A a = new A();&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t1.Start();&lt;br /&gt;t2.Start();&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;//Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;--------------------------------------------------&lt;br /&gt;We have three threads opened: t1, t2 and main. The output starts with the &quot;Main End&quot; message, because the t1 and t2 threads runs in parallel to the main method. The Thread.Sleep method applies to the current thread, and stops the thread for a while. The ThreadStart is a delegate to a void method without parameters. (In 2005 there is an option to pass an object). The thread can have a priority, Which is one of 5 levels. Normal is the defaut.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;BackGround Threads&lt;/span&gt;&lt;br /&gt;The application will not finish until all threads are over. If you wish the application to end when the main thread is over, you should set the IsBackGround property of the thread to be true:&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Thread t1 = new Thread(new ThreadStart(A.f2));&lt;br /&gt;A a = new A();&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t1.IsBackGround = true;&lt;br /&gt;t2.IsBackGround = true;&lt;br /&gt;t1.Start();&lt;br /&gt;t2.Start();&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------&lt;br /&gt;The main method will stop right away, and with it the whole application.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Join&lt;/span&gt;&lt;br /&gt;We can use the Join method in order to wait for the thread to end:&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Thread t1 = new Thread(new ThreadStart(A.f2));&lt;br /&gt;A a = new A();&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t1.Start();&lt;br /&gt;t2.Start();&lt;br /&gt;t1.Join(); // Don&#39;t continue until t1 is done&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------&lt;br /&gt;We can pass an timeout parameter to the Join method. This way we can avoid deadlocks. This overload also return the success status of the thread.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Passing Parameters&lt;/span&gt;&lt;br /&gt;When we wish to pass a parameter to a thread we need to add a variable which is shared by the caller and the thread:&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;public int Delay = 500;&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(Delay);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;class Class1&lt;br /&gt;{&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;A a = new A();&lt;br /&gt;a.Delay = 1000;&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t2.Start();&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------&lt;br /&gt;In the example above we passed the Delay variable to the thread. We can use any variable that is acessible to the thread in order to pass parameters to the thread.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Changing Thread State&lt;/span&gt;&lt;br /&gt;The Abort method will recommend for the thread to stop by throwing an exception in the thread.&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;Thread t1 = new Thread(new ThreadStart(A.f2));&lt;br /&gt;A a = new A();&lt;br /&gt;a.Delay = 1000;&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t1.Start();&lt;br /&gt;t2.Start();&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;t1.Abort();&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------&lt;br /&gt;Only the t2 thread will finish gracefully.&lt;br /&gt;&lt;br /&gt;The Interrupt method, similar to the Abort method, throws an exception to the thread. The only difference is that after the thread handles the exception it continues to work.&lt;br /&gt;&lt;br /&gt;In both cases the exception is asynchronous (you cannot deterministically determin the exact time the thread will catch the exception). You can pass an object (StateInfo) to the exception, and the thread can use it if necessary. Here is how you catch the event in the thread:&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;// Do stuff here&lt;br /&gt;}&lt;br /&gt;catch (ThreadAbortException ex)&lt;br /&gt;{&lt;br /&gt;// you can use ex.ExceptionState in order to get the object passed in the Abort method.&lt;br /&gt;}&lt;br /&gt;finally&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-----------------------------------------------------&lt;br /&gt;If in the catch clause we write &lt;code&gt;Thread.ResetAbort()&lt;/code&gt; we can bypass the Abort command and cancel the abort (in Interrupt we have it automatically).&lt;br /&gt;&lt;br /&gt;It is not recommended to stop a thread by using Abort if the thread handles resources. The problem is that the exception can be caught at any time, and if it were cought in the finally clause, the exception will not be handled at all, and the thread will be stopped without freeing the resources.&lt;br /&gt;&lt;br /&gt;A solution to this problem could be to use a public volatile member flag in the object, which is checked by the thread, and when set to true, the thread exits gracefully:&lt;br /&gt;---------------------------------------&lt;code&gt;&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;public volatile bool ThreadStop = false;&lt;br /&gt;public int Delay = 500;&lt;br /&gt;public void f1()&lt;br /&gt;{&lt;br /&gt;for (int i=0; i&amp;lt;10; i++)&lt;br /&gt;{&lt;br /&gt;if (ThreadStop)&lt;br /&gt;return;&lt;br /&gt;Console.WriteLine(&quot;f1: &quot; + i);&lt;br /&gt;Thread.Sleep(Delay);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Class1&lt;br /&gt;{&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;A a = new A();&lt;br /&gt;a.Delay = 1000;&lt;br /&gt;Thread t2 = new Thread(new ThreadStart(a.f1));&lt;br /&gt;t2.Start();&lt;br /&gt;Thread.Sleep(1000);&lt;br /&gt;a.ThreadStop = true;&lt;br /&gt;Console.WriteLine(&quot;Main End&quot;);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;----------------------------------------&lt;br /&gt;We use a volatile member in order to instruct the garbage colector not to do any optimizations on this member, and to pass it to a register. This will make sure that a concurrent method that wants to stop the thread will not have a context switch problem when registered are restored.&lt;br /&gt;&lt;br /&gt;Suspend and Resume can help external threads to control the state execution status.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Appartment State&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;If you want call a COM component from the thread you should know whether it runs in a Multi Threaded Appartment (MTA) or Single Stated Apartment (STA). The Thread class has an ApartmentState property, that you can set to MTA (default) or STA. If the COM and the thread appartment state do not match you might get performance issues (because of extra marshaling between apartments). &lt;/span&gt;&lt;br /&gt;You might have noticed that the automated Main method has an attribute called [STAThread]. This means that the main thread will always work with STA appartments. The question that comes to mind is why not just write &lt;code&gt;&quot;Thread.CurrentThread.ApartmentState = ApartmentState.STA;&quot;&lt;/code&gt; in the first line of Main. The answer is that the Main is not the first command the application does. There are many operations that happen when the application loads before Main is called, and in these operations there might have been calls to COM.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I introduced some classes in .NET that represents units of run-time environment in the CLR and in Win32. I covered some of the capabilities of the Thread class. I showed how to run threads in the background, how to wait for a thread to end, how to make a thread sleep, and how to tell a thread to end.&lt;br /&gt;In the next article I will discuss other issues regarding threads.</description><link>http://j1hammer.blogspot.com/2006/03/asynchronous-programming-in-net.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114115947530264755</guid><pubDate>Tue, 28 Feb 2006 20:11:00 +0000</pubDate><atom:updated>2006-03-02T11:53:39.093-08:00</atom:updated><title>Transactions in Enterprise Services</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;One of the most important features in COM+ is distributed transactions. A local transaction is a transaction in which we open a connection, perform a series of actions, and close the connection. In a distributed transaction we have several connections we need to synchronize in order to be able to perform several related actions simultaneously.&lt;br /&gt;An example: We have two bank accounts. We need to move 100$ from one account to the other. In order to achieve that we need to decrease the amount of money in the first account and increase the amount of money in the second account. But what if one of the operations fails? There could have been a network problem, an electrical power interruption or a malfunction in the database. If we managed to increase the money in the second account but failed to decrease the money in the first account, we will loose money. We need a way to perform both operations as one unit - if one of them fails - they both fails. Only if both succeeds - they all succeed. Transactions can help in those situations.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Two Phases Commit&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;DTC of Microsoft works in an algorithm called Two Phases Commit. In a One Phase Commit we will perform some operations in a serial way, and at the end, if we succeeded, we commit the changes. In our case, each transaction is split into sevral local transaction. At the first action COM+ handles the transaction. First we perform the Prepare Phase - each component must indicate that it is prepared to commit. There is no actual Commit yet. If one of the component aborted the transaction, we do not even enter the second phase. If all the components voted that they are prepared, we perform the Commit Phase. The DTC waits for a vote of Commit from all the component. After Fail or Timeout, all the components are told to Roll-back the transaction, and return to the state from before the transaction.&lt;br /&gt;&lt;br /&gt;In COM+ we use DTC. SQL Server and MSMQ are examples for services which support Two Phase Commit, and thus are considered as valid Resource Managers. For other systems, such as Oracle, which doesn&#39;t support Two Phase Commit, we have the CRM (Compensating Resource Manager) mechanism in COM+. It helps us to implement a class that will manage the resource in the COM+ way.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Requirement of Transactions - ACID&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Atomicity&lt;/span&gt; - The transaction perform several operations that are considered as one unit - all succeed or all fail. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Consistency&lt;/span&gt; - Information is not lost during the transaction. If the transaction was commited, we are at a stable consistent state. If the transaction rolls-back we are at the state prior to the transaction.&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Isolated&lt;/span&gt; - There are no outside interference during the transaction. Components which do not participate in the transaction cannot access the resources while the transaction is in action. In order to achieve this, we must of some synchronization mechanism (we have COM+ of course for that too).&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Durable&lt;/span&gt; - Failures can be recovered. In case of a failue the system, once working again, should be at a consistent state. DTC uses a logging mechanism in order to achieve that.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Setting Up Transactions Using COM+ Configuration Tool&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Each component, in the Transaction tab, has the following options:&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Disabled&lt;/span&gt; - The component is not built in a technology in which transactions are supported.&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Not Supported&lt;/span&gt; - The component does not participate in any transactions.&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Supported&lt;/span&gt; - The component will participate in a transaction only if the creating component is in a transaction&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Required&lt;/span&gt; - The component will always participate in a transaction. If the calling component is in a transaction, the component will exist in that transaction. If the caller do not participate in a transaction, the component will start its own transaction.&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Required New&lt;/span&gt; - The component always start a new transaction.&lt;br /&gt;&lt;br /&gt;When you program a transaction, the first step would be to take a pen or pencil (or your favourite case tool), and draw the components and their relation. Lets do an example: We have a BankManager which has a method MoveFunds. The method should take money from one account into a second account. So Account will have AddFunds and RemoveFunds methods. We will set the BankManager to be Require New, and the Account to be Supported. This way, whenever we call MoveFunds we start a new transaction. The class will use Accounts, and the AddFunds and RemoveFunds will participate in the transaction. If one of the methods will fail, the whole transaction will fail, and the money will be restored into the first account.&lt;br /&gt;&lt;br /&gt;The costs of the transactions are management of flags. JITA (Just-In-Time-Activation) is on if you use transaction. Each objects is created when it is called in the trasaction, and is released when it is not used. This insures isolation. Another mandatory feature is synchronization.&lt;br /&gt;In order to support JITA there is a Done flag. In order to support consistency there is Consistent and Abort flags. There are also costs in performance. It is better not to include too many components in the same transaction.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Programming Transactions&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;When you want your component to participate in a transaction, you mark it with attribute [Transaction]. The properties of this attribute correspond with the Transaction Tab in the COM+ Configuratin Tool: TransactionOptions contains enumerations of all transaction support section (Requirted, Supported, etc).  Take for example the case of RequiredNew: any call to any method of the class will start a new transaction. You can also set Isolation and Timeout.&lt;br /&gt;&lt;br /&gt;In the beginning of transaction the Consistent flag of all components is set to true. Once we do a change in state which makes it no loger consistent, we set the flag to false. The Done flag is set to false in every component. Once we call the SetComplete method in the end of the operation the Done flag is set to true. If we call the SetAbort method, the Done flag will be true, but the Consistent flag will be false, and the whole transaction will be rolled back. Once every component called the SetComplete method, the transaction succeeds. It takes only one component to abort for the whole transaction to fail.&lt;br /&gt;We can have an intermediate state, on which there is a problem, but the caller component might be able to fix it. For example, the database failed to respond, but the caller can start it up and the transaction can continue. In this case we can call the DisableCommit which leaves both the Done and the Consistent flags in false. The object still lives, and at any other point the component will be able to call the SetComplete method.&lt;br /&gt;&lt;br /&gt;The ContextUtil class has static properties and methods that can help us manage the transactions.&lt;br /&gt;- SetComplete - Done = true        Consistent = false. Commit transaction.&lt;br /&gt;- SetAbort       -  Done = true       Consistent = true. Abort Transaction.&lt;br /&gt;- EnableCommit - Done = false     Consistent = true. The transaction can be commited. The object cannot be deactivated&lt;br /&gt;- DisableCmmit - Done = false     Consistent = false. The transaction should not commit (but it can be changed).&lt;br /&gt;- DeactivateOnReturn - Controls the Done flag&lt;br /&gt;- MyTransactionVote - Controls the Consistent flag&lt;br /&gt;&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;[Transaction(TransactionOption.Required)]&lt;br /&gt;class Account:ServicedComponent&lt;br /&gt;{&lt;br /&gt;public class AdjustBalance(int account, decimal amount)&lt;br /&gt;{&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;              PrepareTransfer();&lt;br /&gt;              ExcecuteTransfer();&lt;br /&gt;              ContextUtil.SetComplete();&lt;br /&gt;        }&lt;br /&gt;        catch&lt;br /&gt;        {&lt;br /&gt;             ContextUtil.SetAbort();&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------------&lt;br /&gt;You can have a shortcut by using the [AutoComplete] attribute. The SetComplete will automatically be called for you when the method is over, unless an exception is thrown, and in this case SetAbort will be called for you:&lt;br /&gt;--------------------------------------------------&lt;code&gt;&lt;br /&gt;[AutoComplete(true)]&lt;br /&gt;public class AdjustBalance(int account, decimal amount)&lt;br /&gt;{&lt;br /&gt;   PrepareTransfer();&lt;br /&gt;   ExecuteTransfer();&lt;br /&gt;   if (fail)&lt;br /&gt;   {&lt;br /&gt;        throw new exception(&quot;Insufficient funds&quot;);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;&lt;br /&gt;Any component that is instanciated in the methods PrepareTransfer or ExecuteTransfer with transaction set to Supported or Required will be in the same transaction, need to vote SetComplete in order for the transaction to work, and will be created by the JITA rules.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I showed how transactions work, what are the costs, how to configure them in the COM+ Configuration Tool, and how to program them.&lt;br /&gt;I showed the Transaction attribute attached to the ServicedComponent. I showed the usage of ContextUtil in order to vote Complete or Abort. I showed the usage of the AutoComplete attribute as a shortcut.</description><link>http://j1hammer.blogspot.com/2006/02/transactions-in-enterprise-services.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114098897932508170</guid><pubDate>Sun, 26 Feb 2006 21:16:00 +0000</pubDate><atom:updated>2006-02-28T12:07:22.196-08:00</atom:updated><title>Getting Started With Enterprise Services</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;COM+ components are called Serviced Components in .NET.&lt;br /&gt;In this article I will show how to create a very simple Serviced Component, how to use this component from a .NET application, and how to configure the component from the COM+ Configuration Tool.&lt;br /&gt;In addition, I will discuss some of the aspects of the usage of COM+ from whithin the .NET framework.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Your First Enterprise Service&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;- Open VS.NET 2003.&lt;/span&gt;&lt;br /&gt;- Create a new class library (I will use C# in this tutorial), called HelloEnterpriseServices.&lt;br /&gt;- Add a reference to System.EnterpriseServices.dll&lt;br /&gt;- We must sign the assembly with a strong name (it is not mandatory to place it in the GAC):&lt;br /&gt;In the command prompt of .NET, in the foder of the class library, type &quot;sn -k keys.snk&quot;&lt;br /&gt;In the AssemblyInfo.cs file change the following:&lt;br /&gt;&lt;code&gt;[assembly: AssemblyKeyFile(&quot;&lt;strong&gt;..\\..\\keys.snk&lt;/strong&gt;&quot;)]&lt;/code&gt;&lt;br /&gt;Build the class library.&lt;br /&gt;- Add a class called HelloService as follows:&lt;br /&gt;--------------------------------------------------------------&lt;code&gt;&lt;br /&gt;using System.EnterpriseServices;&lt;br /&gt;namespace HelloEnterpriseServices&lt;br /&gt;{&lt;br /&gt;public class HelloService:ServicedComponent&lt;br /&gt;{&lt;br /&gt;public string SayHello(string name)&lt;br /&gt;{&lt;br /&gt;return &quot;Hello &quot; + name;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------------------&lt;br /&gt;- Build the project.&lt;br /&gt;&lt;br /&gt;Congratulations. You have just created your first enterprise service.&lt;br /&gt;&lt;br /&gt;Some Explanations:&lt;br /&gt;In our class library we can place as many classes as we want. Some of the classes may inherit from the ServicedComponent class. All the classes are managed, and act the same as any other .NET class. The classes which derive from ServicedComponent will be registered as COM+ components, and the class library will be registered as a COM+ application.&lt;br /&gt;&lt;br /&gt;If you will check the COM+ Configuration Tool you still can&#39;t see the new application.&lt;br /&gt;&lt;br /&gt;Next we will fix some security issues. COM+ provide a service for role-based security. It is a good practice to use it, but if you do not wish to do so, you should open the AssemblyInfo file and add the following:&lt;br /&gt;- Add &lt;code&gt;using System.EnterpriseServices &lt;/code&gt;in the beginning of the file.&lt;br /&gt;- Add &lt;code&gt;&lt;strong&gt;[Assembly: ApplicationAccessControl(false)]&lt;/strong&gt; &lt;/code&gt;at the end.&lt;br /&gt;- Build again.&lt;br /&gt;In the COM+ Configuration Tool, in the application properties, in the Security tab, you can see a checkbox &quot;Enforce access checks for this application&quot;. You don&#39;t want this to be checked, unless you set up the security, because the application will throw an exception when loaded by clients.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Consuming the Serviced Component&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;Add a new Windows Application to the solution, called HelloEnterpriseServicesApp.&lt;/span&gt;&lt;br /&gt;First add a reference to the previous HelloEnterpriseServices library. You &lt;strong&gt;must&lt;/strong&gt; also add a reference to System.EnterpriseServices.&lt;br /&gt;&lt;br /&gt;Next we will use our HelloService class. It is used as any other .NET class.&lt;br /&gt;- Add a button to Form1. Double-click on it.&lt;br /&gt;- Add &lt;code&gt;using&lt;br /&gt;HelloEnterpriseServices &lt;/code&gt;to the form code.&lt;br /&gt;- Add the following code to the button click event handler:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;private void button1_Click(object sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;HelloService service = new HelloService();&lt;br /&gt;MessageBox.Show(service.SayHello(&quot;Jack&quot;));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;- Set the windows application project to be the startup project of the solution.&lt;br /&gt;&lt;br /&gt;Run the solution. The form shows right away. Clicking on the button will take a couple of seconds. At this time the COM+ component is first registered. Clicking again on the button shows the message box much faster. This kind of registration is called &quot;Lazy Registration&quot;. I will show another method soon.&lt;br /&gt;&lt;br /&gt;If you check the COM+ Configuration Tool (right click and select Refresh), you will see HelloEntrpriseServices. It is a Library application (as you can see by the icon). If you expand the application, and the Components, you will see the HelloService component. Expnad it further, and the Interfaces and you will not find the SayHello method. That is because we did not define a .NET interface for the service.&lt;br /&gt;If you right-click on the HelloService component, and select properties, you will see 2 GUIDs (in the General tab) - one is CLSID and the other is for the Application. You must understand that by using COM+ from whithin .NET you go back to the time of COM and &quot;DLL Hell&quot; - The classes are registered to the registry, you cannot do side-by-side deployment, and cannot easily version the component. The .NET assembly, however, has all the benefits of .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Improving the Component&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Consider the following steps a MUST-DO. You will save yourself some headaches.&lt;br /&gt;First we will add an interface to our component:&lt;br /&gt;------------------------------------------------------&lt;code&gt;&lt;br /&gt;using System.EnterpriseServices;&lt;br /&gt;namespace HelloEnterpriseServices&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;public interface IHello&lt;br /&gt;{&lt;br /&gt;string SayHello(string name);&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;public class HelloService:ServicedComponent,&lt;strong&gt;IHello&lt;/strong&gt;&lt;br /&gt;{&lt;br /&gt;public string SayHello(string name)&lt;br /&gt;{&lt;br /&gt;return &quot;Hello &quot; + name;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------------&lt;br /&gt;If you wish to do so, you can place the interface in a different assembly. You can also define multiple interfaces to the assembly.&lt;br /&gt;&lt;br /&gt;Next step will be to put the GUIDs ourselves. It is a good practice to force COM+ to use our GUIDs, in order to make sure that the applications that are installed are indeed ours.&lt;br /&gt;In Visual Studio .NET, select Tools menu, and Create GUID tool. Select Registry Format, and Copy.&lt;br /&gt;Add the following attribute to the interface:&lt;br /&gt;[Guid(&quot;B906C925-D72F-4d8d-B8D5-672D7C2E595A&quot;)] .&lt;br /&gt;The GUID here is just an example. Be sure to remove the curly brackets ({})&lt;br /&gt;You can do the same process to add a GUID to the class (A different GUID).&lt;br /&gt;&lt;br /&gt;Generate another GUID, and in the AssemblyInfo file add the following attribute:&lt;br /&gt;[assembly: ApplicationID(&quot;Guid here&quot;)]&lt;br /&gt;This will set the Application GUID.&lt;br /&gt;&lt;br /&gt;Other useful attributes you can use:&lt;br /&gt;[assembly: ApplicationName(&quot;Hello Application&quot;)] - for a user friendly name.&lt;br /&gt;[assembly: ApplicationActivation(ActivationOption.Library)] - To set Library or Server application&lt;br /&gt;[assembly: Description(&quot;Here is a nice description you will see in the COM+ Configuration Tool&quot;)]&lt;br /&gt;&lt;br /&gt;Rebuild the class library.&lt;br /&gt;Before we continue, delete the previous HelloEnterpriseServices application (right-click on it and select &quot;delete&quot;).&lt;br /&gt;&lt;br /&gt;I will show you how to register the application from command prompt:&lt;br /&gt;go to the folder of the dll (using &quot;cd fullpath&quot;) and type&lt;br /&gt;&lt;strong&gt;regsvcs &lt;dllname&gt;.dll&lt;/strong&gt;&lt;br /&gt;Later, when you update your component, you can call&lt;br /&gt;regsvcs &lt;dllname&gt;.dll &lt;strong&gt;/reconfig&lt;/strong&gt;&lt;br /&gt;The regsvcs tool wrapps the .NET dll with COM, register it in the registry and creates a tlb file you can call from a native language (as COM)&lt;br /&gt;&lt;br /&gt;In the COM+ Configuration Tool (after you refresh) you will see in the application properties the new application ID, the description, and the use-friendly application name. When you expand the interface of the HelloService component, you will see the IHello interface, and underneath the SayHello method.&lt;br /&gt;&lt;br /&gt;The attribute we placed in the AssemblyInfo file (such as ApplicationActivation to set Server/Library) are only relevant when we deploy. After deployment anyone can reconfig the application to their choosing (that&#39;s the whole point of COM+).&lt;br /&gt;&lt;br /&gt;When you create newer versions you do not need to create new GUIDs. Unless of course you change the interface (which is not recommended as you will need to support old clients). Remember that there is no side-by-side activation of com components (unlike .NET assemblies).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Contexts&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Each ServicedComponent inherits from ContextBoundObject. When an object tries, for example, to participate in a transaction, it must know the transaction ID. This parameter is a part of the context of the object. Each feature we use in COM+, we make the context of the object more heavy, with more parameters. The object context is the set of services provided by COM+ to the object.&lt;br /&gt;For some objects the context is more similar than other objects. In this case the interaction will be more smooth. In cases where the contexts differ significantly, there will be an interception - a context switch. Your goal is to keep the contexts similar as much as possible, which will cause less interception, and improve performance.&lt;br /&gt;&lt;br /&gt;The handling of contexts is completely transparent in .NET. Any interaction you will do inside your code with COM+ is done through a class called ContextUtil. This stateless class has only static methods and properties. You will need to use this class when you will program transactions, object pooling and other COM+ services.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;COM+ Applications vs. .NET Assemblies&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The COM+ application is a frame for logical management of components. It is not a single unit of deployment, as opposed to the .NET dll. You can create new COM+ applications with components from different .NET assemblies. If you design your component to be reusable blackboxes correctly, you will have a very powerful way of using the same components in different COM+ applications.&lt;br /&gt;In the COM+ Configuration Tool, right click on COM+ Applications, and select New Application. This will start the COM+ Application Installation Wizard. You can install a pre-built application, so you can choose an msi file you exported using the Export option. Or you could create an empty application. You will enter a name &quot;HelloEnterpriseServices2&quot; and select the type of application (server or library). Select InteractiveUser at this point. You will see the empty application under COM+ application. Now you can add a Component (select New Component to show another wizard). You can install new components from dll or tlb files, or you can use one of the components already installed in your computer. When you are done, you can Export it all, and create an msi file to install it on one shot in another computer. Note that you will need to do proper ID management.&lt;br /&gt;&lt;br /&gt;There are very few services that will force you to use a Server application (for example: Queued Component must be in a different process). It is usually more efficient to use Library applications.&lt;br /&gt;&lt;br /&gt;There might be some issues when using several versions or copies of .NET assemblies. Although it is not mandatory, placing the .NET assembly in the GAC will tell everybody to use the same copy of the assembly, which help might help you when debugging and deploying. You will always know the exact copy of the assembly the COM+ application is using.&lt;br /&gt;&lt;br /&gt;Another issue you should consider is the usage of properties. COM does not support properies, as .NET does. So each property will be translated into a get and a set method. I would suggest using Getter and Setter methods in the first lace, so you will not have different interfaces to managed and native components.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In this article I showed how to build the minimum enterprise service possible. I showed the usage of GUID and interfaces. I showed how to consume the enterprise service in managed code. I discussed some deployment issues.&lt;br /&gt;&lt;br /&gt;In the next article I will show how to use transactions in managed code.</description><link>http://j1hammer.blogspot.com/2006/02/getting-started-with-enterprise.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114047072314674119</guid><pubDate>Mon, 20 Feb 2006 21:12:00 +0000</pubDate><atom:updated>2006-02-28T11:27:49.316-08:00</atom:updated><title>Introduction To Enterprise Services (COM+) In .NET</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;COM+ is a service provided by Windows (it is not a .NET feature), similarly to other services such as IIS. Despite common belief, COM+ is not COM.&lt;br /&gt;The main goal of COM+ is to provide a management environment for processes. The services provided by COM+ are required in distributed application (such as Client/Server application), especially with numerous calls.&lt;br /&gt;By using COM+ in the .NET environment, or as it is called in .NET - &quot;Enterprise Services&quot;, we benefit from the advantages of both the CLR and managed environment and the native environment of COM+, including integration with old legacy applications.&lt;br /&gt;&lt;br /&gt;Almost all the services provided by COM+ is not supplied by the .NET framework, and without COM+ you will need to program them yourself. You don&#39;t need to know anything about COM development, and the use of Enterprise Services in .NET is quite easy.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;A Short History Lesson&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;COM is a protocol for development component prior to .NET. When needs for distributed application have emerged, it was upgraded into DCOM (Distributed COM), which is a protocol for running in different machines or processes. For example, you could write an ActiveX Exe, which is an application that could be called from remote clients. The interface was done with DCOM. This protocol is lightweighted, in a binary format, without a lot of overhead. It is suitable for local networks, especially when the computers on the network are well known in advance. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;However, some new services were required which provide long term stability, correct management of resources, and usage of resources by demand. For example: you could set up a server application, on which every call created a new process in the server - problem with scalability, and the syncronization of clients. Another required service is distributed transactions (For example: The server updates a database in a local transaction. There could be a dependency on another server which updates a database in another local transaction. The problem is that updating a database is an action which strongly depend on a connection, which is a resource the server has no control over. There might be a failure, and both updates should be rolled back to the previous state). Windows has a service called DTC (Distributed Transaction Coordinator), that can be called in order to request the transaction service.&lt;br /&gt;&lt;br /&gt;In Win-NT a new service was created, called MTS (Microsoft Transaction Server), that wrapped DTC. On request, MTS supplies a new process, dllhost, that can host other dlls. Instead of writing an ActiveX Exe, we write an ActiveX dll (which is a COM component), and when it is hosted in dllhost process it automatically works in DCOM. It is possible to connect remotely to dll that is hosted by dllhost, and dllhost manages the resources. One of the services is distributed transactions.&lt;br /&gt;&lt;br /&gt;In Win2000 the MTS was built-in, and some other services were added, all wrapped together in COM+.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;COM+ in .NET&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In the .NET environment we create a managed library (dll). If we wish to use COM+ in the dll, the dll is wrapped (automatically) with COM, in order for native components to be able to connect to it.&lt;br /&gt;Note: As I wrote above, you can use COM+ in order to communicate between processes, and even between machines, using DCOM. But in .NET we have other technologies to perform interprocess communications (Remoting, Web Services, etc). It is not recommended to use COM+ just to perform RPC. A good architecture will be to call the server using Web Services (for example), and utilizing the COM+ services in the server locally, without the client knowledge.&lt;br /&gt;In fact, when you call a Web Service, it is hosted in the IIS, which is a server. This is similar to COM+ approach - the application is hosted in the dllhost process. For communication, IIS is a better choice.&lt;br /&gt;&lt;br /&gt;In .NET a COM+ component is called a Serviced Component or a Configured Component. When the component is already active, maybe even installed on a client, it can be configured administratively using a user friendly tool, supplied with Windows. For example: COM+ has a feature called Just-In-Time Activation (JITA), and we wrote a Serviced Component using this feature. We installed the software on the client, and now we can configure turn the feature on and off using the COM+ Configuration tool, by checking a checkbox. No code required. No Xml or other configuration files. No registry or environment variables. It all comes in the box.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The COM+ Configuration Tool&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Enough with the dry discussions. Time to see it in action.&lt;br /&gt;The COM+ Configuration Tool is located in the Control Panel-&gt;Administrative Tools-&gt; Component Services.&lt;br /&gt;On the left tree view, expand Component Services -&gt; Computers -&gt; My Computer -&gt; COM+ Applications.&lt;br /&gt;&lt;br /&gt;You can see all the COM+ applications installed in your computer. Soon we will add a component there. Right clicking on any application, and selecting Properties will open the confiduration window. There are many tabs with a lot of configurations you can change, without even knowing what the application does (so be careful!!!). You don&#39;t even need to restart the application - clicking Apply will configure the application while it is running.&lt;br /&gt;&lt;br /&gt;If you expand the applications even further, you can see Components. Under components you can see all the classes you registered to COM+ (remember that not all the applications here are .NET). Under each component you can see the Interfaces of this component. You can configure a Component, an Interface, and even a Method (by selecting properties).&lt;br /&gt;&lt;br /&gt;The data that you configure in the COM+ Configuration Tool is stored in a database called &quot;COM+ Catalog&quot;. This database is managed by Windows, and you can even access it programatically. The data that is stored in the COM+ Catalog is the metadata of all the applications and components.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;What Services Are Provided By COM+&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Lets start to explore the Properties window, in order to better understand what features are supported by COM+. As I describe a feature, try to think how you can achieve it using your .NET programming skills (without COM+ of course).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Application Recycling&lt;/span&gt;&lt;br /&gt;The application will be restarted automatically depending on configurable conditions, such as Lifetime Limit (restart every period of time), Memory Limit (protection against memory leaks), Call Limit and more. The recycling is done in a smart way. For example, COM+ waits until there are no more clients to the application. This can be configured in the &quot;Pooling&amp;amp;Recycling&quot; tab.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/bf98318b-4d87-44cc-85a1-68faf5547e06.asp&quot;&gt;COM+ Application Recycling Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Just-In-Time Activation&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;It happens a lot that a client holds on to an object, it does not need right now. The object may be expensive on resources, so there is a waste of resources. JITA helps manage the resources more efficiently. When the client finish working with the object, COM+ will automatically deactivate the object, freeing all resources. When the client uses the object again, it will be activated once more by COM+. The client uses the object without even knowing that JITA is applied. Notice that the object should be stateless, or it should be able to store and restore its state. &lt;/span&gt;&lt;br /&gt;This configuartion is per Component, in the Activation tab, under Activation Context, by selecting Don&#39;t Force Activation Context, and checking the Enable Just-In-Time Activation.&lt;br /&gt;For more information: &lt;a href=&quot;http://windowssdk.msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/dbc7b257-8506-42c8-8a78-3474c6d4f4b6.asp&quot;&gt;COM+ Just-in-Time Activation Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Please note that some of the services of COM+ depend on other services. For example: When working with transactions we must enable the JITA, in order to support Atomicity.&lt;br /&gt;&lt;br /&gt;When we use JITA, we must take into consideration the tradoff: When we call a JITA object, we implicitly activate and deactivate it (time consuming), but we benefit the proper management of resources (the object will not hold unused resources).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Object Pooling&lt;/span&gt;&lt;br /&gt;We can have a pool of objects, initialized when the application started. Whenever a client creates a new instance, it gets an object from the pool, without the cost of creating the new instance. When the client releases the object, it goes back to the pool.&lt;br /&gt;You can enable Object Pooling of a compoent, in the Activation tab. Select Enable Object Pooling. You can set a minimum and maximum pool size. The minimum pool size will be created when the application starts (slowing your application startup). When the pool is full, and a new instance is requested it is created and added to the pool. If the pool size approched its maximum size, the creation of new objects would fail (after a timeout - configurable too).&lt;br /&gt;You can configure the object pooling while the application is running of course, and profile the performance in order to fine-tune the startup time, memory usage and performance of the application.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/74a45220-449a-4d89-a979-a206e5e3d3ad.asp&quot;&gt;COM+ Object Pooling Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Notice the possibility to enable both Object Pooling and JITA - whenever an object is used it is activated from the pool, and when finished it is sent back to the pool. This will speed up the activation/deactivation process of JITA.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Transactions&lt;/span&gt;&lt;br /&gt;I will not discribe here what a transaction is. I will only give a short and simple example: A starts a transaction, calling B and C. B fails during execution. Even if A and C succeeded they must all fail, and roll-back to the last consistent state.&lt;br /&gt;COM+ handles the transactions for you (using DTC). You will need to perform some actions in code in order for this to work.&lt;br /&gt;For more information:&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/e2198514-c403-4b31-8c8c-0b1c83c4f936.asp&quot;&gt; COM+ Transactions Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In order to configure transactions, go to the Transactions tab of your component, and select one of the options for Transaction Support.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Synchronization (Concurrency)&lt;/span&gt;&lt;br /&gt;When there are several components sharing resources in a multi threading environment, we must always keep the resources consistent. If our components share one logical activity, we can set up the Synchroniztion service of COM+, so it will lock a component for any component that is not in the activity.&lt;br /&gt;For example: Say we 4 components A,B,C, and D. A starts a thread, and calls B, which in turn calls C. D will not be able to access A,B, or C, and will be blocked until the activity is over.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/a05de040-0115-44aa-80e2-55eff2ec894d.asp&quot;&gt;COM+ Synchronization Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note: This is a very heavy mechanism in a multithreaded environment. If B has two methods, and A only calls one of them, D will not be able to call any of B&#39;s methods. However, this forces you to design and implement a well behaved multi threaded application, which might be a good thing.&lt;br /&gt;&lt;br /&gt;In order to configure Synchronization, go to the Concurrency tab of your component, and select one of the options for Synchronization Support. If you use transactions, you must also use Synchronization (COM+ will configure this for you).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Queued Component&lt;/span&gt;&lt;br /&gt;MSMQ is one of the services provided by Windows. MSMQ provides message queueing services, administrated by Windows. There is a good integration between COM+ and MSMQ, in the form of Queued Component service.&lt;br /&gt;COM+ can intercept any call to a method of the component, and serialize (translate) it into a message in a queue. The COM+ component, in its own time, will handle the COM+ call. COM+ will deserialize the message to a proper method call, and the server will be able to perform the action. This mechanism is a good solution in cases of asynchronous method calls. It will not work well with synchronous calls. The server should not return a value, throw exceptions and so on (It is possible, but not trivial).&lt;br /&gt;This mechanism is good when the client should not rely on the service availability.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/ff11e251-311f-4612-8f0d-a4cbc5b4d872.asp&quot;&gt;COM+ Queued Components Concepts&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note: You can use MSMQ from .NET independently of COM+, using the System.Messaging namespace. For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdadotnetasync1.asp&quot;&gt;Accessing Message Queues&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You can configure Queueing services of an Interface, in the Queueing tab, by checking the Queued option.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Loosly Coupled Events&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;COM+ introduces a new concept of event handling. Usually a publisher raises an event, and a subscriber&#39;s method is executed. This implies a coupling between the publisher and the subscriber. In COM+ publishers provide events, subscribers register for these events, but COM+ is the mediator between the publishers and the subscribers. The benefits of this approach is that the subscribe do not know which publisher raised the event.&lt;/span&gt;&lt;br /&gt;The service is good if the publishers and subscribers fdo not know each other (they may not even know that the others exist). It is also good when there are number of publishers for the same event, and it is not important which one raised the event.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/ff11e251-311f-4612-8f0d-a4cbc5b4d872.asp&quot;&gt;COM+ Events Concepts &lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;The subscriptions for event is configured in the COM+ Configuration Tool.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Role-Based Security&lt;/span&gt;&lt;br /&gt;COM+ provides a mechanism in which you can define which user can initializes components, call a method or even perform a custom operation. Instead of selecting users for each task, you define Roles, and assign rules to that role. The users are mapped into roles using the COM+ Configuration Tool, using the Windows built-in authentication system.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/686fb391-d337-41b4-bb51-b70f27a0c300.asp&quot;&gt;COM+ Security Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Object Construction&lt;/span&gt;&lt;br /&gt;You can send a message to a component when it is constructed in the form of a string. This mechanism resembles the command line arguments of an application. For example, you can pass a connection string, or a path, or several arguments. The advantage is that it is configurable from the COM+ tool (in the Activation tab).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Compensating Resource Managers (CRM)&lt;/span&gt;&lt;br /&gt;In order to participate in a COM+ distributed transaction, the resources should be managed by a resource manager. For example: SQL Server and MSMQ are both resource managers, so you can handle them from whithin a transaction.&lt;br /&gt;Take for example the case of writing to a file. How can you roll back from that? If you want the writing to a file action to be coordinated in a transaction, you must create a Compensating Resource Manager for that file. COM+ provide the infrastructure to create a class with an interface that supports the CRM. By implementing this class you can add any resource into the transaction.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/8d1d034f-8a09-40ae-842a-5251135bd3c8.asp&quot;&gt;COM+ Compensating Resource Manager Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Shared Property Manager (SPM)&lt;/span&gt;&lt;br /&gt;COM+ provides a mechanism to share state between components.&lt;br /&gt;For more information: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/8d1d034f-8a09-40ae-842a-5251135bd3c8.asp&quot;&gt;COM+ Shared Property Manager Concepts &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;Soap&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;You can create a SOAP WebService Wrapper around your application (you will need IIS installed for this). This feature will automatically create a Web Service facade to be used in order to connect remotely to the COM+ application.&lt;/span&gt;&lt;br /&gt;All you need to do is select the Uses SOAP checkbox in the Activation tab of the application.&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Server and Library Components&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;COM+ applications can be in the form of Library applications or Server applications. It is configurable from the COM+ Configuration Tool, in the Activation tab of an application.&lt;br /&gt;All the Server applications has icons shaped as boxes, and Library applications has a round shaped icons.&lt;br /&gt;A Server application will be hosted by a dllhost, and will run as a different process. All the instances will run in the same dllhost. A Library application will run in the process of the client.&lt;br /&gt;You can start a Server application, by right clicking on the application and selecting Start. You can stop the applications in the same way. All the runnning Server applications can be monitored in the Running Processes node, under COM+ Applications. Each running application will have a number in brackets. This is the process ID of the dllhost hosting the application (You can see the process in the Task Manager).&lt;br /&gt;&lt;br /&gt;When you use Library applications (the default) you will gain all the non-distributed services of COM+. For example, you can perform transactions from inside the process without the costs of DCOM communication. On the other hand, if you use a Server application you loose performance, because of the inter process communication, but you gain centralization of the services inside one process. For example, you might want to put all the services in the server-side. You can use the Shared Property Manager in order to share data between clients.&lt;br /&gt;&lt;br /&gt;If you use a Server application in a remote machine, you must consider the deployment of the components in the clients. It is a good practice to avoid this situation, and to use other, more advanced, technologies, such as Web Services, in order to handle the communication, and to limit the usage of COM+ from whithin the boundaries of the machine.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Exporting the Configuration&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;For server applications, it is a very important feature to save the current settings in order to load it later on. For that you have the Export options in the context menu of the application. A handy wizard will guide you through the very easy process. You select a path, click OK and an msi file will be generated. By double clicking on it you will install the COM+ component on the machine, and set the configuration as the one that was saved.&lt;br /&gt;&lt;br /&gt;A very powerful feature is to save the component as an Application Proxy. This way you can install the application on machines that should connect to the server remotely.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;All of the services which are provided by COM+ are fully documented in MSDN: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/8d1d034f-8a09-40ae-842a-5251135bd3c8.asp&quot;&gt;COM+ Services &lt;/a&gt;&lt;br /&gt;In the next article we will create a COM+ component in .NET and deploy it.</description><link>http://j1hammer.blogspot.com/2006/02/introduction-to-enterprise-services.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114021333118849559</guid><pubDate>Fri, 17 Feb 2006 21:27:00 +0000</pubDate><atom:updated>2006-02-18T02:03:50.410-08:00</atom:updated><title>Administrative Configuration of Applications</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;This article is one of a series of articles exploring .NET Assemblies.&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html&quot;&gt;Exploring .NET Assemblies&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html&quot;&gt;Multi-File Assemblies&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;Strong Named Assemblies&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html&quot;&gt;Placing Assemblies in the Global Assembly Cache (GAC)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html&quot;&gt;Administrative Configuration of Applications&lt;/a&gt;&lt;br /&gt;The articles are intended for programmers who are using .NET for a while, and wish to know more about .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;.NET Applications compile into executable assemblies. The standard way to configure .NET applications is through .NET built-in mechanism of configuration files. Those files are Xml files, with a specific format. The exact structure of those files can be seen &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconapplicationconfigurationfiles.asp&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;.NET configration files holds information about versioning, references, Remoting, and many other, including custom configurations done by the user (Here is a nice tutorial about the subject: &lt;a href=&quot;http://www.codeguru.com/Csharp/Csharp/cs_misc/designtechniques/article.php/c7987/)&quot;&gt;Article From CodeGuru&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In order to create the configuration file yourself, you add to the project a new item of type Application Configuration File. The item that is added to the project is called App.Config (for web applications it is called Web.Config). When you build your project, the App.Config file is copied into the target directory, and renamed to [exename].exe.config. It must be located in the same directory as the exeutable, with that specific name. When an administrator wants to change edit this file, he can open the application.exe.config file in an Xml editor, and if he knows wht he is doing change the configuration. By the way, Whenever building the application, the settings are overwritten by the original App.Config file.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Administrative Configuration&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The problem with letting administrators configure Xml files, is that they must be familiar with Xml in general, and with .NET configuration files in particular. It would have been nice to have a tool that can manage the configuration of the application without the need to open Xml files.&lt;br /&gt;&lt;br /&gt;Such a tool exist, and supplied when installing the .NET Framework. In the Control Panel-&gt;Administrative Tools-&gt;Microsoft .NET Framework 1.1 Configuration, there is a configuration tool. Under the Application section, you can select &quot;Add an Application to Configure&quot;, and then choose from the list of applications installed on the computer. By selecting &quot;View the Application&#39;s Properties&quot;, you can select the Garbage Collector mode, Publisher Policy and the relative search path for additional assemblies. I will not discuss the first two here. By selecting a relative search path you can specify directories where referenced assemblies may be located (this is called &quot;Probing&quot;). For example: You might wish to place all the dlls in a subfolder called &quot;references&quot;. In that case put the string &quot;references&quot; (of course you will need to deploy the application in this way). The relative path must be underneath the executable (You cannot put &quot;..\&quot; in the relative search path). As soon as you save your settings, an application.exe.config file is created (if the file already exist it is simply modified).&lt;br /&gt;&lt;br /&gt;There is a lot of strength in using this tool. There is a central repositories of all managed application. The configuration can be done by administrators (who know what they are doing of course) rather than the programmer. If the assembly is deployed properly, the configuration cannot impact the application performance and correctness. The deployment can be decided administratively rather than be constant (you might want as a user to place files in a different location).&lt;br /&gt;&lt;br /&gt;Note that everything you can configure with the tool, can be configured manually by changing the Xml.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;More Configurations&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The &quot;Managed Configured Assemblies&quot; link, shows you all the references you can configure. By default no assemblies are seen. By right-clicking ad selecting &quot;Add...&quot;, you can select assemblies which are referenced: From the GAC, Framework assemblies the application uses, or manually (by typing the name of the assembly and the public key token you can see in the manifest of the application). Once you select the assembly, you can configure some things about the referencing: Publisher Policy (will not be discussed here), Binding Policy (you can select the version of the reference), and Code Bases (you can select the full path to the assembly, including full URLs).&lt;br /&gt;&lt;br /&gt;I would like especially to discuss the Binding Policy. It is often that you diploy an application, and then provide a new version of a dll. You do not need to compile the exe and deploy it again. If you did not change the interface of the dll, then the exe should work fine. When you deploy the new version of the dll, the old version of the dll can coexist with the new version (for example by placing both in the GAC). But you still need to tell the exe in the client to change the binding to the new version of the dll. This is done by using the Binding Policy page. You can tell the version the exe is currently using, and the new version you want the exe to use (You can also set a range of versions).&lt;br /&gt;&lt;br /&gt;You can also configure the Remoting (when necessary), but I will not discuss this in this scope.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Machine Configuration&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;There is a set of Xml configuration files for the .NET framework, located in your Windows folder under C:\Windows\Microsoft.NET\Framework\v1.1.4322\CONFIG.&lt;br /&gt;&lt;br /&gt;The file machine.config contains a lot of global configurations, some of which are defaults for applications running on the machine. Avoid changing this file as much as possible. Some of the settings can be overrided in the application configuration files.&lt;br /&gt;Discussing the exact contents of the machine.config file is out of scope in this article.</description><link>http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-114004443690100207</guid><pubDate>Wed, 15 Feb 2006 22:07:00 +0000</pubDate><atom:updated>2006-02-18T02:03:34.473-08:00</atom:updated><title>Placing Assemblies in the Global Assembly Cache (GAC)</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;This article is one of a series of articles exploring .NET Assemblies.&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html&quot;&gt;Exploring .NET Assemblies&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html&quot;&gt;Multi-File Assemblies&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;Strong Named Assemblies&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html&quot;&gt;Placing Assemblies in the Global Assembly Cache (GAC)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html&quot;&gt;Administrative Configuration of Applications&lt;/a&gt;&lt;br /&gt;The articles are intended for programmers who are using .NET for a while, and wish to know more about .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The Global Assembly Cache (GAC) is a repository of .NET assemblies. Assemblies which are stored in the GAC are meant to be shared by several applications on the computer. Good examples of such assemblies are the .NET Framework assemblies (System.Data.dll, System.Xml.dll, etc).&lt;br /&gt;&lt;br /&gt;In .NET the deployment process is called XCopy deployment: you just copy the output folder from one place to another and everything works without the need to register stuff on the computer. The Registry is avoided altogether in .NET, and in future Windows versions, it might disappear.&lt;br /&gt;&lt;br /&gt;As default, when you reference a dll, and build the project, VS.NET will copy the dll to the output folder, where the exe is generated (Unless the &lt;em&gt;Copy Local&lt;/em&gt; property of the reference is set to false). You can place the dll in a sub folder named as the dll without problem. If the referenced Assembly is in the GAC, it will appear in the .NET tab of the Add Reference dialog in .NET, and the Copy Local property will be set to false as default. The exe will know to look for the assembly in the GAC.&lt;br /&gt;There is a very good article about where the assembly searches the referenced assemblies here: &lt;a href=&quot;http://www.codeproject.com/dotnet/assemblydeployment.asp&quot;&gt;http://www.codeproject.com/dotnet/assemblydeployment.asp&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Placing an Assembly in the GAC&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;There are few ways to place an assembly in the GAC. First of all you must sign the assembly with a strong name (look &lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;here&lt;/a&gt;).&lt;br /&gt;One way is to simply copy the assembly file to the GAC folder: C:\WINDOWS\assembly (this is relevant to my computer), by dragging using Windows Explorer.&lt;br /&gt;As you can see this is not a regular folder: You can see that many assemblies appear several times with different versions (the public key token can also be seen). This is the solution to &quot;Dll Hell&quot;. Dlls can exist side-by-side with different versions. This way old application which has reference to the old versions can still work.&lt;br /&gt;&lt;br /&gt;Another way is to use the command line utility: &lt;strong&gt;gacutil /i MyAssembly.dll.&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;The last way is to use the administrative tools for .NET. Open Start-&gt;Control Panel-&gt;Administrative Tools-&gt;.NET Configuration 1.1 .&lt;br /&gt;On the dialog select &quot;Assembly Cache&quot;, and then click on &quot;Add an Assembly to the Assembly Cache&quot;. Select the assembly and it will be added to the GAC. If you click on &quot;View List of Assemblies in the Assembly Cache&quot;, you can see a different view of the GAC, and you can delete an assembly from the GAC (be careful). You can also delete the assembly from the windows folder.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Setting the Version of the Assembly&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In order to version the assembly, you should open the AssemblyInfo file in the project and set the AssemblyVersion attribute.&lt;strong&gt; [assembly: AssemblyVersion(&quot;1.0.*&quot;)].&lt;/strong&gt;&lt;br /&gt;The version consists of four number separated by dots ([major version].[minor version].[build number].[revision]). The asterisk (*) tells the framework to advance the two least significant numbers every build. The two most significant numbers (the left ones) should be changed by you when you want to change versions. This version can be seen in the Manifest of the assembly, and can be seen as a result in the GAC.&lt;br /&gt;You can set some attributes (AssemblyTitle, AssemblyDescription, etc...) to characterize the assembly. This attributes can be seen in Windows Explorer by right-clicking on the assembly file, selecting &quot;Properties&quot;, in the Version tab.&lt;br /&gt;&lt;br /&gt;In order to change the version of the dll that the exe should refer to (for example if I now have a new dll, and the old exe is still referring to the old version of the dll), we need to change the application configuration file of the exe. This file must be located in the same folder as the exe, and the name of the file is [exename].exe.config . If there is no such file, you can add it yourself. The following code must appear in the file:&lt;br /&gt;&lt;em&gt;&amp;lt;bindingRedirect oldVersion=&quot;1.0.0.0&quot; newVersion=&quot;2.0.0.0&quot;/&amp;gt;&lt;/em&gt;&lt;br /&gt;For more information you can look here: &lt;a href=&quot;http://msdn2.microsoft.com/en-us/library/7wd6ex19.aspx&quot;&gt;http://msdn2.microsoft.com/en-us/library/7wd6ex19.aspx&lt;/a&gt; or in the link I have posted in the introduction.&lt;br /&gt;&lt;br /&gt;Note: You can configure an application assembly through the administrative tool .NET Configuration 1.1 (in Control Panel) by adding an assembly to the Applications section. The tool will generate the App.Config for you.</description><link>http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113995851879571539</guid><pubDate>Tue, 14 Feb 2006 23:00:00 +0000</pubDate><atom:updated>2006-02-18T02:03:12.746-08:00</atom:updated><title>Strong Named Assemblies</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;This article is one of a series of articles exploring .NET Assemblies.&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html&quot;&gt;Exploring .NET Assemblies&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html&quot;&gt;Multi-File Assemblies&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;Strong Named Assemblies&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html&quot;&gt;Placing Assemblies in the Global Assembly Cache (GAC)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html&quot;&gt;Administrative Configuration of Applications&lt;/a&gt;&lt;br /&gt;The articles are intended for programmers who are using .NET for a while, and wish to know more about .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Digital Signature is the way to ensure that information was not altered since it was signed.&lt;br /&gt;As developers we distribute our compiled assemblies to our clients. Both the developers and the clients want to make sure that the assemblies are exactly the ones that were first distributed. For example, a malicious programmer can hack into the assembly and alter it to his will. The least we can do is make sure that such thing did not happen before loading the assembly. It would be shameful, for example, if we tried to run an assembly signed by Microsoft, which was changed by someone without our knowledge.&lt;br /&gt;&lt;br /&gt;An assembly which is digitally signed is called a &lt;em&gt;Strongly-Named Assembly&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Process of Signing the Assembly&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In order to sign an assembly we need to do the following steps:&lt;br /&gt;1. From the command line, run the line &lt;strong&gt;&lt;em&gt;sn -k keys.snk&lt;/em&gt;&lt;/strong&gt; where keys.snk is the name of the file with the keys. This step will generate a file containing a private key and a public key.&lt;br /&gt;2. Open the project you want to sign in VS.NET, and in the AssemblyIbfo file, set the AssemblyKeyFile attribute with the path the snk file.&lt;br /&gt;&lt;strong&gt;[assembly: AssemblyKeyFile(&quot;..\\..\\keys.snk&quot;)]&lt;/strong&gt;&lt;br /&gt;Alternatively you can do this while linking in the command line:&lt;br /&gt;&lt;strong&gt;al /out:MyAssembly.dll /keyfile:keys.snk&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;After building the assembly if we open the assembly with ildasm, and watch the Manifest, we will be able to see the public key. So the public key is stored inside the assembly file.&lt;br /&gt;The signature appears somewhere at the end of the assembly and cannot be seen in the manifest.&lt;br /&gt;&lt;br /&gt;The snk file MUST be kept (in a secure way). Without this file you will not be able to sign the assembly with the same key anymore, and if you already deployed your dll, and there are existing executables referencing to this assembly, those executables will no longer work, unless deployed again with the new signed dll. Access to the snk file by malicious user will allow him or her to make changes to the assembly and sign it on your behalf with the same pair of keys. Be careful.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;How Do the Signing Process Work&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Now that we know technically how to sign the assembly, lets try to understand what exactly happens, and then try to evaluate the costs of signing.&lt;br /&gt;&lt;br /&gt;At the compilation of the dll: The dll IL code is hashed in a well known Hashing algorithm (SHA1). The hashing algorithm produces a word of a constant amount of bits. This word cannot be decrypted back to the assembly code. But, it is statistically almost sure that only the dll produced this code (finding another code that will be hashed to the same hash as the original code is not practical).&lt;br /&gt;After we have the hashed assembly we encrypt it in RSA algorithm using the private key from the snk file. Only the assembly knows the private key, so only the assembly can be encrypted using that key. As we saw before the public key is distributed inside the resulting assembly, so it is known to everybody.&lt;br /&gt;&lt;br /&gt;At the compilation of the referencing assembly: For every strong named assembly which is referenced the following process is done: The public key of the referenced assembly is hashed into a 16 bit word, which is called the public key token. You will be able to see this in the Manifest of the assembly after building using ildasm. It will appear under the appropriate reference.&lt;br /&gt;&lt;br /&gt;So far we discussed compilation time actions done by the compiler. There are no implications to performance so far.&lt;br /&gt;At run-time: Whenever a Strong Named assembly is loaded, the public key of the dll is first Hashed. The result of the hash should be the same as the public key token of that dll. In order to evaluate the signature, we Hash the dll. This will be the expected result after evaluating the digital signature. We take the digital signature of the dll assembly, and decipher it using RSA algorithm with the public key of the dll. The result should be the same as the hashed dll.&lt;br /&gt;If the comparison failed, we have a problem and we know for sure that the dll was tampered with. The CLR will refuse to load the assembly in that case.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Why Do We Sign Assemblies?&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The drawbacks of signing the assemblies are obvious. First, there is an overhead every time we load the assembly. The assembly will be loaded more slowly (Although this should not be too long). Second, we need to look after the snk file or we will loose the private key.&lt;br /&gt;&lt;br /&gt;The advantage is that by signing the assemblies we provide reliability to the assembly. The client and you will be sure that the assembly was provided by you (of course the signature has nothing to do with certification for the assembly, and the assembly can still be malicious if you programmed it so).&lt;br /&gt;&lt;br /&gt;There are cases where you must sign assemblies: If you place them in the GAC, or if you develop an Enterprise Service (COM+). There are more cases.&lt;br /&gt;&lt;br /&gt;It is a best practice to always sign assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Getting Rid of the Snk File&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;After generating the snk file (by calling &lt;strong&gt;sn -k&lt;/strong&gt;), we can call&lt;br /&gt;&lt;strong&gt;sn -i keys.snk AssemblyKey&lt;/strong&gt;&lt;br /&gt;Now in the AssemblyInfo file, instead of updating the AssemblyKeyFile attribute (leave empty string), we need to update AssemblyKeyName:&lt;br /&gt;&lt;strong&gt;[assembly: AssemblyKeyName(&quot;AssemblyKey&quot;)]&lt;/strong&gt;&lt;br /&gt;You can see that we can now use a string (&quot;AssemblyKey&quot; in this example) instead of an snk file.&lt;br /&gt;The file snk file can now be deleted from the computer. We don&#39;t need it.&lt;br /&gt;&lt;br /&gt;The &lt;em&gt;sn&lt;/em&gt; application put the keys in the folder&lt;br /&gt;&quot;C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys&quot; .&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Delay Signing&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In some scenarios, during development, the signing mechanism is unnecessary and inconvenient. In this case we can prepare the assembly for signing eventually, but for the time being tell the runtime not to validate the signature. When the appropriate time comes, and we wish to deploy the assembly, we will enable the signature validating. This process is called &lt;em&gt;Delay Signing&lt;/em&gt;.&lt;br /&gt;In order to delay sign the assembly:&lt;br /&gt;1. Create an snk file: &lt;strong&gt;sn -k keys.snk&lt;/strong&gt;.&lt;br /&gt;2. Create an snk file which contains only the public key: &lt;strong&gt;sn -p keys.snk publicKey.snk&lt;/strong&gt;&lt;br /&gt;3. Go to AssemblyInfo file, and change the &lt;em&gt;AssemblyDelaySign&lt;/em&gt; Attribute to true.&lt;br /&gt;&lt;strong&gt;[assembly: AssemblyDelaySign(true)]&lt;/strong&gt;&lt;br /&gt;4. Change the AssemblyKeyFile Attribute to the publicKey.snk file&lt;br /&gt;&lt;strong&gt;[assembly: AssemblyKeyFile(&quot;..\\..\\public.snk&quot;)]&lt;/strong&gt;&lt;br /&gt;5. Remove the request to validate the assembly: &lt;strong&gt;sn -Vr MyAssembly.dll&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;When we wish to stop the delay signing, and return to regular signing:&lt;br /&gt;1. Re-sign the assembly with the original snk file: &lt;strong&gt;sn -R MyAssembly.dll ..\..\keys.snk&lt;/strong&gt;&lt;br /&gt;2. Enable the signature validation: &lt;strong&gt;sn -Vu MyAssembly.dll&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;</description><link>http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113995795126545831</guid><pubDate>Tue, 14 Feb 2006 22:28:00 +0000</pubDate><atom:updated>2006-02-18T02:02:28.906-08:00</atom:updated><title>Multi-File Assemblies</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;This article is one of a series of articles exploring .NET Assemblies.&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html&quot;&gt;Exploring .NET Assemblies&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html&quot;&gt;Multi-File Assemblies&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;Strong Named Assemblies&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html&quot;&gt;Placing Assemblies in the Global Assembly Cache (GAC)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html&quot;&gt;Administrative Configuration of Applications&lt;/a&gt;&lt;br /&gt;The articles are intended for programmers who are using .NET for a while, and wish to know more about .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Assemblies are normally built into a single file (exe or dll). There is a feature to compile parts of the assembly into net-modules, and then build an assembly that consists of all the net-modules.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Creating Multi-File Assemblies&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The feature is not supported by the IDE, so we must compile in command line (using csc.exe r vbc.exe).&lt;br /&gt;Lets assume we have the following code:&lt;br /&gt;Class A (A.cs):&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;namespace ClassLibrary1&lt;br /&gt;{&lt;br /&gt;public class A&lt;br /&gt;{&lt;br /&gt;public void foo()&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(&quot;A.foo()&quot;);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------------&lt;br /&gt;Class B (B.cs)&lt;br /&gt;------------------------------------------------&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;namespace ClassLibrary1&lt;br /&gt;{&lt;br /&gt;public class B&lt;br /&gt;{&lt;br /&gt;public void foo()&lt;br /&gt;{&lt;br /&gt;A a = new A();&lt;br /&gt;a.foo();&lt;br /&gt;Console.WriteLine(&quot;A.foo()&quot;);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;----------------------------------------------&lt;br /&gt;As you can see B uses class A.&lt;br /&gt;&lt;br /&gt;Now start Command-Line (Start-&gt;Programs-&gt;.NET 2003-&gt;Tools-&gt;Command Line Prompt).&lt;br /&gt;Change directory to the folder of the files (&lt;code&gt; cd MyFolder &lt;/code&gt;).&lt;br /&gt;In that folder we have a.cs and b.cs .&lt;br /&gt;Type the following lines (one by one):&lt;br /&gt;----------------------------------------------&lt;code&gt;&lt;br /&gt;csc /t:module a.cs&lt;br /&gt;csc /t:module b.cs /addmodule:a.netmodule&lt;br /&gt;al /out:MyAssembly.dll /t:library a.netmodule b.netmodule&lt;br /&gt;&lt;/code&gt;---------------------------------------------&lt;br /&gt;&lt;br /&gt;Lets go over the code line by line:&lt;br /&gt;&lt;strong&gt;csc /t:module a.cs&lt;/strong&gt;&lt;br /&gt;This line will compile a.cs (and any other file you might specify) into one netmodule file.&lt;br /&gt;After the line is executed you can see in the folder a file called a.netmodule.&lt;br /&gt;If you view the a.netmodule using &lt;em&gt;ildasm&lt;/em&gt;, you can see that the structure of a.netmodule is similar to a regular assembly.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;csc /t:module b.cs /addmodule:a.netmodule&lt;/strong&gt;&lt;br /&gt;As in class A, cladd B which resides in b.cs is compiled into a netmodule. the /addmodule directive is used to add a &quot;reference&quot; to a.netmodule. The reason we need this is because B used class A, so much like dlls we need a reference to all the netmodules containing classes that are used in the current netmodule we are trying to compile.&lt;br /&gt;Again, a file called b.netmodule now exists.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;al /out:MyAssembly.dll /t:library a.netmodule b.netmodule&lt;/strong&gt;&lt;br /&gt;&lt;em&gt;al&lt;/em&gt; is the Assembly Linker. We use it in order to generate our Dll Assembly (using the /out directive). All the netmodules are specified.&lt;br /&gt;After running this code we see a file MyAssembly.dll in the folder. Lets run &lt;em&gt;ildasm&lt;/em&gt; on this assembly. There is only a Manifest to the assembly, and no types. Inside the manifest you will see the references to the netmodules. You can see that the dll do not contain the types, but it reference to them. This is important, because now we can replace the netmodules without the need to compile the assembly.&lt;br /&gt;&lt;br /&gt;Note: Because the IDE do not support Multi-file assemblies, you will need to create batch files which compile the code for you. This can be quite tedious.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Advantages of Multi File Assemblies&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Although the obvious disadvantage stated above, there are some benefits to this kind of deployment:&lt;br /&gt;1. We can use multiple languages inside one assembly. Each language should be compiled in its own netmodule. This can&#39;t be done otherwise.&lt;br /&gt;2. Modularity - We can replace netmodules without affecting other netmodules.&lt;br /&gt;3. Late Loading - The netmodule will only be loaded if and when used.&lt;br /&gt;4. Less network traffic while deploying. Only the changed netmodule will be transferred.&lt;br /&gt;&lt;br /&gt;Also this can be useful when we have an infrastructure assembly (which is shared by many solutions), with only a subset of classes changing from solution to solution. A netmodule can be useful to compile the changing part, without the need to compile the other parts of the assembly.</description><link>http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113994148570732756</guid><pubDate>Tue, 14 Feb 2006 18:23:00 +0000</pubDate><atom:updated>2006-02-18T02:01:34.206-08:00</atom:updated><title>Exploring .NET Assemblies</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;This article is one of a series of articles exploring .NET Assemblies.&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html&quot;&gt;Exploring .NET Assemblies&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/multi-file-assemblies.html&quot;&gt;Multi-File Assemblies&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/strong-named-assemblies.html&quot;&gt;Strong Named Assemblies&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/placing-assemblies-in-global-assembly.html&quot;&gt;Placing Assemblies in the Global Assembly Cache (GAC)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://j1hammer.blogspot.com/2006/02/administrative-configuration-of.html&quot;&gt;Administrative Configuration of Applications&lt;/a&gt;&lt;br /&gt;The articles are intended for programmers who are using .NET for a while, and wish to know more about .NET assemblies.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Assemblies are the smallest deployment units possible in the .NET environment. Assembly is the term to describe a .NET executable (.exe) or class library (dll) file. A .NET assembly contains two main parts: The Manifest and the IL code. The Manifest contains all the metadata of the assembly: The Version and ID of the assembly, public keys, references and more. The IL code is a binary representation of the intermediate language which is very similar to Assembly language. The fact that an IL code is in the assembly file, and not C# or VB.NET, enable us to program in any .NET language we want (currently there are more than 20 managed languages), and even combine languages. All of the code is compiled into the IL code, which is similar no matter what language you programmed in.&lt;br /&gt;&lt;br /&gt;There is not much of a difference between an Exe file and a Dll file. The obvious difference is the fact that an Exe file can be executed, while a Dll cannot be executed. Other than that the only difference is the Main method in the Exe code, which is the entry point of the Assembly. There is nothing else to distinguish between an Exe and a Dll. You can even reference an Exe just as you reference a Dll (It is not supported by the .NET 2003 IDE though, you can use the csc.exe compiler to add references to Exe files).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Solving The Dll Hell&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A known problem with native Dlls and COM components was called &quot;Dll Hell&quot;. In short, the problem was that no multiple versions were able to be stored at the same time, which caused problems in deployment.&lt;br /&gt;.NET solved the problem by putting versions inside the assemblies manifests. This way two assemblies with different versions can co-exist in the same computer.&lt;br /&gt;Another advantage of .NET is the ease of deployment. In order to run the application, the exe with all referenced dlls should be placed in the same windows folder (This is the most simple scenario). With a simple copy of files the application can be installed in a different location. No need of registry or any additional installations.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Exploring the Assembly&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;There is a very nice tool to explore the .NET assemblies called ildasm. To run ildasm you need to run the .NET Command Prompt (from the Start-&gt;Programs-&gt;.NET 2003-&gt;.NET Tools folder). Then by typing &quot;ildasm&quot; you start the application. The option &quot;ildasm /adv&quot; will show more properties of the assembly. Opening an Assembly (any .NET exe or dll file), will show its contents in a tree view. First we can see the Manifest file (by double clicking on it). We see the references, followed by the Assembly Info (as it appears in the AssemblyInfo attributes), resources and other information.&lt;br /&gt;After the Manifest we can see the Assembly types. Each type can be expanded to see all the methods, members, properties and events. By double-clicking on a method, for example, we can see the IL code of the method.&lt;br /&gt;&lt;br /&gt;Another option is the View-&gt;MetaInfo-&gt;Show! . Here is all the metadata which is exposed by the Reflection mechanism in .NET, among other things, such as list of constant strings used in the assembly. You can use File-&gt;Dump to save the assembly contents to a file.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Reflector&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A very nice (and free) tool is Reflector. This tool is a MUST. It can be downloaded from here: &lt;a href=&quot;http://www.aisto.com/roeder/dotnet/&quot;&gt;http://www.aisto.com/roeder/dotnet/&lt;/a&gt; . The tool has nicer UI than ildasm, and it can show you the code in C# and VB.NET. So you can view Dlls and Exes that you did not write, and even disassemble the code for you. You can even see the code of .NET Framework dlls, such as System.IO, System.Xml etc.&lt;br /&gt;Tons of Add-ins can be found here: &lt;a href=&quot;http://www.aisto.com/incoming/Reflector/AddIns/&quot;&gt;http://www.aisto.com/incoming/Reflector/AddIns/&lt;/a&gt; (search Google for more).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Everyone Can See Your Code&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Yes, this is true. You cannot hide your code. Everyone with ildasm or Reflector can disassemble your code and see it.&lt;br /&gt;First, this is not such a problem. Even for native assemblies, with some effort the exe and dll files can be disassembled. With the assumption that any hacker can see your code eventually, Microsoft did not bother to make it hard for people to disassemble the code.&lt;br /&gt;What you can do is make the code hard to read. For this there is a tool called Dotfuscator (which is installed with .NET). The tool makes the code less readable. Here is a link to articles about this tool: &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dotfuscator/dotf3e5x.asp&quot;&gt;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dotfuscator/dotf3e5x.asp&lt;/a&gt;</description><link>http://j1hammer.blogspot.com/2006/02/exploring-net-assemblies.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113845248961314145</guid><pubDate>Sat, 28 Jan 2006 11:55:00 +0000</pubDate><atom:updated>2006-01-28T13:01:43.986-08:00</atom:updated><title>Maintaining a List of All Connected Clients - A Remoting Tutorial</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;This tutorial continue the the list of Remoting tutorials in this blog. In my &lt;a href=&quot;http://j1hammer.blogspot.com/2005/12/recovering-from-serverclients.html&quot;&gt;last tutorial&lt;/a&gt; I showed how to recover from disconnecions both in the server and the client.&lt;br /&gt;Now I am going to demonstrate how to maintain a list of connected clients both in the server and the clients. At the end, we will have a window that looks like a chat room, with all the connected users listed on the form.&lt;br /&gt;&lt;br /&gt;The starting point of this tutorial is the end point of the previous one, so be sure to read my previous one first.&lt;br /&gt;&lt;br /&gt;Full source code (C#) can be downloaded from &lt;a href=&quot;http://www.hmr.co.il/Yariv/clientsManagerCS.zip&quot;&gt;here&lt;/a&gt; (~350 KB)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 1 - Create an IClientsManager Interface&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;In the Common assembly we will add a new interface called IClientsManager.&lt;/span&gt;&lt;br /&gt;Here is the code:&lt;br /&gt;-------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;using System.Collections;&lt;br /&gt;namespace Common&lt;br /&gt;{&lt;br /&gt;public interface IClientsManager&lt;br /&gt;{&lt;br /&gt;long RegisterNewClient();&lt;br /&gt;bool NotifyClientAlive(long clientId);&lt;br /&gt;bool IsClientConnected(long clientId);&lt;br /&gt;ICollection GetListOfActiveClients();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------------------&lt;br /&gt;The client will be identified by a unique id, given by the server. Later you can extend this to be a full ClientInfo class. For now a number will do.&lt;br /&gt;The interface IClientsManager has 4 methods. RegisterNewClient will be called by the client when it connects to the server. The server will return the new Id.&lt;br /&gt;NotifyClientAlive will be called by the client once a period. When the method is no longer called, the server can know that the client is disconnected and thus notify the other clients.&lt;br /&gt;IsClientConnected will help clients ask the server whether another client is connected.&lt;br /&gt;GetListOfActiveClients will be used by the clients to refresh their list of active clients.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 2 - Extending the Messages&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Before we jump for the implementation of the new interface we need to do some modifications.&lt;br /&gt;In order to distinguish between custom messages raised by the clients, and system messages such as client disconnections, we will create a new type of message: SystemMessage.&lt;br /&gt;&lt;br /&gt;First we will change a bit the Message class (in Common assembly)&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;[Serializable()]&lt;br /&gt;public class Message&lt;br /&gt;{&lt;br /&gt;public enum MessageType&lt;br /&gt;{&lt;br /&gt;Custom,&lt;br /&gt;System&lt;br /&gt;}&lt;br /&gt;public string _msg;&lt;br /&gt;public Message(string msg)&lt;br /&gt;{&lt;br /&gt;this._msg = msg;&lt;br /&gt;}&lt;br /&gt;public string Text&lt;br /&gt;{&lt;br /&gt;get { return _msg; }&lt;br /&gt;}&lt;br /&gt;public virtual MessageType Type&lt;br /&gt;{&lt;br /&gt;get { return MessageType.Custom;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------------&lt;br /&gt;&lt;br /&gt;What I&#39;ve done here is add an enumeration MessageType. If the message is a string message sent by a client (as before) then the message is a Custom message. If the message is sent by the server to notify about the connections or disconnection of a client then type is System.&lt;br /&gt;&lt;br /&gt;Now we will derive from Message to create a new class SystemMessage. I put the class in the Message.cs file right under the Message class.&lt;br /&gt;-------------------------------------------------------&lt;code&gt;&lt;br /&gt;[Serializable()]&lt;br /&gt;public class SystemMessage:Message&lt;br /&gt;{&lt;br /&gt;private SystemMessageType _type;&lt;br /&gt;private long _clientId;&lt;br /&gt;public enum SystemMessageType&lt;br /&gt;{&lt;br /&gt;ClientConnected,&lt;br /&gt;ClientDisconnected&lt;br /&gt;}&lt;br /&gt;public SystemMessage(SystemMessageType type, long clientId, string message):&lt;br /&gt;base(message)&lt;br /&gt;{&lt;br /&gt;_type = type;&lt;br /&gt;_clientId = clientId;&lt;br /&gt;}&lt;br /&gt;public override Common.Message.MessageType Type&lt;br /&gt;{&lt;br /&gt;get { return MessageType.System; }&lt;br /&gt;}&lt;br /&gt;public SystemMessageType SystemType&lt;br /&gt;{&lt;br /&gt;get { return _type; }&lt;br /&gt;}&lt;br /&gt;public long ClientId&lt;br /&gt;{&lt;br /&gt;get { return _clientId; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;---------------------------------------------------------&lt;br /&gt;The SystemMessageType enumeration contains client connected or client disconnected types of messages. Later we might want to add more system messages. The SystemType property holds this information.&lt;br /&gt;The ClientId property will be used in order to know which client connected or disconnected.&lt;br /&gt;&lt;br /&gt;Note: I must say that rethinking about this design, I don&#39;t like it. It is very hard to extend the Message using inheritance. I would much prefer to use a pattern such as the Tag property of a Control (extend the message by composition instead of inheritance). But lets leave it like this at this point.&lt;br /&gt;&lt;br /&gt;We will return later to modify further the Message class.&lt;br /&gt;&lt;br /&gt;Now we add a method, BroadcastSystemMessage to the Broadcaster class in the Server:&lt;br /&gt;--------------------------------------------&lt;code&gt;&lt;br /&gt;[Serializable()]&lt;br /&gt;public class Broadcaster:MarshalByRefObject,IBroadcaster&lt;br /&gt;{&lt;br /&gt;public event MessageArrivedHandler MessageArrived;&lt;br /&gt;public void BroadcastMessage(Message msg)&lt;br /&gt;{&lt;br /&gt;SafeInvokeEvent(msg);&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;public void BroadcastSystemMessage(SystemMessage msg)&lt;br /&gt;{&lt;br /&gt;SafeInvokeEvent(msg);&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;private void SafeInvokeEvent(Message msg)&lt;br /&gt;{&lt;br /&gt;if (MessageArrived == null)&lt;br /&gt;return;&lt;br /&gt;MessageArrivedHandler mah=null;&lt;br /&gt;foreach(Delegate del in MessageArrived.GetInvocationList())&lt;br /&gt;{&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;mah = (MessageArrivedHandler)del;&lt;br /&gt;mah(msg);&lt;br /&gt;}&lt;br /&gt;catch (Exception ex)&lt;br /&gt;{&lt;br /&gt;Console.Write(ex.Message);&lt;br /&gt;MessageArrived -= mah;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public override object InitializeLifetimeService()&lt;br /&gt;{&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------------&lt;br /&gt;The BroadcastMessage and BroadcastSystemMessages are identical. We only make a distinction in order for our code to be more readable. Note that the client cannot invoke this new method because we did not add the method to the IBroadcaster interface. This is good, because only the server should raise system events.&lt;br /&gt;By now you should have guessed that we are going to use our already implemented Broadcaster to distribute the connections and disconnections of clients. Thats the power of a good and solid well-designed system.&lt;br /&gt;&lt;br /&gt;Last at this step we need to extend the functionality of MessageBroadcaster in the client. The class will raise the SystemMessageArrived event, so the client&#39;s objects can easily distinguish between the two types of events:&lt;br /&gt;---------------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Net.Sockets;&lt;br /&gt;using System.Threading;&lt;br /&gt;using Common;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class MessageBroadcaster:IBroadcaster&lt;br /&gt;{&lt;br /&gt;private IBroadcaster _bcaster = null;&lt;br /&gt;private BroadcastEventHelper _eventHelper = null;&lt;br /&gt;public MessageBroadcaster()&lt;br /&gt;{&lt;br /&gt;if (ClientStartup.Connector.ServerAlive)&lt;br /&gt;{&lt;br /&gt;InitializeObjects();&lt;br /&gt;}&lt;br /&gt;ClientStartup.Connector.ServerDisconnected += new EventHandler(Connector_ServerDisconnected);&lt;br /&gt;ClientStartup.Connector.ServerReconnect += new EventHandler(Connector_ServerReconnect);&lt;br /&gt;}&lt;br /&gt;public void BroadcastMessage(Message msg)&lt;br /&gt;{&lt;br /&gt;if (_bcaster == null)&lt;br /&gt;throw new ServerNotAliveException();&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;_bcaster.BroadcastMessage(msg);&lt;br /&gt;}&lt;br /&gt;catch (SocketException ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;}&lt;br /&gt;catch (ThreadAbortException ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;private void HandleMessage(Message msg)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;switch (msg.Type)&lt;br /&gt;{&lt;br /&gt;case Message.MessageType.Custom:&lt;br /&gt;if (MessageArrived != null)&lt;br /&gt;MessageArrived(msg);&lt;br /&gt;break;&lt;br /&gt;case Message.MessageType.System:&lt;br /&gt;if (SystemMessageArrived != null)&lt;br /&gt;SystemMessageArrived(msg);&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;private void InitializeObjects()&lt;br /&gt;{&lt;br /&gt;_bcaster = (IBroadcaster)RemotingHelper.GetObject(typeof(IBroadcaster));&lt;br /&gt;_eventHelper = new BroadcastEventHelper();&lt;br /&gt;_eventHelper.MessageArrivedLocally += new MessageArrivedHandler(HandleMessage);&lt;br /&gt;_bcaster.MessageArrived += new MessageArrivedHandler(_eventHelper.LocallyHandleMessageArrived);&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerDisconnected(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;_bcaster = null;&lt;br /&gt;_eventHelper = null;&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerReconnect(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;InitializeObjects();&lt;br /&gt;}&lt;br /&gt;public event MessageArrivedHandler MessageArrived;&lt;br /&gt;&lt;strong&gt;public event MessageArrivedHandler SystemMessageArrived;&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;--------------------------------------------------------&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 3 - Implementing the ClientsManager Class&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Now we are more than ready to implement the IClientsManager interface. Add a new class to the Server assembly called ClientsManager, derived from MarshalByRefObject and implementing the new IClientsManager interface.&lt;br /&gt;First the code:&lt;br /&gt;------------------------------------------------&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections;&lt;br /&gt;using System.Timers;&lt;br /&gt;using Common;&lt;br /&gt;namespace Server&lt;br /&gt;{&lt;br /&gt;public class ClientsManager:MarshalByRefObject, IClientsManager&lt;br /&gt;{&lt;br /&gt;private Timer _aliveTimer = new Timer(1000);&lt;br /&gt;private Hashtable _currentClients = new Hashtable();&lt;br /&gt;private long _currentMaxClientId = -1;&lt;br /&gt;public ClientsManager()&lt;br /&gt;{&lt;br /&gt;_aliveTimer.Elapsed += new ElapsedEventHandler(aliveTimer_Elapsed);&lt;br /&gt;_aliveTimer.Start();&lt;br /&gt;}&lt;br /&gt;public override object InitializeLifetimeService()&lt;br /&gt;{&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;public long RegisterNewClient()&lt;br /&gt;{&lt;br /&gt;long id = ++_currentMaxClientId;&lt;br /&gt;_currentClients[id] = DateTime.Now.Ticks;&lt;br /&gt;Broadcaster bcast = (Broadcaster) RemoteSingletonObjectsList. Instance. GetRemoteSingletonObject( typeof(Broadcaster));&lt;br /&gt;bcast. BroadcastSystemMessage (new SystemMessage(SystemMessage.SystemMessageType. ClientConnected, id,&quot;&quot;));&lt;br /&gt;return id;&lt;br /&gt;}&lt;br /&gt;public bool NotifyClientAlive(long clientId)&lt;br /&gt;{&lt;br /&gt;if (IsClientConnected(clientId))&lt;br /&gt;{&lt;br /&gt;_currentClients[clientId] = DateTime.Now.Ticks;&lt;br /&gt;return true;&lt;br /&gt;}&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;public bool IsClientConnected(long clientId)&lt;br /&gt;{&lt;br /&gt;return _currentClients.ContainsKey(clientId);&lt;br /&gt;}&lt;br /&gt;public ICollection GetListOfActiveClients()&lt;br /&gt;{&lt;br /&gt;return _currentClients.Keys;&lt;br /&gt;}&lt;br /&gt;private void aliveTimer_Elapsed(object sender, ElapsedEventArgs e)&lt;br /&gt;{&lt;br /&gt;_aliveTimer.Stop();&lt;br /&gt;long now = DateTime.Now.Ticks;&lt;br /&gt;foreach (long id in _currentClients.Keys)&lt;br /&gt;{&lt;br /&gt;long lastTicks = (long)_currentClients[id];&lt;br /&gt;if (now - lastTicks &gt;= 20000000)&lt;br /&gt;{&lt;br /&gt;_currentClients.Remove(id);&lt;br /&gt;Broadcaster bcast = (Broadcaster) RemoteSingletonObjectsList. Instance.GetRemoteSingletonObject( typeof(Broadcaster));&lt;br /&gt;bcast.BroadcastSystemMessage( new SystemMessage(SystemMessage.SystemMessageType. ClientDisconnected,id,&quot;&quot;));&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;_aliveTimer.Start();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------------&lt;br /&gt;_currentClients is a HashTable - the keys are the clients Ids of connected clients. The values are time stamps. The Timer (_aliveTimer) will elapse every second, and check the timestamps. If a client failed to call NotifyClientAlive for more then 20 seconds or so then the server will remove the client from _currentClients and notify everybody using the BroadcastSystemMessage method of Broadcaster.&lt;br /&gt;When a client calls RegisterNewClient, it is added to _connectedClients, and a new Id is returned. The Ids are resources handled by this class. In our case the Ids are sequential, so each client gets an Id larger by 1 than the previous client. This way we ensure that the Ids are unique. A system event will be broadcasted, notifying everybody about the new client.&lt;br /&gt;Last, the NotifyClientAlive should be called by clients, passing their Ids, once in a while. The timestamp will be updated every call. This way if a client fails to notify the server, the _aliveTimer will clean it up.&lt;br /&gt;&lt;br /&gt;Next we register the new class for remoting. Open the App.Config of the server and add this line (you should already know where to put the line - in the &quot;service&quot; tag)&lt;br /&gt;------------------------------------------------------------&lt;code&gt;&lt;br /&gt;&amp;lt;wellknown mode= &quot;Singleton&quot; type= &quot;Server.ClientsManager, Server&quot; objectUri= &quot;ClientsManager.soap&quot;/&amp;gt;&lt;br /&gt;&lt;/code&gt;------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;We also need to register the class in Client&#39;s App.Config file. Add this line to the &quot;client&quot; tag:&lt;br /&gt;------------------------------------------------------------&lt;code&gt;&lt;br /&gt;&amp;lt;wellknown type= &quot;Common.IClientsManager, Common&quot; url= &quot;tcp://localhost:16784/ EventServer/ClientsManager.soap&quot;/&amp;gt;&lt;br /&gt;&lt;/code&gt;------------------------------------------------------------&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 4 - Changing ClientConnector Class in the Client&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The ClientConnector class was created in the previous tutorial. Its purose is throwing events whenever the client is connected and disconneted to the server.&lt;br /&gt;We will now add more functionality to this class.&lt;br /&gt;-------------------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Threading;&lt;br /&gt;using System.Timers;&lt;br /&gt;using Common;&lt;br /&gt;using Timer = System.Timers.Timer;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class ClientConnector&lt;br /&gt;{&lt;br /&gt;public ClientConnector()&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;this.ServerReconnect += new EventHandler (ClientConnector_ServerReconnect);&lt;br /&gt;this.ServerDisconnected += new EventHandler (ClientConnector_ServerDisconnected);&lt;br /&gt;&lt;/strong&gt;_serverConnectivityTimer = new Timer(1000);&lt;br /&gt;_serverConnectivityTimer.Elapsed += new ElapsedEventHandler(serverConnectivityTimer_Elapsed);&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;}&lt;br /&gt;public event EventHandler ServerDisconnected;&lt;br /&gt;public event EventHandler ServerReconnect;&lt;br /&gt;private Timer _serverConnectivityTimer;&lt;br /&gt;private void serverConnectivityTimer_Elapsed(object sender, ElapsedEventArgs e)&lt;br /&gt;{&lt;br /&gt;_serverConnectivityTimer.Stop();&lt;br /&gt;CheckServerConnectivity();&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;}&lt;br /&gt;private bool _serverIsAlive = false;&lt;br /&gt;private void CheckServerConnectivity()&lt;br /&gt;{&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;if (_serverIsAlive &amp;&amp;amp; IsClientIdValid)&lt;br /&gt;{&lt;br /&gt;IClientsManager manager = (IClientsManager)RemotingHelper.GetObject(typeof(IClientsManager));&lt;br /&gt;manager.NotifyClientAlive(this.ClientId);&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;IServerConnector serverConnector = (IServerConnector) RemotingHelper.GetObject (typeof(IServerConnector));&lt;br /&gt;bool isAlive = serverConnector.IsAlive;&lt;br /&gt;if (isAlive &amp;&amp;amp; !_serverIsAlive)&lt;br /&gt;SetServerAlive();&lt;br /&gt;}&lt;br /&gt;catch (Exception ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;Thread t = new Thread(new ThreadStart(SetServerNotAlive));&lt;br /&gt;t.Start();&lt;br /&gt;t.Join();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;private void SetServerAlive()&lt;br /&gt;{&lt;br /&gt;if (!_serverIsAlive &amp;&amp;amp; ServerReconnect != null)&lt;br /&gt;ServerReconnect(this,EventArgs.Empty);&lt;br /&gt;_serverIsAlive = true;&lt;br /&gt;}&lt;br /&gt;public void SetServerNotAlive()&lt;br /&gt;{&lt;br /&gt;if (_serverIsAlive &amp;&amp;amp; ServerDisconnected != null)&lt;br /&gt;ServerDisconnected(this,EventArgs.Empty);&lt;br /&gt;_serverIsAlive = false;&lt;br /&gt;}&lt;br /&gt;public bool ServerAlive&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{&lt;br /&gt;_serverConnectivityTimer.Stop();&lt;br /&gt;CheckServerConnectivity();&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;return _serverIsAlive;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;private long _clientId = -1;&lt;br /&gt;public event EventHandler ClientIdChanged;&lt;br /&gt;public long ClientId&lt;br /&gt;{&lt;br /&gt;get { return _clientId; }&lt;br /&gt;}&lt;br /&gt;public bool IsClientIdValid&lt;br /&gt;{&lt;br /&gt;get { return _clientId &gt;= 0; }&lt;br /&gt;}&lt;br /&gt;private void ClientConnector_ServerReconnect (object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;IClientsManager manager = (IClientsManager) RemotingHelper.GetObject (typeof(IClientsManager));&lt;br /&gt;_clientId = manager.RegisterNewClient();&lt;br /&gt;if (ClientIdChanged != null)&lt;br /&gt;ClientIdChanged(this,EventArgs.Empty);&lt;br /&gt;}&lt;br /&gt;private void ClientConnector_ServerDisconnected (object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;_clientId = -1;&lt;br /&gt;}&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------------------------&lt;br /&gt;the _clientId property is by default equal to -1. When the event ServerReconnect is raised, we assign the client with a new client Id, by calling RegisterNewClient (See step 3). The server will notify the other clients (Next step). When the event ServerDisconnected is raised we assign the client again with Id -1.&lt;br /&gt;We already had a timer to check if the server is alive (remember the ServerConnector class from the previous tutorial?). We now need to call NoitifyClientAlive in the server (See step 3).&lt;br /&gt;Last I added the ClientIdChanged, because it is a good practice to have &amp;lt;Property&amp;gt;Changed events.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 5 - Implementing the Client Side Object&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Add a new class to the Client assembly, called ConnectedClients.&lt;br /&gt;------------------------------------------------------------&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections;&lt;br /&gt;using Common;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class ConnectedClients&lt;br /&gt;{&lt;br /&gt;private MessageBroadcaster _message = new MessageBroadcaster();&lt;br /&gt;private ArrayList _activeClients = new ArrayList();&lt;br /&gt;public ConnectedClients()&lt;br /&gt;{&lt;br /&gt;_message.SystemMessageArrived += new MessageArrivedHandler(Message_SystemMessageArrived);&lt;br /&gt;ClientStartup.Connector.ServerDisconnected += new EventHandler(Connector_ServerDisconnected);&lt;br /&gt;ClientStartup.Connector.ServerReconnect += new EventHandler(Connector_ServerReconnect);&lt;br /&gt;}&lt;br /&gt;private void Message_SystemMessageArrived(Message msg)&lt;br /&gt;{&lt;br /&gt;SystemMessage systemMessage = msg as SystemMessage;&lt;br /&gt;switch (systemMessage.SystemType)&lt;br /&gt;{&lt;br /&gt;case SystemMessage.SystemMessageType.ClientConnected:&lt;br /&gt;AddClient(systemMessage.ClientId);&lt;br /&gt;break;&lt;br /&gt;case SystemMessage.SystemMessageType.ClientDisconnected:&lt;br /&gt;if (systemMessage.ClientId == ClientStartup.Connector.ClientId) return;&lt;br /&gt;_activeClients.Remove(systemMessage.ClientId);&lt;br /&gt;if (ClientRemoved != null)&lt;br /&gt;ClientRemoved(this,new ClientEventArgs( systemMessage.ClientId));&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;private void AddClient(long clientId)&lt;br /&gt;{&lt;br /&gt;if (clientId == ClientStartup.Connector.ClientId) return;&lt;br /&gt;_activeClients.Add(clientId);&lt;br /&gt;if (ClientAdded != null)&lt;br /&gt;ClientAdded(this,new ClientEventArgs(clientId));&lt;br /&gt;}&lt;br /&gt;public ArrayList ActiveClients&lt;br /&gt;{&lt;br /&gt;get { return _activeClients; }&lt;br /&gt;}&lt;br /&gt;public event ClientEventHandler ClientAdded;&lt;br /&gt;public event ClientEventHandler ClientRemoved;&lt;br /&gt;public event EventHandler ClientsCleared;&lt;br /&gt;public class ClientEventArgs:EventArgs&lt;br /&gt;{&lt;br /&gt;public long ClientId&lt;br /&gt;{&lt;br /&gt;get { return _clientId; }&lt;br /&gt;}&lt;br /&gt;public ClientEventArgs(long clientId)&lt;br /&gt;{&lt;br /&gt;this._clientId = clientId;&lt;br /&gt;}&lt;br /&gt;private long _clientId = -1;&lt;br /&gt;}&lt;br /&gt;public delegate void ClientEventHandler(object sender, ClientEventArgs e);&lt;br /&gt;private void Connector_ServerDisconnected(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;ClearClients();&lt;br /&gt;}&lt;br /&gt;private void ClearClients()&lt;br /&gt;{&lt;br /&gt;_activeClients.Clear();&lt;br /&gt;if (ClientsCleared != null)&lt;br /&gt;ClientsCleared(this,EventArgs.Empty);&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerReconnect(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;RefreshList();&lt;br /&gt;}&lt;br /&gt;public void RefreshList()&lt;br /&gt;{&lt;br /&gt;ClearClients();&lt;br /&gt;IClientsManager manager = (IClientsManager)RemotingHelper.GetObject(typeof(IClientsManager));&lt;br /&gt;ICollection clients = manager.GetListOfActiveClients();&lt;br /&gt;foreach (long id in clients)&lt;br /&gt;AddClient(id);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-----------------------------------------------------------&lt;br /&gt;I will now explain the class fully (It is quite long).&lt;br /&gt;First of all we have the ActiveClients property. This property is the whole purpose of this class, right?&lt;br /&gt;The class has 3 events: ClientAdded, ClientRemoved, and ClientsCleared (less important). The ClientEventHandler is defined within the scopr of this class, and the ClientEventArgs as well.&lt;br /&gt;The class uses three events:&lt;br /&gt;1. The SystemMessageArrived (See step 2) of MessageBroadcaster. The event indicate that a client connected or disconnected. In both cases we check that the event is not raised by chance by the client itself. After that we add the client Id or remove it from _activeClients list. Then we raise the ClientAdded or ClientRemoved event.&lt;br /&gt;2. ServerReconnect of ClientConnector. In this case we refresh the list of clients by calling GetListOfActiveClients (See step 3), and adding each client to _activeClients.&lt;br /&gt;3. ServerDisconnected of ClientConnector. We clear the clients list, and raise the ClientsCleared event.&lt;br /&gt;&lt;br /&gt;Next, we need to add an instance of the ConnectedClients class to ClientStartup class (same as we did for ClientConnector). Change the ClientStartup class as follows:&lt;br /&gt;-----------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Runtime.Remoting;&lt;br /&gt;using System.Windows.Forms;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class ClientStartup&lt;br /&gt;{&lt;br /&gt;private static ClientConnector _connector;&lt;br /&gt;public static ClientConnector Connector&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{ return _connector; }&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;private static ConnectedClients _clients;&lt;br /&gt;public static ConnectedClients ConnectedClients&lt;br /&gt;{&lt;br /&gt;get { return _clients; }&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;[STAThread]&lt;br /&gt;static void Main()&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;InitRemoting&lt;/strong&gt;(); &lt;/code&gt;&lt;br /&gt;&lt;code&gt;_connector = new ClientConnector();&lt;br /&gt;&lt;strong&gt;_clients = new ConnectedClients();&lt;/strong&gt;&lt;br /&gt;Application.Run(new ClientForm());&lt;br /&gt;}&lt;br /&gt;private static void InitRemoting()&lt;br /&gt;{&lt;br /&gt;string fileName = &quot;client.exe.config&quot;;&lt;br /&gt;RemotingConfiguration.Configure(fileName);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-----------------------------------------&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 6 - Add a Client Id to the Message&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;We can now add to the Message the property SenderId in order to know which client sent the message (we did not know this information before). For the special case of the Server sending messages, we will set the SERVER_ID to be -100 (arbitrarily selected).&lt;br /&gt;Change the Message class (in Common assembly) as follows:&lt;br /&gt;-------------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;[Serializable()]&lt;br /&gt;public class Message&lt;br /&gt;{&lt;br /&gt;public enum MessageType&lt;br /&gt;{&lt;br /&gt;Custom,&lt;br /&gt;System&lt;br /&gt;}&lt;br /&gt;public string _msg;&lt;br /&gt;&lt;strong&gt;protected long _senderId;&lt;br /&gt;&lt;/strong&gt;public Message(&lt;strong&gt;long senderId&lt;/strong&gt;, string msg)&lt;br /&gt;{&lt;br /&gt;this._msg = msg;&lt;br /&gt;&lt;strong&gt;this._senderId = senderId;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;public string Text&lt;br /&gt;{&lt;br /&gt;get { return _msg; }&lt;br /&gt;}&lt;br /&gt;public virtual MessageType Type&lt;br /&gt;{&lt;br /&gt;get { return MessageType.Custom;}&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;public static long SERVER_ID&lt;br /&gt;{&lt;br /&gt;get { return -100; }&lt;br /&gt;}&lt;br /&gt;public string TextWithSenderId&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{&lt;br /&gt;if (!IsSenderServer)&lt;br /&gt;return &quot;Client &quot; + SenderId.ToString() + &quot;: &quot; + Text;&lt;br /&gt;else&lt;br /&gt;return &quot;Server: &quot; + Text;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;public virtual long SenderId&lt;br /&gt;{&lt;br /&gt;get { return _senderId; }&lt;br /&gt;}&lt;br /&gt;public bool IsSenderServer&lt;br /&gt;{&lt;br /&gt;get { return _senderId == SERVER_ID; }&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;[Serializable()]&lt;br /&gt;public class SystemMessage:Message&lt;br /&gt;{&lt;br /&gt;private SystemMessageType _type;&lt;br /&gt;private long _clientId;&lt;br /&gt;public enum SystemMessageType&lt;br /&gt;{&lt;br /&gt;ClientConnected,&lt;br /&gt;ClientDisconnected&lt;br /&gt;}&lt;br /&gt;public SystemMessage(SystemMessageType type, long clientId, string message):&lt;br /&gt;base(&lt;strong&gt;SERVER_ID&lt;/strong&gt;, message)&lt;br /&gt;{&lt;br /&gt;_type = type;&lt;br /&gt;_clientId = clientId;&lt;br /&gt;}&lt;br /&gt;public override Common.Message.MessageType Type&lt;br /&gt;{&lt;br /&gt;get { return MessageType.System; }&lt;br /&gt;}&lt;br /&gt;public SystemMessageType SystemType&lt;br /&gt;{&lt;br /&gt;get { return _type; }&lt;br /&gt;}&lt;br /&gt;public long ClientId&lt;br /&gt;{&lt;br /&gt;get { return _clientId; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------------------&lt;br /&gt;Notice the TextWithSenderId method, which formats the message with the identity of the sender.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Demo&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Lets test things. We will modify both ClientForm and ServerForm.&lt;br /&gt;&lt;br /&gt;ServerForm:&lt;br /&gt;So far we had a ListBox for messages. We will add another ListBox for the clients list.&lt;br /&gt;This is the code (without theauto-generated code):&lt;br /&gt;------------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;public class ServerForm : Form&lt;br /&gt;{&lt;br /&gt;private ListBox lsbMessages;&lt;br /&gt;private Button btnSend;&lt;br /&gt;&lt;strong&gt;private ListBox lsbConnectedClients;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;private Container components = null;&lt;br /&gt;public ServerForm()&lt;br /&gt;{&lt;br /&gt;InitializeComponent();&lt;br /&gt;_bcast = (Broadcaster) RemoteSingletonObjectsList.Instance. GetRemoteSingletonObject( typeof(Broadcaster));&lt;br /&gt;_bcast.MessageArrived += new MessageArrivedHandler(bcast_MessageArrived);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;(...)&lt;br /&gt;&lt;br /&gt;private Broadcaster _bcast = null;&lt;br /&gt;public void bcast_MessageArrived(Message msg)&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;if (msg.Type == Message.MessageType.Custom)&lt;br /&gt;{&lt;br /&gt;&lt;/strong&gt;_msgToShow = msg;&lt;br /&gt;Thread t = new Thread(new ThreadStart(ShowMessage));&lt;br /&gt;t.Start();&lt;br /&gt;&lt;strong&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;HandleSystemMessage((SystemMessage)msg);&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;&lt;strong&gt;private void HandleSystemMessage (SystemMessage systemMessage)&lt;br /&gt;{&lt;br /&gt;switch (systemMessage.SystemType)&lt;br /&gt;{&lt;br /&gt;case SystemMessage.SystemMessageType. ClientConnected:&lt;br /&gt;lsbConnectedClients.Items.Add(&quot;Client &quot; + systemMessage.ClientId);&lt;br /&gt;break;&lt;br /&gt;case SystemMessage.SystemMessageType. ClientDisconnected:&lt;br /&gt;lsbConnectedClients.Items.Remove(&quot;Client &quot; + systemMessage.ClientId);&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;private Message _msgToShow=null;&lt;br /&gt;private void ShowMessage()&lt;br /&gt;{&lt;br /&gt;if (_msgToShow == null) return;&lt;br /&gt;lsbMessages.Items.Add(_msgToShow.TextWithSenderId);&lt;br /&gt;lsbMessages.SelectedIndex = lsbMessages.Items.Count -1;&lt;br /&gt;_msgToShow = null;&lt;br /&gt;}&lt;br /&gt;private long index=0;&lt;br /&gt;private void btnSend_Click(object sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;_bcast.BroadcastMessage(new Message(&lt;strong&gt;Message.SERVER_ID&lt;/strong&gt;, &quot;Server: This is message #&quot; + (index++).ToString()) );&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-----------------------------------------------------&lt;br /&gt;&lt;br /&gt;ClientForm:&lt;br /&gt;Again, we add another ListBox to show the list of active clients. Here is the code (without the auto-generated code):&lt;br /&gt;----------------------------------------------------------&lt;code&gt;&lt;br /&gt;&lt;br /&gt;public class ClientForm : System.Windows.Forms.Form&lt;br /&gt;{&lt;br /&gt;private System.Windows.Forms.ListBox lsbMessages;&lt;br /&gt;private System.Windows.Forms.Button btnSendMessage;&lt;br /&gt;&lt;strong&gt;private System.Windows.Forms.ListBox lsbClients;&lt;/strong&gt;&lt;br /&gt;private System.ComponentModel.Container components = null;&lt;br /&gt;public ClientForm()&lt;br /&gt;{&lt;br /&gt;InitializeComponent();&lt;br /&gt;_messageBroadcaster.MessageArrived += new Common.MessageArrivedHandler(broadcaster_MessageArrived);&lt;br /&gt;&lt;strong&gt;ClientStartup.ConnectedClients.ClientAdded += new Client.ConnectedClients.ClientEventHandler (ConnectedClients_ClientAdded);&lt;br /&gt;ClientStartup.ConnectedClients.ClientRemoved += new Client.ConnectedClients.ClientEventHandler (ConnectedClients_ClientRemoved);&lt;br /&gt;ClientStartup.ConnectedClients.ClientsCleared += new EventHandler(ConnectedClients_ClientsCleared);&lt;br /&gt;ClientStartup.Connector.ClientIdChanged += new EventHandler (Connector_ClientIdChanged);&lt;br /&gt;&lt;/strong&gt;ClientStartup.Connector.ServerDisconnected += new EventHandler(Connector_ServerDisconnected);&lt;br /&gt;ClientStartup.Connector.ServerReconnect += new EventHandler (Connector_ServerReconnect);&lt;br /&gt;&lt;strong&gt;if (ClientStartup.Connector.IsClientIdValid) &lt;/strong&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;strong&gt;{&lt;br /&gt;this.Text = &quot;Client &quot; + ClientStartup.Connector.ClientId; &lt;/strong&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;strong&gt;ClientStartup.ConnectedClients.RefreshList();&lt;/strong&gt; &lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;strong&gt;}&lt;br /&gt;else&lt;br /&gt;this.Text = &quot;Client Not Connected&quot;;&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;&lt;br /&gt;(...)&lt;br /&gt;&lt;br /&gt;MessageBroadcaster _messageBroadcaster = new MessageBroadcaster();&lt;br /&gt;private long _index=0;&lt;br /&gt;private void btnSendMessage_Click (object sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;Thread t = new Thread(new ThreadStart(SendMessage));&lt;br /&gt;t.Start();&lt;br /&gt;}&lt;br /&gt;private void SendMessage()&lt;br /&gt;{&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;Message msg = new Message (&lt;strong&gt;ClientStartup.Connector.ClientId,&lt;/strong&gt; &quot;This is message #&quot; + (_index++).ToString());&lt;br /&gt;_messageBroadcaster.BroadcastMessage(msg);&lt;br /&gt;msg = null;&lt;br /&gt;}&lt;br /&gt;catch (ServerNotAliveException ex)&lt;br /&gt;{&lt;br /&gt;lsbMessages.Items.Add(ex.Message);&lt;br /&gt;lsbMessages.SelectedIndex = lsbMessages.Items.Count-1;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;private void broadcaster_MessageArrived(Message msg)&lt;br /&gt;{&lt;br /&gt;lsbMessages.Items.Add(&lt;strong&gt;msg.TextWithSenderId&lt;/strong&gt;);&lt;br /&gt;lsbMessages.SelectedIndex = lsbMessages.Items.Count-1;&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerDisconnected(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;this.Text = &quot;Client Not Connected&quot;;&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerReconnect(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;this.Text =&lt;strong&gt; &quot;Client &quot; + ClientStartup.Connector.ClientId&lt;/strong&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;strong&gt;private void Connector_ClientIdChanged(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;this.Text = &quot;Client &quot; + ClientStartup.Connector.ClientId;&lt;br /&gt;}&lt;br /&gt;private void ConnectedClients_ClientAdded(object sender, Client.ConnectedClients.ClientEventArgs e)&lt;br /&gt;{&lt;br /&gt;lsbClients.Items.Add(&quot;Client &quot; + e.ClientId.ToString());&lt;br /&gt;}&lt;br /&gt;private void ConnectedClients_ClientRemoved(object sender, Client.ConnectedClients.ClientEventArgs e)&lt;br /&gt;{&lt;br /&gt;lsbClients.Items.Remove(&quot;Client &quot; + e.ClientId.ToString());&lt;br /&gt;}&lt;br /&gt;private void ConnectedClients_ClientsCleared(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;lsbClients.Items.Clear();&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;&lt;/code&gt;----------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Run the server and few instances of the client. See the Ids of the clients in the header of the form, and the lists update both in the server and the clients form. Try to disconnect the server and the client. Try to send messages.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;This was a tutorial for a fully functional server-client application using Remoting.NET technology. At this point you have a generic Broadcaster for events, a reconnections mechanism, and a list of connected clients. I don&#39;t think that I have ever seen such a tutorial for Remoting over the net (you are welcome to correct me on this).&lt;br /&gt;&lt;br /&gt;The next step will be the most impressive (in my opinion): a Multicaster class, which will enable distributing events to selected clients instead of all the clients.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Comments&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Two things I don&#39;t like about the code I have posted. The first is the use of inheritance to create a SystemMessage (you saw at step 2 how much work was involved, and how many layers were effected). The second is the use of long Id instead of a class to represent the Client. It should not be hard to fix the second problem.&lt;br /&gt;&lt;br /&gt;I will finish my (very long) article by saying that this was a very complex code to design, program and document. It works as far as I can tell, but if you see a bug, or a better way to do something, you are more than welcome to comment. Any bugs I will try to fix.</description><link>http://j1hammer.blogspot.com/2006/01/maintaining-list-of-all-connected.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113684110908791032</guid><pubDate>Mon, 09 Jan 2006 20:50:00 +0000</pubDate><atom:updated>2006-01-09T13:32:22.640-08:00</atom:updated><title>DegreesUpDown Control</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The code I will post here is for a custom Control called &lt;em&gt;DegreesUpDown&lt;/em&gt;. The control is similar to a NumericUpDown only it shows degrees (from 0 to 359). The nice thing about it is that the degrees are shown in 3 digits (000, 050, 180, etc...).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Code&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;You might have thought that the control inherits from NumericUpDown. Well, it does not. It inherits from &lt;em&gt;DomainUpDown&lt;/em&gt; which is much more flexible. I cannot format the way numbers look in NumericUpDown, so the trick is to convert strings into numeric values.&lt;br /&gt;&lt;br /&gt;------------------------------------------------&lt;code&gt;&lt;br /&gt;Imports System.ComponentModel&lt;br /&gt;Imports CommonControls.CGraphicsCommon&lt;br /&gt;Imports System.Drawing&lt;br /&gt;&lt;br /&gt;Public Class DegreesUpDown&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Inherits System.Windows.Forms.DomainUpDown&lt;br /&gt;&lt;br /&gt;#Region &quot; Windows Form Designer generated code &quot;&lt;br /&gt;Public Sub New()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;MyBase.New()&lt;br /&gt;&#39;This call is required by the Windows Form Designer.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;InitializeComponent()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;InitializeGraphics()&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&#39;UserControl overrides dispose to clean up the component list.&lt;br /&gt;Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If disposing Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If Not (components Is Nothing) Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;components.Dispose()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;MyBase.Dispose(disposing)&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&#39;Required by the Windows Form Designer&lt;br /&gt;Private components As System.ComponentModel.IContainer&lt;br /&gt;&#39;NOTE: The following procedure is required by the Windows Form Designer&lt;br /&gt;&#39;It can be modified using the Windows Form Designer.&lt;br /&gt;&#39;Do not modify it using the code editor.&lt;br /&gt;&amp;lt;System.Diagnostics.DebuggerStepThrough()&amp;gt; _&lt;br /&gt;Private Sub InitializeComponent()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;DegreesUpDown&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Wrap = True&lt;br /&gt;End Sub&lt;br /&gt;#End Region&lt;br /&gt;&lt;br /&gt;Private Sub InitializeGraphics()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Font = CommonFont&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.BackColor = Color.FromArgb(238, 238, 238)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.BorderStyle = BorderStyle.FixedSingle&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Private Sub &lt;strong&gt;InitValues&lt;/strong&gt;()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Items.Clear()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim vals As ArrayList = New ArrayList(360)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;For i As Integer = 359 To 0 Step -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vals.Add(ConvertTo3Digits(i))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Next&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Items.AddRange(vals)&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Public Shared Function &lt;strong&gt;ConvertTo3Digits&lt;/strong&gt;(ByVal i As Integer) As String&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (i &amp;gt;= 100) Then Return i.ToString()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (i &amp;gt;= 10) Then Return &quot;0&quot; + i.ToString()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (i &amp;gt;= 0) Then Return &quot;00&quot; + i.ToString()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return &quot;000&quot;&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;&amp;lt;Browsable(False)&amp;gt; _&lt;br /&gt;Public Shadows ReadOnly Property Items() As DomainUpDownItemCollection&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return MyBase.Items&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Get&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&amp;lt;Browsable(False), _&lt;br /&gt;Bindable(True)&amp;gt; _&lt;br /&gt;Public Property &lt;strong&gt;Value&lt;/strong&gt;() As Long&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (IsNothing(Me.SelectedItem)) Then Return 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return Long.Parse(Me.SelectedItem)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Set(ByVal Value As Long)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If IsNothing(Value) Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedItem = &quot;000&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RaiseEvent ValueChanged(Me, EventArgs.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Exit Property&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (Value &amp;gt;= 360 Or Value &amp;lt; 0) Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Text = Value.ToString&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixHeading()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedItem = ConvertTo3Digits(Value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RaiseEvent ValueChanged(Me, EventArgs.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&amp;lt;Category(&quot;Action&quot;), _&lt;br /&gt;Description(&quot;Raised when the value is changed&quot;)&amp;gt; _&lt;br /&gt;Public Event &lt;strong&gt;ValueChanged&lt;/strong&gt; As EventHandler&lt;br /&gt;&lt;br /&gt;Private Sub DegreesUpDown_SelectedItemChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.SelectedItemChanged&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;RaiseEvent ValueChanged(Me, EventArgs.Empty)&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Protected Overrides Sub OnCreateControl()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Items.Clear()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If Not DesignMode Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim vals As ArrayList = New ArrayList(360)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;For i As Integer = 359 To 0 Step -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vals.Add(ConvertTo3Digits(i))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Next&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Items.AddRange(vals)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedIndex = 359&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.Text = &quot;000&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Private Sub DegreesUpDown_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Leave&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixHeading()&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Private Sub FixHeading()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim i As Integer = Integer.Parse(Me.Text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If i &amp;lt; 0 Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedIndex = ((-i) Mod 360) - 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If i &amp;gt; 359 Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedIndex = 359 - (i Mod 360)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If i &amp;lt; 359 And i &amp;gt; 0 Then _&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedIndex = 359 - i&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Catch ex As Exception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Me.SelectedIndex = 359&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Try&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Protected Overrides Sub UpdateEditText()&lt;br /&gt;Try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dim i As Integer = Integer.Parse(Me.Text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (i &amp;gt; 359 Or i &amp;lt; 0) Then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixHeading()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If Me.Text.Length &amp;lt;&amp;gt; 3 Then _&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixHeading()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Catch ex As Exception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixHeading()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MyBase.UpdateEditText()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Try&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&amp;lt;Browsable(False)&amp;gt; _&lt;br /&gt;Public Shadows Property Text() As String&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Return MyBase.Text&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Set(ByVal Value As String)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MyBase.Text = Value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/code&gt;------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Explanations&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;On initialization, in method &lt;em&gt;InitValues&lt;/em&gt;, I create 360 strings (000,001,...,359), and add them to the items collection of the DomainUpDown base. The method &lt;em&gt;ConvertTo3Digits&lt;/em&gt; does the convertion from int to 3 digits string.&lt;br /&gt;In order to make the control fill more like a NumericUpDown, I added a &lt;em&gt;Value&lt;/em&gt; property, which is numeric. Convertion to string and vice versa is done. The event &lt;em&gt;ValueChanged&lt;/em&gt; also emulate the corresponding event of NumericUpDown control.&lt;br /&gt;The &lt;em&gt;Items&lt;/em&gt; and &lt;em&gt;Text&lt;/em&gt; properties is no longer needed for use, so they are shadowed and marked as Browsable false.&lt;br /&gt;&lt;br /&gt;FixHeading method is a nice feature - when the user enters a number out of range 0..359, the control automatically convert the number to the appropriate degree (For example: -90 =&gt; 270).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Extensions&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;You can extend the control by adding an Increment property (pressing on the UpDown buttons as of now only increase or decrease by one).&lt;br /&gt;Also, you can add Maximum and Minimum properties.&lt;br /&gt;In addition, the range -180..180 can be useful.</description><link>http://j1hammer.blogspot.com/2006/01/degreesupdown-control.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113674438644614054</guid><pubDate>Sun, 08 Jan 2006 18:15:00 +0000</pubDate><atom:updated>2006-01-09T12:49:54.246-08:00</atom:updated><title>Print Screen And Save It to File</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;A task which is quite common is to press the Print Screen button, and save the screen in Paint application as an Image file.&lt;br /&gt;If you ever wondered how to perform such a task in .NET, here is the solution.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The following snippet of code does the trick:&lt;br /&gt;------------------------------------------&lt;code&gt;&lt;br /&gt;SendKeys.Send(&quot;+{PRTSC}&quot;);&lt;br /&gt;if (Clipboard.GetDataObject() != null)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;IDataObject data = Clipboard.GetDataObject();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (data.GetDataPresent(DataFormats.Bitmap))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Image image = (Image)data.GetData(DataFormats.Bitmap,true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;image.Save(&quot;image.jpg&quot;,ImageFormat.Jpeg); // pick your format and path&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------&lt;br /&gt;&lt;br /&gt;First we use the &lt;em&gt;Sendkeys&lt;/em&gt; class. This class is not very famous, but it is quite nice. Whenever you want to emulate a key press you can call the Send method with the key you want to press. In our case we don&#39;t want just a key - we want to press the Print Screen key combined with the Ctrl key. &lt;em&gt;&quot;+&quot; &lt;/em&gt;stands for Ctrl, and &lt;em&gt;&quot;PRTSC&quot;&lt;/em&gt; stands for the Print Screen key.&lt;br /&gt;&lt;br /&gt;From here on its all about the &lt;em&gt;Clipboard&lt;/em&gt;. A press on Print Screen actually copies the screen image as it is into the clipboard. Instead of pasting it in Paint and saving it, we will use the Clipboard class. &lt;em&gt;GetDataObject&lt;/em&gt; is the method to get the object currently in the clipboard. Unfortunately, we only get an &lt;em&gt;IDataObject&lt;/em&gt; interface, without any knowledge of the actual type. So we call &lt;em&gt;GetDataPresent&lt;/em&gt; to varify that the object is indeed an image. Once we are there, we call the &lt;em&gt;GetData&lt;/em&gt; method which finally reveals the &lt;em&gt;Image&lt;/em&gt; object. The &lt;em&gt;Save&lt;/em&gt; method will store the image in any format we like and in any location.</description><link>http://j1hammer.blogspot.com/2006/01/print-screen-and-save-it-to-file.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113666045349710454</guid><pubDate>Sat, 07 Jan 2006 18:48:00 +0000</pubDate><atom:updated>2006-01-08T10:31:19.183-08:00</atom:updated><title>Using JoinView to Show Queries From Multiple Tables</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In MS-Access there is the possibility to show queries from two tables. Take for example the case of Order and Product tables in the Northwind database. Say we want to show in a grid the amount of items purchased (per Order) with the appropriate product name and price (from the Product table). We are able to do so if there is a relation between the tables, and by using SQL Join clause.&lt;br /&gt;This effect is not available in ADO.NET. Once the data is filled into the DataSet, there is no built-in way to produce this kind of query. Sure, we can show a Master-Detail view of the data. But in order to show data from several tables in one DataGrid at once we will need to work hard. DataGrid can only show data from one DataTable at a time, and DataView can only reflect data of one table.&lt;br /&gt;This is a very big problem. One way of solving it is getting the data into a DataSet from the database itself using a Select Command with Join clauses. The DataSet should have a DataTable with the same schema as the resulting Join query. While this is possible in some cases, other cases require some kind of mechanism to have a joined view without accessing the database.&lt;br /&gt;For example, say I have a server and clients. The server does all the access to the database. The client gets the DataSets from the server. If I want to show the data in a DataGrid in the client, I cannot afford to approach the server again. Not only this is expensive, but it can also cause concurrency problems. I have all the data I need in memory, so why do I need to access the database again?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Microsoft comes to our aid. Obviously someone understood the problem, so he or she developed a class called &lt;strong&gt;JoinView&lt;/strong&gt; which acts like a DataView class, but handles data with multiple DataTables with DataRelations set between them.&lt;br /&gt;The code for the class (VB.NET), with examples of usage can be found here: &lt;a href=&quot;http://support.microsoft.com/kb/325682/EN-US/&quot;&gt;http://support.microsoft.com/kb/325682/EN-US/&lt;/a&gt;&lt;br /&gt;C# source code can be found here: &lt;a href=&quot;http://www.valley.ru/~dmoiseev/q325682_csh.html&quot;&gt;http://www.valley.ru/~dmoiseev/q325682_csh.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This class is the ONLY convenient way I could find to show the data from few related tables in one table without getting the data in this structure in the first place.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Disadvantages&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Unfortunately, the JoinView class is not as convenient as the DataView class. Here are few of the drawbacks I could find when using this class:&lt;br /&gt;1. You &lt;strong&gt;cannot&lt;/strong&gt; edit the &lt;em&gt;related&lt;/em&gt; columns - only the main table columns. The rest are read-only and I couldn&#39;t get passed that.&lt;br /&gt;2. You &lt;strong&gt;cannot&lt;/strong&gt; dynamically filter the rows!!! you will need to create a new JoinView whenever the filter is changed. This is a huge drawback, as instanciating a JoinView is probably expensive.&lt;br /&gt;3. Apparently the AllowNew property works well if you you set it BEFORE you assign the JoinView as a DataSource to the grid. If you set the AllowNew property to False after you assign the DataSource, the result is unexpected (I got the Add New Row displayed, and when the user pressed on this line, an exception was thrown, saying: &quot;AddNew is not allowed&quot;).&lt;br /&gt;4. It works with column styles, but i needed to make some little changes.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Summary&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Basically it was a good experience working with JoinView, although it is not perfect. As I said before, it is currently the only solution i know of to work with Join without getting the data from the database in that structure.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Some Thoughts&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Why has Microsoft not provided us a proper way of doing a Join on the DataSet objects in-the-box? We have DataTables and DataRelations in .NET so the JoinView class seems to be a required piece. More importantly - someone realized the need, otherwise the JoinView class would not have been published - so why it is not provided in .NET 2005?&lt;br /&gt;&lt;br /&gt;Why can&#39;t we use Command objects on DataSets? By allowing us to use SQL commands on DataSets we will have more strength in using DataSets.</description><link>http://j1hammer.blogspot.com/2006/01/using-joinview-to-show-queries-from.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113649061359781550</guid><pubDate>Thu, 05 Jan 2006 19:20:00 +0000</pubDate><atom:updated>2006-01-06T02:21:41.493-08:00</atom:updated><title>Setting the Row Height of the Grid to Fit the Contents</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Intorduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;It is very hard to set the row height of the DataGrid (Framework 1.1). Actually I was very surprised that it is not a built-in feature. Not only it is not built-in, but also it is a very non-trivial task.&lt;br /&gt;&lt;br /&gt;Suppose I have a row with text containing the \n\r characters (meaning new line). In this case I need the row to have multiple lines. Try and put a text with this characters in a normal DataGrid - only the first row will appear, and the other lines can be reached by using the up and down arrw keys. I will show a way of showing all the lines.&lt;br /&gt;&lt;br /&gt;The source code in this article was inspired from &lt;a href=&quot;http://www.codeproject.com/cs/miscctrl/WCsAutoSizeDataGrid.asp&quot;&gt;this&lt;/a&gt; article in &lt;a href=&quot;http://www.codeproject.com&quot;&gt;CodeProject&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;If you followed my previous articles in my blog, you can guess that the solution involves ColumnStyles. You will need to create your own custom DataGridColumnStyle and use it in order to populate the grid. A reminder of ColumnStyles can be found &lt;a href=&quot;http://j1hammer.blogspot.com/2005/10/how-to-use-datagridcolumnstyles.html&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First, I implement a class called &lt;em&gt;MultiLineColumnStyle&lt;/em&gt; derived from DataGridTextBoxColumn. This class has a property &lt;em&gt;MultiLine&lt;/em&gt; which when set to true will show all the lines in the cell.&lt;br /&gt;The trick of resizing the height of the row is in the &lt;em&gt;GetMinimumHeight&lt;/em&gt; overrided method.&lt;br /&gt;Here is the code&lt;br /&gt;------------------------------------------------------------&lt;code&gt;&lt;br /&gt;public class MultiLineColumnStyle:DataGridTextBoxColumn&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;public bool &lt;strong&gt;MultiLine&lt;/strong&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ return _multiLine; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ _multiLine = value; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;private int _currentCell = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;private bool _multiLine = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void ResetIterations()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_currentCell = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected override int &lt;strong&gt;GetMinimumHeight&lt;/strong&gt;()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!_multiLine)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return base.GetMinimumHeight();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Get CurrencyManager&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CurrencyManager cur = (CurrencyManager)this.DataGridTableStyle.DataGrid.BindingContext[this.DataGridTableStyle.DataGrid.DataSource, this.DataGridTableStyle.DataGrid.DataMember];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Rows available?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(cur == null cur.Count == 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return base.GetMinimumHeight();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Increment counter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this._currentCell ++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Initialize return value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int retVal = base.GetMinimumHeight();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Calculate height of row at currentIteration 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retVal = this.CalcStringHeight(GetColumnValueAtRow(cur,currentCell - 1).ToString());&lt;br /&gt;// Reset when last cell reached&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(_currentCell == cur.Count)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.ResetIterations(); // sets currentIteration to 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return retVal;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;private int &lt;em&gt;CalcStringHeight&lt;/em&gt;(string s)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Create graphics for calculation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Drawing.Graphics g = this.TextBox.CreateGraphics();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Do measure, and add a bit (4 pixels for me)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (int)g.MeasureString(s,this.TextBox.Font).Height + 4;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Error, return default font height.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return base.GetMinimumHeight();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;------------------------------------------------------------&lt;br /&gt;The GetMinimumHeight is called for each cell seperately starting with the topmost cell in the column. So the _currentCell variable is used to run through the cells - each call will increment this (thanks to the above website for the great idea).&lt;br /&gt;Obtaining the CurrencyManager (to get the text of the cell) of the column is another problem, which is solved in a very long and not straight-forward way.&lt;br /&gt;One last problem is the actual calculation of the height of the row which is done in the CalcStringHeight method - by using MeasureString method.&lt;br /&gt;&lt;br /&gt;Indeed, very hard work!!!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Demo&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Add to the main form of the application a DataGrid. Here is the Form.Load event handler:&lt;br /&gt;---------------------------------------------------&lt;code&gt;&lt;br /&gt;private void Form1_Load(object sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DataTable dt = new DataTable(&quot;MyTable&quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dt.Columns.Add(new DataColumn(&quot;multiline&quot;,typeof(string)) );&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataGrid1.DataSource = dt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DataRow dr;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dr = dt.NewRow();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dr[&quot;multiline&quot;] = &quot;vsvsdfsdf\n\rdfgdfgsd\n\rcvsdvgsf&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dt.Rows.Add(dr);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dr = dt.NewRow();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dr[&quot;multiline&quot;] = &quot;gsddg\n\rasfsfswef&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dt.Rows.Add(dr);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DataGridTableStyle ts = new DataGridTableStyle();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ts.MappingName = &quot;MyTable&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;em&gt;MultiLineColumnStyle cs = new MultiLineColumnStyle();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/em&gt;cs.MappingName = &quot;multiline&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;cs.HeaderText = &quot;multiline&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;em&gt;cs.MultiLine = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/em&gt;ts.GridColumnStyles.Add(cs);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataGrid1.TableStyles.Add(ts);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;-------------------------------------------------&lt;br /&gt;Run the application, and you will see that all the rows have appropriate height.</description><link>http://j1hammer.blogspot.com/2006/01/setting-row-height-of-grid-to-fit.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113640028840306743</guid><pubDate>Wed, 04 Jan 2006 18:16:00 +0000</pubDate><atom:updated>2006-01-05T11:19:37.246-08:00</atom:updated><title>Loading and Validating Xml Files</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;Source code in VB (with demo) can be downloaded from &lt;a href=&quot;http://www.hmr.co.il/Yariv/ValidatingxmlDocument.zip&quot;&gt;here&lt;/a&gt;&lt;/span&gt;&lt;a href=&quot;http://www.hmr.co.il/Yariv/ValidatingxmlDocument.zip&quot;&gt; &lt;/a&gt;(~35 KB)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;An Xml should be Well-Formed:&lt;br /&gt;- must begin with the XML declaration&lt;br /&gt;- must have one unique root element&lt;br /&gt;- all start tags must match end-tags&lt;br /&gt;- XML tags are case sensitive&lt;br /&gt;- all elements must be closed&lt;br /&gt;- all elements must be properly nested&lt;br /&gt;- all attribute values must be quoted&lt;br /&gt;- XML entities must be used for special characters&lt;br /&gt;&lt;br /&gt;However, being Well-Formed is not enough. There can still be errors in the Xml. Those errors are in the logical aspects, rather than the syntax. Xml Schemas help us define the business rules of our Xmls. Xml Schemas are written in Xml and they support Data Types.&lt;br /&gt;&lt;br /&gt;It is a good practice to use Xml Schemas always. Don&#39;t write just Xmls. Use schemas as well!!!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Xml and Xml Schemas In .NET&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;As you probably know there is a wide support for Xml in the .NET . The class XmlDocument is the DOM implementation in the .NET Framework. This is a very strong class. However, there is no easy support for loading and validating the Xml against its Schema.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Validating Against the Schema&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;My friend,&lt;strong&gt; Dror Givoli&lt;/strong&gt;, who works with me, supplied a code to a class, &lt;strong&gt;ValidatingXmlDocument&lt;/strong&gt;, derived from &lt;em&gt;XmlDocument&lt;/em&gt;, which has a method called LoadAndValidate, with tons of overloads. Here are few:&lt;br /&gt;----------------------------------------------------------- &lt;code&gt;&lt;br /&gt;Public Overloads Sub LoadAndValidate(ByVal filename As String, ByVal Schema As XmlSchema)&lt;br /&gt;   MyBase.Load(filename)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub LoadAndValidate(ByVal inStream As Stream, ByVal Schema As XmlSchema)&lt;br /&gt;   MyBase.Load(inStream)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub LoadAndValidate(ByVal txtReader As TextReader, ByVal Schema As XmlSchema)&lt;br /&gt;   MyBase.Load(txtReader)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub LoadAndValidate(ByVal reader As XmlReader, ByVal Schema As XmlSchema)&lt;br /&gt;   MyBase.Load(reader)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;br /&gt;Public Sub LoadXmlAndValidate(ByVal xml As String, ByVal Schema As XmlSchema)&lt;br /&gt;   MyBase.LoadXml(xml)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub LoadAndValidate(ByVal filename As String, ByVal schemaFileName As String)&lt;br /&gt;   Dim Schema As XmlSchema = XmlSchema.Read(New XmlTextReader(schemaFileName), AddressOf ValidationEventHandler)&lt;br /&gt;   MyBase.Load(filename)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;End Sub&lt;/code&gt;&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;There are more. The Validate method is as follows:&lt;br /&gt;---------------------------------------------------- &lt;code&gt;&lt;br /&gt;&lt;strong&gt;Public Sub Validate(ByVal Schema As XmlSchema)&lt;br /&gt;   Dim ms As New MemoryStream&lt;br /&gt;   Me.Save(ms)&lt;br /&gt;   ms.Position = 0&lt;br /&gt;   Dim rdr As New XmlTextReader(ms)&lt;br /&gt;   Dim vrdr As New XmlValidatingReader(rdr)&lt;br /&gt;   vrdr.Schemas.Add(Schema)&lt;br /&gt;   AddHandler vrdr.ValidationEventHandler, AddressOf ValidationEventHandler&lt;br /&gt;   While vrdr.Read&lt;br /&gt;   End While&lt;br /&gt;End Sub&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Private Sub ValidationEventHandler(ByVal sender As Object, ByVal args As ValidationEventArgs)&lt;br /&gt;   Throw New XmlValidationException(args)&lt;br /&gt;End Sub &lt;/code&gt;&lt;br /&gt;--------------------------------------------------&lt;br /&gt;Last there is an implementations of the method SaveAndValidate, again with tons of overloads:&lt;br /&gt;-------------------------------------------------- &lt;code&gt;&lt;br /&gt;Public Overloads Sub SaveAndValidate(ByVal filename As String, ByVal Schema As XmlSchema)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;   Save(filename)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub SaveAndValidate(ByVal outStream As System.IO.Stream, ByVal Schema As XmlSchema)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;   Save(outStream)&lt;br /&gt;End Sub&lt;br /&gt;Public Overloads Sub SaveAndValidate(ByVal writer As System.IO.TextWriter, ByVal Schema As XmlSchema)&lt;br /&gt;   Validate(Schema)&lt;br /&gt;   Save(writer)&lt;br /&gt;End Sub &lt;/code&gt;&lt;br /&gt;-----------------------------------------&lt;br /&gt;And there are more overloads.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Demo&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;Here is an example of how to use this class:&lt;br /&gt;------------------------------------------&lt;br /&gt;&lt;code&gt;Dim vDoc As New ValidatingXmlDocument&lt;br /&gt;Dim xmlFile As String = &quot;c:\MyXmlFile.xml&quot;&lt;br /&gt;Dim schemaFile As String = &quot;c:\MySchemaFile.xsd&quot;&lt;br /&gt;Try&lt;br /&gt;   vDoc.LoadAndValidate(xmlFile, schemaFile)&lt;br /&gt;Catch ex As XmlValidationException&lt;br /&gt;   Console.Out.WriteLine(ex.Args.Message)&lt;br /&gt;End Try &lt;/code&gt;&lt;br /&gt;------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Acknowledgment&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;This code was given to me by Dror Givoli, in order to post it in my blog. Thanks Dror for sharing your knowledge with others.</description><link>http://j1hammer.blogspot.com/2006/01/loading-and-validating-xml-files.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113475063141444255</guid><pubDate>Fri, 16 Dec 2005 16:13:00 +0000</pubDate><atom:updated>2005-12-16T08:47:15.633-08:00</atom:updated><title>Xml Schema Reader Application</title><description>&lt;span style=&quot;color:#000000;&quot;&gt;Download XmlSchemaReader.zip from &lt;a href=&quot;http://www.hmr.co.il/Yariv/XmlSchemaReader.zip&quot;&gt;here&lt;/a&gt; (~200 KB)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Intoduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;There are several ways to handle Xmls in .NET . My favourite is DataSet.ReadXml method. It is fast, clean, efficient and productive. &lt;/span&gt;&lt;br /&gt;Xml files are not that different from databases, although their use is somewhat different. The use of DataSet helps us as developers to use one API (which is ADO.NET) in order to handle both relational data when it is brought from the database or when it is brought from Xml files.&lt;br /&gt;&lt;br /&gt;In order to read Xmls into Typed Datasets, or to make them valid, we should use Xml Schemas (xsd files). Now, designing Xml Schemas is a whole new ballgame. We can use the designer of VS.NET to help us, but I personally find it very awkward. Most of the time I know how my Xml file should look. I just don&#39;t know how the Schema file should look, and coding (or designing) it is a real pain. I also don&#39;t know how the DataSet will look when I import my Xml. Which elements are tables, which are fields, and so on. Sometimes DataRelations will be created for me by the ReadXml method, and primary keys too.&lt;br /&gt;&lt;br /&gt;One more case which I find difficult, is when I get an already prepared xsd file, and it can be big. The person who designed the xsd may not even have an Xml file example for me. So it can be very difficult to work with it (I will need few hours spent to investigate the schema).&lt;br /&gt;&lt;br /&gt;So here comes XmlSchemaReader for our aid.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;The Application&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style=&quot;color:#000000;&quot;&gt;XmlSchemaReader is a simple tool, written in C# by me, which is designed to address these issues. You simply load the Xml or Xsd file (by clicking on xml or xsd button, and then selecting it from the Open Dialog). The application will tell you if it is illegal to read it into a DataSet.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After the file is loaded the DataTables are seen in the ListBox in the left. When the selecting a table, the data is seen in the grid in Data tab. The data can be edited and then saved by pressing the Save button (be careful - it will be saved into the file specified in the TextBox). A neat feature is to attach the schema into an Xml file with data (By clicking Save Schema). So I can write my xml first (outside, in notepad for example), and then generate the schema later.&lt;br /&gt;&lt;br /&gt;Two more tabs are Column and Relations. The Columns tab will show you the generated columns for each table, the datatype, and if it is a primary key (+ or -). Last, the Relations tab will show you the generated relations from the Xml.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Best Practice&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I suggest you follow these steps:&lt;br /&gt;1. Write the xml in Notepad, as you think it should look.&lt;br /&gt;2. Load the xml to the XmlSchemaReader application.&lt;br /&gt;3. Look at the tables and columns. Add data if you wish and save. I suggest you create a backup before saving.&lt;br /&gt;4. Save the schema into a seperate file, or into the xml file.&lt;br /&gt;5. Edit the schema outside the XmlSchemaReader application. For example, change types of columns.&lt;br /&gt;6. Whenever you need a view, load the file into the application.&lt;br /&gt;&lt;br /&gt;It really saved me a lot of time!!!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Last Words&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;I am giving the XmlSchemaReader for free, without any profit on my part. Please do not distribute it, or use it in production versions. Please use it only for your own use. Read the License file before using it. Feel free to advertise me of course ;)&lt;br /&gt;&lt;br /&gt;Obviousely improvements can be made to the interface and capabilities. I will probably get there, but the tool is really helpful even as it is. You may post comments on any failures, bugs, or suggestions you have.</description><link>http://j1hammer.blogspot.com/2005/12/xml-schema-reader-application.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-18006460.post-113390737776842637</guid><pubDate>Tue, 06 Dec 2005 22:03:00 +0000</pubDate><atom:updated>2005-12-30T11:01:37.930-08:00</atom:updated><title>Recovering From Server/Clients Disconnections in Remoting</title><description>&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;In the &lt;a href=&quot;http://j1hammer.blogspot.com/2005/11/creating-broadcast-events-server-using.html&quot;&gt;previous article&lt;/a&gt; I showed how to create a Broadcast Event Server. The clients could send messages through the server to all the other connected clients.&lt;br /&gt;One of the problems you have maybe seen is that if the client starts and the server is not working, than you will get an exception: &quot;Additional information: No connection could be made because the target machine actively refused it&quot;.&lt;br /&gt;On this tutorial I will show how to improve our Client/Server system by automatically connect the clients to the server once the server starts, get notifications about connectivity problems, and recover the connection.&lt;br /&gt;&lt;br /&gt;Download &lt;a href=&quot;http://www.hmr.co.il/Yariv/ServerDisconnections.zip&quot;&gt;Full Source Code&lt;/a&gt; (~300KB)&lt;br /&gt;New: Download the Full Source Code in &lt;a href=&quot;http://www.hmr.co.il/Yariv/ServerDisconnections_VB.zip&quot;&gt;VB&lt;/a&gt; (~22KB)&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 1 – Create a ServerConnector Class&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;First we add another interface to Common project. This interface is IServerConnector interface:&lt;br /&gt;------------------------------------------------------------&lt;br /&gt;&lt;code&gt;&lt;br /&gt;namespace Common&lt;br /&gt;{&lt;br /&gt;public interface IServerConnector&lt;br /&gt;{&lt;br /&gt;bool IsAlive&lt;br /&gt;{&lt;br /&gt;get;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;As you can see the interface has one read-only property IsAlive. This property will be called by the client periodically in order to check if the server is running.&lt;br /&gt;Next we add a class called ServerConnector to the Server project:&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using Common;&lt;br /&gt;&lt;br /&gt;namespace Server&lt;br /&gt;{&lt;br /&gt;[Serializable()]&lt;br /&gt;public class ServerConnector:MarshalByRefObject,IServerConnector&lt;br /&gt;{&lt;br /&gt;public bool IsAlive&lt;br /&gt;{&lt;br /&gt;get { return true; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override object InitializeLifetimeService()&lt;br /&gt;{&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;This class will be called remotely by the client once in a while. If the return value is true then all is well. If the client gets an exception, then the server is not functioning.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:#006600;&quot;&gt;&lt;strong&gt;Step 2 – Create a ClientConnector Class&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;Now that we have our ServerConnector class, we need to call the IsAlive method. We create a class that will have a Timer, and every period of time we will call ServerConnector. The class will have two events: ServerConnected and ServerDisconnected. This events can be used later by other objects in the client in order to provide functionality in those two states.&lt;br /&gt;Add the following ClientConnector class to the Client project:&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Threading;&lt;br /&gt;using System.Timers;&lt;br /&gt;using Common;&lt;br /&gt;using Timer = System.Timers.Timer;&lt;br /&gt;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class ClientConnector&lt;br /&gt;{&lt;br /&gt;public ClientConnector()&lt;br /&gt;{&lt;br /&gt;_serverConnectivityTimer = new Timer(1000);&lt;br /&gt;_serverConnectivityTimer.Elapsed += new ElapsedEventHandler (serverConnectivityTimer_Elapsed);&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public event EventHandler ServerDisconnected;&lt;br /&gt;public event EventHandler ServerReconnect;&lt;br /&gt;&lt;br /&gt;private Timer _serverConnectivityTimer;&lt;br /&gt;private void serverConnectivityTimer_Elapsed(object sender, ElapsedEventArgs e)&lt;br /&gt;{&lt;br /&gt;_serverConnectivityTimer.Stop();&lt;br /&gt;CheckServerConnectivity();&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private bool _serverIsAlive = false;&lt;br /&gt;private void CheckServerConnectivity()&lt;br /&gt;{&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;IServerConnector serverConnector = (IServerConnector) RemotingHelper. GetObject(typeof(IServerConnector));&lt;br /&gt;bool isAlive = serverConnector.IsAlive;&lt;br /&gt;if (isAlive &amp;&amp;amp; !_serverIsAlive)&lt;br /&gt;SetServerAlive();&lt;br /&gt;}&lt;br /&gt;catch (Exception ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;Thread t = new Thread(new ThreadStart (SetServerNotAlive));&lt;br /&gt;t.Start();&lt;br /&gt;t.Join();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void SetServerAlive()&lt;br /&gt;{&lt;br /&gt;if (!_serverIsAlive &amp;&amp;amp; ServerReconnect != null)&lt;br /&gt;ServerReconnect(this,EventArgs.Empty);&lt;br /&gt;_serverIsAlive = true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void SetServerNotAlive()&lt;br /&gt;{&lt;br /&gt;if (_serverIsAlive &amp;&amp;amp; ServerDisconnected != null)&lt;br /&gt;ServerDisconnected(this,EventArgs.Empty);&lt;br /&gt;_serverIsAlive = false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public bool ServerAlive&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{&lt;br /&gt;_serverConnectivityTimer.Stop();&lt;br /&gt;CheckServerConnectivity();&lt;br /&gt;_serverConnectivityTimer.Start();&lt;br /&gt;return _serverIsAlive;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;The timer elapses every second. A call to ServerConnector.IsAlive is done. If no exception was thrown, the server is connected (we raise the ServerReconnected event only if the state has changed). If an exception was thrown, we know that the server is not connected, and we can raise the ServerDisconnected event (only if soemthing has changed).&lt;br /&gt;&lt;br /&gt;We now have a nice generic class to tell us when the server connects and when it disconnects.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 3 – Hook things up&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;First we need to register our remote objects in the configuration file.&lt;br /&gt;Add the following line to the Server App.Config (inside the service tags):&lt;br /&gt;&lt;code&gt;&amp;lt;wellknown mode=&quot;Singleton&quot; type=&quot;Server.ServerConnector, Server&quot; objectUri= &quot;ServerConnector.soap&quot;/&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Add the following line to the Client App.Config (inside the client tags):&lt;br /&gt;&lt;code&gt;&amp;lt;wellknown type=&quot;Common.IServerConnector, Common&quot; url= &quot;tcp://localhost:16784/EventServer/ ServerConnector.soap&quot;/&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Second, we change the ClientStartup class to contain an instance of ClientConnector. This will be the instance that all objects in the client will use:&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Runtime.Remoting;&lt;br /&gt;using System.Windows.Forms;&lt;br /&gt;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;public class ClientStartup&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;private static ClientConnector _connector;&lt;br /&gt;public static ClientConnector Connector&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{ return _connector; }&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;[STAThread]&lt;br /&gt;static void Main()&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;_connector = new ClientConnector();&lt;br /&gt;&lt;/strong&gt;InitRemoting();&lt;br /&gt;Application.Run(new ClientForm());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static void InitRemoting()&lt;br /&gt;{&lt;br /&gt;string fileName = &quot;client.exe.config&quot;;&lt;br /&gt;RemotingConfiguration.Configure(fileName);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Next, we add changes to MessageBroadcaster in the client. We want the class to get the singleton objects from the server when the server reconnects (since we loose them when the server disconnects). Additionally we re-set the events (Once the server disconnects we loose the events).&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Net.Sockets;&lt;br /&gt;using System.Threading;&lt;br /&gt;using Common;&lt;br /&gt;&lt;br /&gt;namespace Client&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;public class MessageBroadcaster:IBroadcaster&lt;br /&gt;{&lt;br /&gt;private IBroadcaster _bcaster = null;&lt;br /&gt;private BroadcastEventHelper _eventHelper = null;&lt;br /&gt;&lt;br /&gt;public MessageBroadcaster()&lt;br /&gt;{&lt;br /&gt;&lt;strong&gt;if (ClientStartup.Connector.ServerAlive)&lt;br /&gt;{&lt;br /&gt;InitializeObjects();&lt;br /&gt;}&lt;br /&gt;ClientStartup.Connector.ServerDisconnected += new EventHandler (Connector_ServerDisconnected);&lt;br /&gt;ClientStartup.Connector.ServerReconnect += new EventHandler (Connector_ServerReconnect);&lt;br /&gt;&lt;/strong&gt;}&lt;br /&gt;&lt;br /&gt;public void BroadcastMessage(Message msg)&lt;br /&gt;{&lt;br /&gt;if (_bcaster == null)&lt;br /&gt;throw new ServerNotAliveException();&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;_bcaster.BroadcastMessage(msg);&lt;br /&gt;}&lt;br /&gt;catch (SocketException ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;}&lt;br /&gt;catch (ThreadAbortException ex)&lt;br /&gt;{&lt;br /&gt;Console.WriteLine(ex.Message);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void HandleMessage(Message msg)&lt;br /&gt;{&lt;br /&gt;if (MessageArrived != null)&lt;br /&gt;MessageArrived(msg);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void InitializeObjects()&lt;br /&gt;{&lt;br /&gt;_bcaster = (IBroadcaster)RemotingHelper. GetObject(typeof(IBroadcaster));&lt;br /&gt;_eventHelper = new BroadcastEventHelper();&lt;br /&gt;_eventHelper.MessageArrivedLocally += new MessageArrivedHandler HandleMessage);&lt;br /&gt;_bcaster.MessageArrived += new MessageArrivedHandler (_eventHelper.LocallyHandleMessageArrived);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;private void Connector_ServerDisconnected(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;_bcaster = null;&lt;br /&gt;_eventHelper = null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void Connector_ServerReconnect(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;InitializeObjects();&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;public event MessageArrivedHandler MessageArrived;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;strong&gt;&lt;span style=&quot;color:#006600;&quot;&gt;Step 4 – Fix the Demo&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;If you want to see the changes, you need to do the following modification to the ClientForm:&lt;br /&gt;Change the constructor of the ClientForm:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public ClientForm()&lt;br /&gt;{&lt;br /&gt;InitializeComponent();&lt;br /&gt;_messageBroadcaster.MessageArrived += new Common.MessageArrivedHandler (broadcaster_MessageArrived);&lt;br /&gt;ClientStartup.Connector.ServerDisconnected += new EventHandler (Connector_ServerDisconnected);&lt;br /&gt;ClientStartup.Connector.ServerReconnect += new EventHandler (Connector_ServerReconnect);&lt;br /&gt;if (ClientStartup.Connector.ServerAlive)&lt;br /&gt;this.Text = &quot;Client Connected&quot;;&lt;br /&gt;else&lt;br /&gt;this.Text = &quot;Client Not Connected&quot;;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Add the following event handlers to the form:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;private void Connector_ServerDisconnected(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;this.Text = &quot;Client Not Connected&quot;;&lt;br /&gt;}&lt;br /&gt;private void Connector_ServerReconnect(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;this.Text = &quot;Client Connected&quot;;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now you can run the server or the clients in any order you wish. If the client starts and the server is not started you will see it in the Text of the client form. If the server disconnects and reconnects you also see it in the clients. And most importantly – the client and server continue to function as they should.</description><link>http://j1hammer.blogspot.com/2005/12/recovering-from-serverclients.html</link><author>noreply@blogger.com (Yariv Hammer)</author><thr:total>9</thr:total></item></channel></rss>