Archive for category Technology

Review Of Windows Azure Virtual Machines Plus Some Scripts To Help You Get Started

I’ve been using Windows Azure Virtual Machines somewhat extensively over the past 6 weeks. I wanted to take a few minutes to give you my thoughts and impressions along with the scripts that I use to create and destroy VMs on demand.

The Good

When everything is setup and working properly, it’s a very powerful platform, and for the most part, the pricing seems very fair, considering what you get. It’s relatively user-friendly, and it doesn’t take too long to get up and running with a basic infrastructure. More complicated setups will of course require a larger time investment, but with the PowerShell scripting interface, a lot can be accomplished.

The Bad

It’s kind of flaky right now, unfortunately. Creating VMs is not always successful. Sometimes you end up creating a VM that simply won’t start. The only solution is to destroy it and try again. Also, sometimes when you restart a functioning VM from within Windows, the machine shuts down but never starts back up again. You then have to manually start it from the Windows Azure Management Console. Documentation is currently rather scattered and still changing rapidly. I expect that in the coming months and years, Microsoft will dramatically improve the offering, but as it stands today, Azure definitely has some problems quirks. Below I will give you all of the details of my setup, along with the scripts that I use to create and destroy VMs, as well as to start and stop persistent VMs. I will, of course, also tell you about the various issues that I have encountered in the past or that I continue to encounter during normal usage.

The Details

First let me say that I’m a big supporter of Microsoft products, in general. As a sysadmin, I am intimately acquainted with many Microsoft offerings, having designed and administered numerous systems. I believe that Microsoft is on the ball, at least when it comes to their software products. Does this mean that I agree with every decision that Microsoft has made over the years with all of their products, licensing, and privacy issues etc? No. Of course not. However, I do believe that the modern computing world owes to Microsoft much more than most people are willing to concede.

With regard to Azure, I have a relatively simple requirement for my virtual infrastructure. My goal was to setup a small Windows domain with a couple of core, persistent infrastructure servers, while giving myself the flexibility to scale up the number of member servers in the domain from anywhere between 5 and 100.

The first thing that I needed to do was upload a Windows image to my Azure account. This image would be the baseline image for all of my virtual machines. Seems harmless enough, but unfortunately the Azure user interface does not provide you with this capability. If you want to upload your own image, as many/most Azure customers will want to do, you have to do it via the PowerShell command line interface. I found myself annoyed that there was no direct support for this operation in the UI, but I was able to complete the process without too much trouble, following the steps outlined here.

I then needed to create my first VM. One key thing to keep in mind when you create VMs is the -ServiceName parameter in the New-AzureVM cmdlet. Essentially, the current implementation of Azure Virtual Machines assumes that every virtual machine that you want to create will need to be accessible on the public internet. You are not required to expose your VMs, but you are given the ability to do so if you desire. As such, each VM you create must have an associated DNS name and Cloud Service name, so that Microsoft can handle the public to private mappings. For my needs, public accessibility isn’t necessary. I really only need to be able to access a couple of VMs from my personal computer. The rest of the VMs simply need to be able to see one another. They do not need to be accessible from my personal computer, as long as they are accessible to the couple of VMs that *are* accessible from my personal computer. It’s fine that the current Azure implementation doesn’t really account for or anticipate this form of usage, but it’s also not clearly documented by Microsoft, which makes things confusing. Furthermore, if you do not specify a unique -ServiceName, the error you get when you execute New-AzureVM is misleading. So, for the sake of efficiency, just take my word for it and make sure to specify a unique -ServiceName. Since I will not have any two virtual machines with the same name, it is sufficient for me to simply use the $vmName as the -ServiceName in addition to the name for the actual virtual machine.

Create a Standalone VM that is Not Joined to a Domain (Script)

This is the script that I use to create virtual machines on Azure that are NOT joined to any domain. So, when I created my first domain controller, I used this script. And now, any time I need a new standalone VM, I use this script. Before using this script, you will want to setup a Network name and an Affinity Group name inside of the Azure management portal. Any virtual machines that are created using the same Network name will automatically be able to communicate with one another:

##Create a standalone VM that is not joined to any domain
 
Set-AzureSubscription "subscription_name_goes_here" -CurrentStorageAccount "storage_account_name_goes_here"
$vmName = 'vm_name_goes_here'
$location = 'East US'
$imageName = 'image_name_goes_here'
$adminUsername = 'username_goes_here'
$adminPassword = 'password_goes_here'
$instanceSize = 'ExtraSmall'
$cloudServiceName = '' #Turns out that this must be unique for every VM, and so we're not using this variable and instead below you'll notice we are instead using the $vmName.  It actually gets used as both the Cloud Service name AND the DNS Name of the VM, which is counter intuitive.
$VNetName = 'network_name_goes_here'
$affinityGroup = 'affinitygroup_name_goes_here'
 
#we attempt to remove the service before creating the VM to avoid errors where the service already exists, which can happen if the script errors early on a previous run
remove-azureservice -servicename $vmName -Force
 
New-AzureVMConfig -Name $vmName -InstanceSize $InstanceSize -ImageName $imageName |
Add-AzureProvisioningConfig -Windows -Password $adminPassword -AdminUsername $adminUsername |
New-AzureVM -ServiceName $vmName -Location $location -VNetName $VNetName -AffinityGroup $affinityGroup

Create a VM that is Joined to a Domain (Script)

Next I needed to create some member VMs that would automatically be joined to my domain during the creation process. The ability to do this smoothly is key for my needs. Below is the script I use to do that. Unfortunately, this process is not always smooth. The important thing to remember here is that this script cannot be run asynchronously, unfortunately. I find it to be a key limitation for me. Ideally, I’d like to be able to run this script in a loop, so that I can create X number of virtual machines at the same time. However, it turns out that when you specify a Network or Affinity Group in the script (VMs must be on the same Network to be able to communicate with one another, so this is a requirement for me), there is an exclusive lock put on those resources, such that only one operation can occur at a time on any given Network or Affinity Group. If you try to loop this script too quickly, some of the iterations will complete while others will fail due to this locking problem. The best solution that I’ve come up with is to run this script about once every 90 seconds, which usually works without any contention issues. However, even when the scripts run smoothly on 90 second intervals, this doesn’t mean that Azure spins everything up problem-free. I don’t believe that this is due to any problems with the script so much as just the inconsistency/instability of the current Azure platform. Unfortunately, sometimes a virtual machine will simply fail to initialize properly. It will get created, but it will never start. When this happens there is nothing you can do other than destroy it and try again. When I spin up a large number of member VMs, sometimes almost all will be operational. But other times it’s closer to about 50% operational, with 50% stuck in the “stopped” state. I then have to destroy the “stuck” VMs and create new ones to take their place.

##Create a VM that is joined to the specified domain
 
Set-AzureSubscription "subscription_name_goes_here" -CurrentStorageAccount "storage_account_name_goes_here"
$vmName = 'vm_name_goes_here'
$location = 'East US'
$imageName = 'image_name_goes_here'
$adminUsername = 'admin_username_goes_here'
$adminPassword = 'admin_password_goes_here'
$instanceSize = 'ExtraSmall'
$cloudServiceName = '' #Turns out that this must be unique for every VM, and so we're not using this variable and instead below you'll notice we are instead using the $vmName.  It actually gets used as both the Cloud Service name AND the DNS Name of the VM, which is counter intuitive.
$VNetName = 'network_name_goes_here'
$affinityGroup = 'affinitygroup_name_goes_here'
 
#Domain join and DNS settings 
$domain = 'domain_name_goes_here' 
$domainjoin = 'fully_qualified_domain_name_goes_here' 
$domainUser = 'domain_username_goes_here' 
$domainPassword = 'domain_password_goes_here'
 
#we attempt to remove the service before creating the VM to avoid errors where the service already exists, which can happen if the script errors early on a previous run
remove-azureservice -servicename $vmName -Force
 
New-AzureVMConfig -Name $vmName -InstanceSize $InstanceSize -ImageName $imageName |
Add-AzureProvisioningConfig -Password $adminPassword -AdminUsername $adminUsername -WindowsDomain -JoinDomain $domainjoin -Domain $domain -DomainPassword $domainPassword -DomainUserName $domainUser|
New-AzureVM -ServiceName $vmName -Location $location -VNetName $VNetName -AffinityGroup $affinityGroup

Destroy a VM and Remove the Associated VHD on Disk (Script)

What about destroying member VMs? Here’s the script I use to do that. Note that this script seems to be the best way to completely destroy a VM and remove the associated VHD. However, it’s not the only way. Microsoft does provide a -DeleteVHD parameter for the Remove-AzureDisk cmdlet. However, I have found that this is not a safe option to use because it frequently leaves orphaned VHDs, which do not get deleted and cannot be deleted easily due to a lease on the blob never properly getting destroyed. When you have an orphaned VHD, you end up having to jump through a couple annoying hoops to break the lease on the blob in order to be able to delete the VHD properly. You can avoid this problem by NOT using the -DeleteVHD parameter. Instead, call Remove-AzureDisk without that parameter, and then call Remove-AzureStorageBlob afterward:

##Destroy VM and delete the VHD that's associated with it.
 
Set-AzureSubscription "subscription_name_goes_here" -CurrentStorageAccount "storage_account_name_goes_here"
 
$vmName = 'vm_name_goes_here'
 
remove-azurevm -Name $vmName -serviceName $vmName
 
Start-Sleep -s 10
 
remove-azureservice -servicename $vmName -Force
 
##we have to wait a little bit here for the vm to be removed before we will be allowed to remove the disk.  If we try to remove it took quickly, it will fail.
#sleep for 5 minutes before proceeding with disk deletion, to make sure there is enough time for the VM to be removed first
Start-Sleep -s 300
 
#Note that we use the wildcard * here when we're looking for disks to remove.  Be careful with what you pass to $vmName to make sure that you don't accidentally remove the wrong disk
#As long as you are using unique VM names, this shouldn't be a problem.  But if you have machines named VM1, VM2, VM3, and then you set $vmName to be "VM" all three of the disks will be removed.  So, make sure to pass just VM1 as $vmName if that's the only one you want to remove
get-azuredisk | where-object {$_.diskname -like "*" + $vmName + "*"} | remove-azuredisk
 
Start-Sleep -s 30
get-azurestorageblob -container vhds | where-object {$_.name -like "*" + $vmName + "*"} | remove-azurestorageblob

Export a VM Without Destroying the VHD, So That You Can Start It Again Later – Prevent accruing fees for a VM that you’re not using now but want to use again in the future (Script)

What if you don’t want to destroy a VM and you simply want to shut it down to use again later? Here’s how you remove/export a persistent VM without destroying it, so that you can then later re-add/import it. This lets you avoid being charged for the VM when you’re not using it. Note, if you shutdown a VM but do not export it, you will still be charged for it just as if it were running. You have to export the settings and then remove the VM but leave the VHD as-is to avoid getting charged for the VM. Once it has been exported and removed, you will only be charged for the blob storage. This is the script that I use for my persistent VMs, like my domain controllers. I first shut them down from inside the VM. Once they have been shutdown, I run this script:

##Export and remove VM but do NOT delete the VHD because we want to be able to use it later
 
Set-AzureSubscription "subscription_name_goes_here" -CurrentStorageAccount "storage_account_name_goes_here"
 
$vmName = 'vm_name_goes_here'
$path = "C:\users\myUsername\desktop\windowsazure\vm_settings_files\" + $vmName + "_Settings.xml"
 
# Exporting the settings to an XML file 
Export-AzureVM -ServiceName $vmName -Name $vmName -Path $path
 
#Remove the VM and service
remove-azurevm -Name $vmName -serviceName $vmName
 
Start-Sleep -s 10
 
remove-azureservice -servicename $vmName -Force

Import a VM That Had Been Previously Exported (Script)

Here’s how you import a VM that you previously exported:

##Import a VM that we previously exported
 
Set-AzureSubscription "subscription_name_goes_here" -CurrentStorageAccount "storage_account_name_goes_here"
 
$vmName = 'vm_name_goes_here'
$path = "C:\users\myUsername\desktop\windowsazure\vm_settings_files\" + $vmName + "_Settings.xml"
$VNetName = 'network_name_goes_here'
$affinityGroup = 'affinitygroup_name_goes_here'
$location = 'East US'
 
#we attempt to remove the service before creating the VM to avoid errors where the service already exists, which can happen if the script errors early on a previous run
remove-azureservice -servicename $vmName -Force
 
Import-AzureVM -Path $path | 
New-AzureVM -ServiceName $vmName -Location $location -VNetName $VNetName -AffinityGroup $affinityGroup

, ,

No Comments

HTML Application – SQL Differential Backup Size Prediction

You can find the full post here: http://dougzuck.com/hta

diffBackupPredictionHTA

, ,

No Comments

SQL – Differential Backup Size Prediction / Estimation

Darwin Hatheway wrote a really nice article explaining how the differential backup size can be estimated or predicted.  He gets all the credit for teaching me how to do this.  However, since he didn’t provide a complete solution, I had to do it myself. There are two implementations below.  One is a straight T-SQL script, and the other is a HTML Application (HTA) that utilizes the T-SQL script with a bit of vbscript.

T-SQL Version: SQL Differential Backup Size Prediction
HTML Application: http://dougzuck.com/hta

diffPredictionResults

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*SQL Differential Backup Size Prediction / Estimation - Doug Zuckerman, 2009 - http://dougzuck.com */
 
IF isNULL(object_id('tempdb.dbo.##showFileStats'), 1) <> 1
	DROP TABLE ##showFileStats
 
CREATE TABLE ##showFileStats (
	fileID INT,
	fileGroup INT,
	totalExtents BIGINT,
	usedExtents BIGINT,
	logicalFileName VARCHAR (500),	
	filePath VARCHAR (1000)
)
 
IF isNULL(object_id('tempdb.dbo.##DCM'), 1) <> 1
	DROP TABLE ##DCM
 
CREATE TABLE ##DCM (
	parentObject VARCHAR(5000),
	[object] VARCHAR(5000),
	FIELD VARCHAR (5000),
	VALUE VARCHAR (5000)
)
 
/*we need to get a list of all the files in the database.  each file needs to be looked at*/	
INSERT INTO ##showFileStats EXEC('DBCC SHOWFILESTATS with tableresults')
 
DECLARE @currentFileID INT,
	@totalExtentsOfFile BIGINT,
	@dbname VARCHAR(100),
	@SQL VARCHAR(200),
	@currentDCM BIGINT,
	@step INT
 
SET @dbname = db_name()
SET @step = 511232
 
DECLARE myCursor SCROLL CURSOR FOR
SELECT fileID, totalExtents 
FROM ##showFileStats
 
OPEN myCursor
FETCH NEXT FROM myCursor INTO @currentFileID, @totalExtentsOfFile
 
/*look at each differential change map page in each data file of the database and put the output into ##DCM*/
WHILE @@FETCH_STATUS = 0 
BEGIN
 
	SET @currentDCM = 6
	WHILE @currentDCM <= @totalExtentsOfFile*8
	BEGIN	
		SET @SQL = 'dbcc page('+ @dbname + ', ' + CAST(@currentFileID AS VARCHAR) + ', ' + CAST(@currentDCM AS VARCHAR) + ', 3) WITH TABLERESULTS'
		INSERT INTO ##DCM EXEC (@SQL)
		SET @currentDCM = @currentDCM + @step
	END
 
	FETCH NEXT FROM myCursor INTO @currentFileID, @totalExtentsOfFile
END
CLOSE myCursor
DEALLOCATE myCursor
 
/*remove all unneeded rows from our results table*/
DELETE FROM ##DCM WHERE VALUE = 'NOT CHANGED' OR parentObject NOT LIKE 'DIFF_MAP%'
--SELECT * FROM ##DCM
 
/*sum the extentTally column*/
SELECT SUM (extentTally) AS totalChangedExtents, SUM(extentTally)/16 AS 'diffPrediction(MB)', SUM(extentTally)/16/1024 AS 'diffPrediction(GB)' 
FROM
	/*create extentTally column*/
	(SELECT extentTally =
	CASE
		WHEN secondChangedExtent > 0 THEN CAST(secondChangedExtent AS BIGINT) - CAST(firstChangedExtent AS BIGINT) + 1
		ELSE 1
	END
	FROM
		/*parse the 'field' column to give us the first and last extents of the range*/
		(SELECT (SUBSTRING(FIELD,(SELECT CHARINDEX(':', FIELD, 0))+1,(CHARINDEX(')', FIELD, 0))-(CHARINDEX(':', FIELD, 0))-1))/8 AS firstChangedExtent,
		secondChangedExtent = 
		CASE	
			WHEN CHARINDEX(':', FIELD, CHARINDEX(':', FIELD, 0)+1) > 0 THEN (SUBSTRING(FIELD,(CHARINDEX(':', FIELD, CHARINDEX(':', FIELD, 0)+1)+1),(CHARINDEX(')', FIELD,CHARINDEX(')', FIELD, 0)+1))-(CHARINDEX(':', FIELD, CHARINDEX(':', FIELD, 0)+1))-1))/8
			ELSE ''
		END 
		FROM ##DCM)parsedFieldColumn)extentTallyColumn

9 Comments

HTML Application – Retrieve SP_WHO2 and the Input Buffer from a SQL Server

You can find the full post here: http://www.dougzuck.com/hta

get_sql_spwho2_and_inputbuffer

, , ,

No Comments

Decrease Malware Infections Using Software Restriction Policies (SRP) to Strip Administrative Privileges from Internet-Facing Applications

This information applies to Windows XP only.  It is NOT valid for Windows Vista or Windows 7

 

I know that many organizations do not put restrictions on their users’ computers.  The users are often given full administrative privileges on their workstations, which means that they can not only mess around with all the settings on their computers, but they can also install and uninstall applications.  This includes accidental virus and malware installations.  While many Systems and Network Admins consider this unacceptable, it’s still a reality in many working environments.  So rather than complain about how it’s not the ideal way to run a windows network, let’s focus on cool ways to mitigate the risks of this approach.  This is one VERY simple but effective method to limit malware infections on your network computers while still allowing users to be local administrators.  This approach can be used across an entire network with a group policy object, or it can simply be applied to a single computer by modifying the computer’s local security policy.

Overview…

The idea here is that you apply a group policy object to the users or computers in your organization.   It prevents whatever applications you choose from launching with full admin privileges on the users’ computers.  The users are still local administrators, but the particular applications that you pre-select get launched without administrative permissions.  If you apply this restriction to all of the applications that deal with typically untrusted, unsafe, or unknown content, then you dramatically decrease the likelihood that a virus or other malware can be installed because non-admin users are not able to install software on the computer.  Windows doesn’t let them.  I recommend applying it to all web browsers, all email clients, and all media players because these are the primary apps that deal with internet content.  You could alternatively apply it to the entire C:\Program Files folder, but if you do so you should be mindful of the fact that some apps might break as a result.

You can produce functionality that is similar to DropMyRights but without the annoyances that come along with it.  In my opinion this method is by far the easiest to deploy to a lot of users or computers, something which DropMyRights isn’t suited for (since any time an application is updated or a new one installed, the DropMyRights configuration has to be re-applied).  Using Software Restriction Policies, you can apply this functionality in a way that is virtually transparent to users.  However, Microsoft doesn’t publicize this particular usage of SRP for whatever reason (the functionality is actually hidden in XP by default – you need to add a registry DWORD to make it available), which is why I’m taking the time to mention it here.  When I embarked on setting this up today at my job I spent hours researching something that took only minutes to implement.  Hopefully I’m now saving you the hours of research.

Caveat…

The only real caveat is that when an application is launched without admin privileges, if that application then launches another process or program, the program that it launches will also not have admin privileges.  This means that if a user wants to install software that he/she downloads from the web, he/she needs to be aware that launching the setup.exe file directly from browser’s ‘Downloads’ window will generate an error and abort the installation.  In some cases it might not throw an error and instead will appear to install successfully, but when the app is launched it isn’t able to run because the installation was actually not successful.  Users have to be trained to save the application setup files to their desktops (or wherever) and then launch them from the desktops or through Windows Explorer.

Additionally, it is up to you test out any applications that you restrict.  While most of the time this is a transparent deployment, there is always the possibility that this restriction could hinder a custom application from working in the way that it was designed.  However, for most applications in most situations it works great and causes no issues.

Here’s the step by step:

1. Expose the hidden ‘Basic User’ option by opening the registry editor and adding a DWORD called “Levels” with a value of 20000 (hexadecimal) to

HKLM\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers

SRP4

2. Open the domain Group Policy editor (or to apply to a single computer, open the local policy editor by going to Start > Run > gpedit.msc) and go to

Computer Configuration\Windows Settings\Security Settings\Software Restriction Policies

If this is the first time you’ve looked at the Software Restriction Policies, the right hand pane will be empty.  To rectify this, click on Action > Create New Policies.  Once you’ve done this you should see ‘Security Levels’ and ‘Additional Rules.’  Now click on ‘Security Levels’ and verify that you see 3 options (Disallowed, Basic User, and Unrestricted).

If you do not see the ‘Basic User’ option on the right-hand pane, close the GPO editor, go back to step 1 and make sure you’ve properly created the registry DWORD, then re-open the GPO editor (Note that the Basic User option is hidden in Windows XP until you add the Levels DWORD value).

SRP1

3.  At this point you’re going to highlight ‘Additional Rules’ and right click to create a new path rule. Specify the path to the application that you want to limit.  In the path entry you can use the asterisk (*) as a wild card for multiple letters/words.  The question mark symbol can be used as a wild card for a single letter.

Set the security level drop-down menu to Basic User.  This is the key ingredient that makes the magic happen.  Now any executable file in the specified path or its subfolders will launch with limited user privileges on a computer that receives this GPO.

SRP2

SRP3

4.  When you’re done creating the policy, link it to your workstations OU and test it!  That’s it.  Pretty simple, eh?

In this example, I’ve limited Firefox so that it can only be launched as a Basic User with no admin rights. I’m able to verify this is the case by first closing all instances of Firefox, then launching it once again after the path rule has been created.  I browse to a website and download a software installation package. I then try to launch the software package from within Firefox’s ‘Downloads’ window, but I can’t perform the installation, so I know it works!

srp5srp6

, ,

6 Comments

HTML Application – Retrieve the model, serial number, RAM amount, OS version, last boot time and more using WMI

You can find the full post here: http://www.dougzuck.com/hta

Get_General_Info

, ,

1 Comment

HTML Application – Retrieve SQL Backup History

You can find the full post here: http://www.dougzuck.com/hta

get_sql_backup_history

, ,

No Comments

C# .NET – UI Threading Example

This is the simplest way to execute work in a separate thread and have it report back to the UI thread

When you’re creating a Windows Form Application using Visual C# 2008, there are some tricky threading issues that need to be dealt with in order to keep your application running smoothly.  Specifically, you have to be very aware of how your application does work.  If you simply create a worker function and then call it, it’s going to execute in the current thread, which is also the thread that’s handling the user interface.  The result really stinks because your UI will completely lock up until the worker thread is finished executing.  This is unacceptable in nearly all situations.

I want to show you what I believe is the absolute simplest way for executing a worker function in a separate thread while still allowing that separate thread to send information back to the main UI thread, whether it be to update a text box, a label, or a field in a datagridview.

In this example we’re going to click a button to launch our worker function, which will count from 1 to 15.  Our user interface will display each number as it counts. 

Also, please pardon the fact that I use ‘function’ and ‘method’ interchangeably in this post.  Technically, I should just be saying ‘method,’ but ‘function’ always slips out.

Download the entire Visual Studio project here: UI_Threading_Example_CSharp.zip

UI_Threading_ExampleUI_Threading_Example(2)

Let’s take a look at the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System;
using System.Windows.Forms;
using System.Threading;
 
namespace UI_Threading_Example
{
    //declare the delegate that we'll use to launch our worker function in a separate thread
    public delegate void workerFunctionDelegate(int totalSeconds);
 
    //declare the delegate that we'll use to call the function that displays text in our text box
    public delegate void poplateTextBoxDelegate(string text);
 
    public partial class Form1 : Form
    {
       public Form1()
       {
          InitializeComponent();
       }
 
       //this function will simply write text to our text box
       //this function will later be called from a worker thread through the use of a delegate using the Invoke method on the form
       void populateTextBox(string text)
       {
          textBox1.Text = textBox1.Text + " " + text;
       }
 
       //this function simulates "work" by simply counting from 1 to totalSeconds
       void workerFunction(int totalSeconds)
       {
          for (int count = 1; count <= totalSeconds ; count++)
          {
             //we use this.Invoke to send information back to our UI thread with a delegate
             //if we were to try to access the text box on the UI thread directly from a different thread, there would be problems
             this.Invoke(new poplateTextBoxDelegate(populateTextBox), new object[] { count.ToString() });
             Thread.Sleep(1000);
          }
       }   
 
       //this function is executed when we click the first button in the windows form
       //this is the PROPER WAY to do work in a UI situation
       //the worker function is launched in a separate thread so that our UI will remain responsive while it does work
       private void buttonNewThread_Click(object sender, EventArgs e)
       {
          workerFunctionDelegate w = workerFunction;
          w.BeginInvoke(15, null, null);
       }
 
       //this function is executed when we click the second button in the windows form
       //it's an example of WHAT NOT TO DO because if we click this button
       //the UI will become completely unresponsive for 15 seconds while the worker fucntion is executed
       private void buttonCurrentThread_Click(object sender, EventArgs e)
       {
          workerFunction(15);
       }
 }

OK, so here’s what we’ve got.  In Visual Studio (I’m using Visual C# 2008) I’ve created a new Windows Form Application and then in the designer window I added a text box (called textBox1) plus two buttons (called buttonNewThread and buttonCurrentThread).

In the code window I created two functions:

1
2
void populateTextBox(string text)
void workerFunction(int totalSeconds)

plus a delegate for each function:

1
2
public delegate void poplateTextBoxDelegate(string text);
public delegate void workerFunctionDelegate(int totalSeconds);

The first function is responsible for writing text to our text box.  The second function is responsible for doing “work,” which in this case is just counting from 1 to 15 and calling the function (using a delegate) to display the count in our text box.  Additionally there are two other functions (buttonCurrentThread_Click and buttonNewThread_Click) which handle the button clicks from our UI:

What’s the deal with delegates in this example?

In order to launch a worker function in a separate thread we have to declare a delegate for that function.  The delegate declaration must be outside the class declaration, and the delegate for each function must have the same format as the function that it’s going to be used to call.  So for example, if your worker function has 3 parameters, your delegate must also have 3 parameters.  And in this example, we not only need a delegate to handle launching our worker function in a new thread, but we also need to declare a delegate that we’ll use from within the worker thread to call a function to update the text box on the UI thread.  It is NOT ok to try to update a text box in the UI from a different thread without the use of a delegate.  If you don’t believe me then give it a shot and you’ll see for yourself that the results are funky.

The WRONG way to execute the worker function (using the UI thread)

When you click the button to execute the worker function in the current thread, which is the same thread that handles the UI, then you’ll see that for 15 seconds you can’t do anything with the UI or even move the form window to a new location.  At the end of 15 seconds the UI becomes responsive again and you see a “15” in the text box.

1
2
3
4
5
//This is the WRONG way to do it
private void buttonCurrentThread_Click(object sender, EventArgs e)
{
   workerFunction(15);
}

The RIGHT way to execute the worker function (using a separate thread)

When you click the button to execute the worker function in a new thread, you watch the count from 1 to 15 displayed in the text box and the UI is not frozen.

Note that the null values are required by the BeginInvoke method.  If you have a function that requires more than one parameter, you would still pass all your parameters in first, followed by the two nulls.  In this example we’re effectively passing the value 15 to the worker function which will then execute for 15 seconds.

1
2
3
4
5
6
//This is the RIGHT way to do it
private void buttonNewThread_Click(object sender, EventArgs e)
{
   workerFunctionDelegate w = workerFunction;
   w.BeginInvoke(15, null, null);
}

The proper way to update the text box in the UI from the worker thread using the Invoke method of the main form

The format can definitely get a little confusing, but hopefully you can follow along and mimic it for your application.  Instead of accessing the text box directly from the worker thread, which will cause problems, we instead invoke a method (using a delegate) that accesses the text box. Note that our populateTextBox function has one parameter, which is a string.  We want to write the current count to the text box using that function, so we have to convert it to a string.  If we were passing multiple variables to the populateTextBox function, the format would still look the same, and we’d simply separate the variables inside the curly braces by a comma.

1
2
3
4
5
6
7
8
9
//update the text box by using a delegate to call a function on the UI thread that will do the update
void workerFunction(int totalSeconds)
{
   for (int count = 1; count <= totalSeconds ; count++)
      {
         this.Invoke(new poplateTextBoxDelegate(populateTextBox), new object[] { count.ToString() });
         Thread.Sleep(1000);
      }
}

, , ,

20 Comments

HTML Application – Retrieving a graphical representation of all SQL databases and the breakdown of space used by each

You can find the full post here: http://www.dougzuck.com/hta

get_sql_db_sizes

, ,

No Comments

HTML Application – Retrieving available disk space on a remote computer

You wholesale jerseys can wholesale nba jerseys find cheap nba jerseys the a full wholesale mlb jerseys post Hacked here: cheap nba jerseys http://www.dougzuck.com/hta

getdiskspace

, ,

No Comments