星期二, 十月 30, 2007

今年144万应届毕业生未如期就业



 
 

Sent to you by Hudong via Google Reader:

 
 

via 新闻中心-国内焦点新闻 by WWW.SINA.COM.CN on 10/30/07

  晨报讯 (记者 罗德宏) 记者昨天从教育部获悉,2008年全国高校毕业生将超过550万人,比今年增加50多万。   据介绍,今年全国高校毕业生是495万人,比2006年增加了82万人,毕业生总量和增量都是最多的一年。到今年9月1日,全国普通高等学校的毕业生实现就业的人数是351万人....

 
 

Things you can do from here:

 
 

星期一, 十月 29, 2007

Lenovo Thinkpad add on for IE7 slowing things down



 
 

Hudong 通过 Google 阅读器发送给您的内容:

 
 

于 07-10-29 通过 MSDN Blogs 作者:pooyad

I just got a new Lenovo Thinkpad T61p at work and am getting it set up.  Overall, it's very nice, I like the Thinkpad design, the pointing stick, and am getting used to the widescreen. 

I noticed that IE7 was unusally slow, especially when opening a new tab.  I always set the home page to blank to make sure everything opens fast; but for some reason everytime I would open a new tab it would take between 3-10 seconds before it would actually allow me to use the new tab.  I'd never seen this before with IE7 on my old laptop or home laptop.  Checked new tabs in my firefox installation and it was fine.

After a little searching I found the answer, though in a different language :)  It turns out it's an add-on from Lenovo (CPwmIEBrowserHelper Object) that's causing the problem.

It turns out many others have seen the same issue.  Now things are back to normal, happily browsing ... now to get my old passwords into this browser.


 
 

可从此处完成的操作:

 
 

Are you making these 3 common ASP.NET AJAX mistakes?



 
 

Hudong 通过 Google 阅读器发送给您的内容:

 
 

于 07-10-29 通过 MSDN Blogs 作者:JoeStagner

Check out Dave Wards advice on Update Panels and Postabacks at Encosia HERE.

http://encosia.com/2007/10/24/are-you-making-these-3-common-aspnet-ajax-mistakes/


 
 

可从此处完成的操作:

 
 

星期四, 十月 25, 2007

星期三, 十月 24, 2007

星期一, 十月 22, 2007

WSS 3.0中文應用範本免費下載中!



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by jchiou on 10/21/07

此外, WSS 提供了20種簡單的中文應用範本,包含人力資源, 專案管理, 銷售管理等常見應用, 免費供客戶使用. 可下載 "快速安裝套件"

下載位置:

http://download.microsoft.com/download/a/3/8/a3873330-221c-4d26-b563-25253deb68ff/WSSInstallationTlp.zip


 
 

Things you can do from here:

 
 

星期五, 十月 19, 2007

P3.NETPractical Programming Pearls For .NET Developers
osu!应援团 PC版

星期三, 十月 17, 2007

杨澜的杂志下载

名称:《StarMook》第十五期

发布时间:2007.10.17

当期艺人:张靓颖
C# Winforms and the Hidden Association Tag at OmegaMan’s Musings
9031: Free Downloads screen saver, icons etc

星期一, 十月 15, 2007

HOWTO: EWS: Use GetAttachment to download attachments off Mail/Appointment



 
 

Hudong 通过 Google 阅读器发送给您的内容:

 
 

于 07-10-15 通过 MSDN Blogs 作者:Vikas Verma

I have fallen for exchange web services. There are endless possibilities with exchange web services, and product group is still working to make it even better.

Today I have created a neat sample to download attachments off Exchange Server

Sample: DownloadAttachments

Input Params: itemID , folder

 

public void DownloadAttachments(string itemID,string folder)

{

 

ExchangeServiceBinding esb = new ExchangeServiceBinding();

esb.AllowAutoRedirect = true;

 

esb.Credentials = new System.Net.NetworkCredential("USERNAME", "PASSWORD", "DOMAIN");

esb.Url = "http://your-cas-server/ews/exchange.asmx";

 

//first we need to get the attachment IDs for the item so we will need to make a GetItem call first

//specify the conetent that we want to retrieve

 

PathToUnindexedFieldType[] ptufta = new PathToUnindexedFieldType[2];

ptufta[0] = new PathToUnindexedFieldType();

ptufta[0].FieldURI = UnindexedFieldURIType.itemAttachments;

ptufta[1] = new PathToUnindexedFieldType();

ptufta[1].FieldURI = UnindexedFieldURIType.itemHasAttachments;

ItemResponseShapeType irst = new ItemResponseShapeType();

irst.BaseShape = DefaultShapeNamesType.IdOnly;

irst.AdditionalProperties = ptufta;

ItemIdType[] biita = new ItemIdType[1];

biita[0] = new ItemIdType();

biita[0].Id = itemID;

//get the items

GetItemType git = new GetItemType();

git.ItemShape = irst;

git.ItemIds = biita;

 

GetItemResponseType girt = esb.GetItem(git);

 

if (girt.ResponseMessages.Items[0].ResponseClass != ResponseClassType.Success)

return;

 

//now that we have the attachment IDs let's request the atthacments and save them to disk

ItemType MsgItem = ((ItemInfoResponseMessageType)girt.ResponseMessages.Items[0]).Items.Items[0];

 

AttachmentResponseShapeType arst = null;

AttachmentIdType[] aita = null;

if (true == MsgItem.HasAttachments)

{

//create the attachment shape; we want the mime contnet just in case this is an message item so that we can save to disk

arst = new AttachmentResponseShapeType();

arst.IncludeMimeContent = true;

arst.IncludeMimeContentSpecified = true;

//create an array of attachment ids that we want to request

aita = new AttachmentIdType[MsgItem.Attachments.Length];

 

for (int i = 0; i < MsgItem.Attachments.Length; i++)

{

aita[i] = new AttachmentIdType();

aita[i].Id = MsgItem.Attachments[i].AttachmentId.Id;

}

}

 

 

//create a GetAttachment object for the GetAttachment operation

GetAttachmentType gat = new GetAttachmentType();

gat.AttachmentIds = aita;

gat.AttachmentShape = arst;

GetAttachmentResponseType gart = esb.GetAttachment(gat);

 

//save each attachment to disk

foreach (AttachmentInfoResponseMessageType Attachment in gart.ResponseMessages.Items)

{

switch (Attachment.Attachments[0].GetType().Name)

{

//attachments can be of type FileAttachmentType or ItemAttachmentType

//so we need to figure out which type we have before we manipulate it

case "FileAttachmentType":

//save to disk

FileAttachmentType TheFileAttachment = (FileAttachmentType)Attachment.Attachments[0];

 

using (Stream FileToDisk = new FileStream(folder + @"\" + Attachment.Attachments[0].Name, FileMode.Create))

{

FileToDisk.Write(TheFileAttachment.Content, 0,

TheFileAttachment.Content.Length);

FileToDisk.Flush();

FileToDisk.Close();

}

break;

 

case "ItemAttachmentType":

//save to disk

ItemType TheItemAttachment = ((ItemAttachmentType)Attachment.Attachments[0]).Item;

 

 

using (Stream FileToDisk = new FileStream(folder + @".\" + Attachment.Attachments[0].Name + ".eml", FileMode.Create))

{

byte[] ContentBytes = System.Convert.FromBase64String(TheItemAttachment.MimeContent.Value);

FileToDisk.Write(ContentBytes, 0,

ContentBytes.Length);

FileToDisk.Flush();

FileToDisk.Close();

}

break;

 

default:

break;

}

}

 

}

 

 

 

 


 
 

可从此处完成的操作:

 
 

Why aren't shortcuts as easy as unix links?



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by oldnewthing on 10/15/07

Commenter dingo asks, "Why are shortcuts so hard to manipulate? Why can't they be as easy as unix links?"

Well, if you want something like unix links, then you can just create a hard link. Creating them is about the same difficulty (CreateHardLink vs link) and manipulating them is the same since you don't actually manipulate a hard link. You just use it like a regular file (since a regular file is a hard link).

If you want something like unix symbolic links, then you can create an NTFS junction, such as this one that mounts a drive into a directory. (I'm told that Windows Vista expands the repertoire of symbolic links as well.)

But neither of these features is available on FAT (or CD-ROMs or Novell Netware or email), which meant that Windows 95 couldn't use them. Last year I discussed in some detail why shortcuts are files. Maybe that's what your question is really about.


 
 

Things you can do from here:

 
 

星期日, 十月 14, 2007

Gmaps Pedometer 5.5mile route

星期五, 十月 12, 2007

Peldet Official Site PSP telnet

星期四, 十月 11, 2007

星期二, 十月 09, 2007

Real Estate Disposition Corporation - Minnesota
About ADC - Company Overview
Gmaps Pedometer


Anderson lake route



Staring lake route



Home 1 circle at lake


home to lake 0.3587 mile

1.1535 total, lake circle ~0.7948
2 circle = 1.9483
3 circle = 2.7431
4 circle = 3.5379

星期五, 十月 05, 2007

星期四, 十月 04, 2007

[Recommended Books] Awesome Debugging Book



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by Roberto Farah on 10/4/07

I've been exchanging e-mails with Mario Hewardt and Daniel Pravat, authors of the upcoming debugging book "Advanced Windows Debugging".

 

I must tell you: this post is to share my excitement with this book!

 

The book covers everything you can imagine regarding native debugging!

Take a look at the table of contents:

 

Part I – Introduction

 

1. Introduction to the Tools
      Introduces the tools used throughout the book including basic usage
      scenarios and download locations. The following tools are covered:

 

    < Diagnosis>Debugging Tools for Windows
    UMDH

    Microsoft Application Verifier

    Global Flags

    Process Explorer

    Windows DDK

    Ethereal

    Debug Diagnostics

 

2. Introduction to the Debuggers

Introduces the fundamentals of the Windows Debuggers including coverage of the different types of debuggers (user mode vs. kernel mode), debugger setup, symbols, crash dumps and much more. The second part of the chapter details the basic and most commonly used debugger commands.

 

                                                                                                                                                                

3. Debugger Uncovered

Chapter 3 continues the examination of the debuggers in greater detail. Coverage includes topics such as the inner workings of a debugger (miniature debugger implementation) and a detailed description of the exceptions dispatching mechanism.

 

4. Managing Symbols and Sources

Without proper symbols debugging is a very hard and sometimes impossible proposition. This chapter covers the important topic of symbol and source management. Topics such as creating private and public symbol packages, setting up a symbol and source server are covered in detail.

 

 

Part II – Applied Debugging

 

5. Memory Corruptions Part I: Stacks

Corrupting the stack is a common programming mistake that can be extremely difficult to debug. Without the proper knowledge of how Windows manages thread stacks as well as the tools available to make debugging stack problems easier developers can spend countless hours navigating a stack corruption. The chapter begins with an overview of Windows stack management followed by a set of scenarios that illustrate common stack problems and how to debug them.

 

 

6. Memory Corruptions Part II: Heaps

This chapter continues the study of memory corruptions and focuses on heap related corruptions. The chapter begins with a detailed discussion of the Windows Heap Manager and covers topics such as the front end allocator, back end allocator, look aside lists and free lists. Several scenarios are also included and serve to illustrate some of the most common forms of heap corruptions. A detailed root cause analysis using the debuggers and tools is also included for each scenario.

 

7. Security

Every so often a developer is faced with an ACCESS DENIED error code without any clues as to why the error was returned. The task of navigating complex security and access denied problems is a covered in this chapter. The primary focus of this chapter is on the Windows native security model and covers the access control constructs available as well as how the debuggers can be used to maximize efficiency when analyzing security problems.

 

8. Interprocess Communication

Processes typically don't live in isolation rather rely on auxiliary processes to complete processing. This chapter details the intricacies of debugging across processes using RPC. Topics covered include identity tracking (who is calling what), RPC debugger commands and extensions as well as an explanation of a powerful freeware tool called Ethereal that can be used to track interprocess calls that cross machine boundaries.

 

9. Resource Leaks

Resource leaks are common programming mistakes that can wreak havoc in your application. This chapter studies the most common form of resource leaks (such as memory and handle leaks) and shows how the debuggers and associated tools can be utilized when tracking down resource leaks.

 

10. Synchronization

A multithreaded environment allows for a great amount of flexibility and efficiency. With this flexibility comes a lot of complexity in the form of thread management. Threads must be made to properly work in tandem without interfering with each other. This chapter introduces the synchronization primitives available in Windows and discusses how the debuggers and tools can be used to debug common thread synchronization problems. Scenarios such as deadlocks, lock contention (lock convoys), indirect deadlocks (such as loader lock) and management of critical sections are discussed in detail.

 

 

Part III – Advanced Topics

 

11. Writing Custom Debugger Extensions

Even though the debuggers include a ton of powerful commands out of the box it is sometimes necessary to extend the core functionality. This can be achieved by writing your own debugger extensions. This provides an in depth discussion of how to develop a custom debugger extension. It will guide you through the overall extensibility mechanism and includes a step by step guide on how to implement a sample debugger extension.

 

12. 64bit Debugging

With the advent of 64bit architectures, the need to understand how these architectures work and how they can be debugged is critical. This chapter outlines the fundamentals of 64bit debugging. The chapter looks at each topic discussed in the book and details the various differences between 32 and 64 bit debugging. The primary architecture covered is the x64 architecture.

 

13. Postmortem Debugging

Quite often, it's not feasible to expect to have full access to a failing machine so that a problem can be debugged. Bugs that surface on production machines on customer sites are rarely available for debugging. This chapter outlines the mechanisms available for debugging a problem without access to the physical machine. Topics discussed include the basics of crash dumps, generating crash dumps, analyzing crash dumps, signing up for Windows Error Reporting and using Corporate Error Reporting to maximize postmortem debugging efficiency.

 

14. Power Tools

This chapter discusses two extremely important tools that can automate much of the debugging process. The DebugDiag is a powerful tool that can help analyze resource leaks, crashes and hangs. It is scripting enabled and allows for a powerful extension model. The second tool discussed is the analyze debugger command which performs a preliminary analysis of the process in question and can automate a lot of the investigative work typically done on a failing process.

 

15. Windows Vista Fundamentals

Windows Vista introduces a slew of new features and many of those new features come in the form of kernel enhancements. This chapter gives an overview of the most important changes such as the low fragmentation heap, new interprocess communication, new security model and much more.

 

 

If it wasn't enough to help you understand my excitement, then read this sample chapter that explains how to debug a heap memory corruption.

 

By the way, if you wish to apply for our Support Escalation Engineer or Escalation Engineer positions, read this book because it will help you during the interviews. J

 

I've already ordered mine and I'm looking forward to having it in my hands!

It's going to be released on October 26, 2007.

 

In this article I comment on other debugging books.

 

See you on next article.


 
 

Things you can do from here:

 
 
Changing PSP face plate PSP - PlayStation Portable - Reviews, Games, Forum

星期三, 十月 03, 2007

.NET Framework Source Code to Ship with Orcas



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by ccalvert on 10/3/07

In what is easily the single best piece of news I have heard since coming to Microsoft, Scott Guthrie has announced today that we will be releasing the source code to the .NET framework when we ship Visual Studio 2008 later this year.

An option will be made available to allow you to step directly into the source from your own code, so you can see exactly what is happening in the .NET framework. This is exactly what developers need, and it will greatly improve the experience of developing on the .NET Framework .

Scott says that the source code and comments to the following will be released:

  • BCL libraries (System, System.IO, System.Collections, System.Configuration, System.Threading, System.NET, System.Security, System.Runtime, System.Text, etc)
  • Windows Forms
  • ASP.NET
  • ADO.NET
  • XML (System.Xml)
  • WPF

The code will be made available on the web, and there will be an option to download it to your machine. Other libraries to be added later include:

  • LINQ
  • WCF
  • Workflow

There is very little I can add to this announcement except to say that it makes me extremely happy. I feel proud to work for a company that offers not only the best tools available, but also open access to the source so that the whole community can fully understand these great frameworks and better debug the tricky parts of their applications.

Here is a link to Scott's post:

http://weblogs.asp.net/scottgu/archive/2007/10/03/releasing-the-source-code-for-the-net-framework-libraries.aspx

 

kick it on DotNetKicks.com

 
 

Things you can do from here:

 
 
PSP blog enable location free

星期二, 十月 02, 2007

MultiWall - Wallpaper Tool for Multiple Monitors



 
 

Sent to you by Hudong via Google Reader:

 
 

via MSDN Blogs by Coding4Fun on 10/2/07

Multiple monitors Are you more productive with more monitors?  Would you be even more productive with better wallpaper management?  I thought so!  In this article, see how to create a utility to manage your Windows desktop wallpaper with separate images per screen.  It's addicting!
Arian Kulp

Difficulty: Intermediate
Time Required: 1-3 hours
Cost: None
Hardware: At least one monitor (more is better!)
Download:

Introduction

Everyone enjoys customizing his or her computer to the extent possible, and this almost always includes the desktop wallpaper.  Windows XP manages wallpaper resizing pretty transparently, increasing or reducing the image display size, or tiling it as specified.  Unfortunately, this isn't quite as smooth if you have more than one monitor.  After some tinkering around with display settings and different bitmaps, I threw together a utility to resize images easily so you can customize what shows on each display.

Before reading further, you may want to download the source code in either Visual Basic or C# from the links above the "Introduction" heading.  The code is identical in either language, so choose the one with which you're more comfortable.  If you haven't yet, download the appropriate version of Visual Studio 2005 Express Edition and let's begin!  You might also want to read through earlier articles that I've written about desktop wallpaper (here and here).

Understanding Wallpaper

Most people are familiar with setting wallpaper through the system's Display Properties control panel applet.  You browse to an image, select how the image should be sized to fit the screen, and it does its magic.  If you have two monitors though, you'll have noticed that it just duplicates the image on each monitor when in Stretch or Center mode, and repeats it unattractively across monitor boundaries in Tile mode.  This isn't generally the intent when selecting an image!

image

Image 1: Windows XP Display Properties - Desktop tab

I just want to be able to select a different image for each screen.  In figuring that out though, I learned that the Tile mode can be pretty useful if you happen to have an image of the right size.  For example, if you have two displays that are each 1024x768, you can select an image that's 2048x768, select Tile mode, and it will spill across the screens perfectly.  This doesn't do much good if your screens are aligned in a non-standard way, at different resolutions, or the image isn't sized right.

With this new understanding in mind, I realized that I could write a utility, that, when given multiple images, could read the display settings and generate a single bitmap with all images sized and positioned properly.  Tiling this image results in the perfect fit on each monitor.

Sizing and Placing

Reading your display configuration is as easy as accessing the System.Windows.Forms.Screens collection.  Each element is of type Screen and exposes its identifier, resolution, and coordinates.  Identifier and resolution should be self-explanatory, but the coordinates may take a little explaining.  By default, Windows will place two monitors side-by-side, such that display 1 is to the far-left, and subsequent monitors line up to the right.

The problem comes with unequal resolutions, or monitors of physically different sizes (as mine are).  To account for these differences, you can just drag the monitor previews around in the Display Properties dialog until things line up properly.  The exact placement is then reflected by the X,Y coordinates for a display.  Note that there can be no gap in-between displays, though you could get a pretty crazy configuration by placing two displays almost diagonal to each other.  Of course, given your physical monitor layout, this might make sense.

image

Image 2: Windows XP Display Properties - Settings tab

The first thing the application does is figure out the overall display bounds, taking into account all monitors.  In the simple case of two 1024x768 monitors, lined up perfectly side-by-side, the overall bounds would be 2048x768.  If you offset them similar to shown above, maybe the bounds become 2048x800.  The key is to create a single rectangle that contains all monitors, and to keep track of the visible regions in that rectangle.

To accomplish this, the AddBounds shared/static method (in the BoundsUtilities class) takes an overall Rectangle and a Rectangle to add.  The returned Rectangle represents the best fit overall bounds.

Visual Basic

Public Shared Function AddBounds(ByVal sourceBounds As Rectangle, ByVal newBounds As Rectangle) As Rectangle     If newBounds.Right > sourceBounds.Right Then         sourceBounds.Width += (newBounds.Right - sourceBounds.Width)     End If      If newBounds.Bottom > sourceBounds.Bottom Then         sourceBounds.Height += (newBounds.Bottom - sourceBounds.Height)     End If      If newBounds.Left < sourceBounds.Left Then         sourceBounds.X = newBounds.X     End If      If newBounds.Top < sourceBounds.Top Then         sourceBounds.Y = newBounds.Y     End If      Return sourceBounds End Function

Visual C#

public static Rectangle AddBounds(Rectangle sourceBounds, Rectangle newBounds) {     if (newBounds.Right > sourceBounds.Right)         sourceBounds.Width += (newBounds.Right - sourceBounds.Width);      if (newBounds.Bottom > sourceBounds.Bottom)         sourceBounds.Height += (newBounds.Bottom - sourceBounds.Height);      if (newBounds.Left < sourceBounds.Left)     {         sourceBounds.X = newBounds.X;     }      if (newBounds.Top < sourceBounds.Top)     {         sourceBounds.Y = newBounds.Y;     }      return sourceBounds; }

In the MainForm class, when the application starts up or the display properties change, the UpdateMonitorBounds method cycles through the screens calling AddBounds.  It also accounts for the fact that the primary display always establishes 0,0, and screens above or to the left of it will have negative coordinates.  This method also creates Bitmap instances for both the preview and the actual desktop display.

Visual Basic

Private Sub UpdateMonitorBounds()     screens = Screen.AllScreens      overallBounds = New Rectangle()     refPoint = New Point()      For Each scr As Screen In screens         overallBounds = BoundsUtilities.AddBounds(overallBounds, scr.Bounds)     Next      ' Screens to the left or above the primary screen cause 0,0 to be other     ' than the top/left corner of the Bitmap     If overallBounds.X < 0 Then         refPoint.X = Math.Abs(overallBounds.X)     End If     If overallBounds.Y < 0 Then         refPoint.Y = Math.Abs(overallBounds.Y)     End If      ' Cancels out the negative values from offset screens     Dim correctedBounds As Rectangle = ZeroRectangle(overallBounds, refPoint)      previewBitmap = New Bitmap(CInt(correctedBounds.Width / 4), CInt(correctedBounds.Height / 4))     desktopBitmap = New Bitmap(correctedBounds.Width, correctedBounds.Height) End Sub

Visual C#

private void UpdateMonitorBounds() {     screens = Screen.AllScreens;      overallBounds = new Rectangle();     refPoint = new Point();      foreach (Screen scr in screens)     {         overallBounds = BoundsUtilities.AddBounds(overallBounds, scr.Bounds);     }      // Screens to the left or above the primary screen cause 0,0 to be other     // than the top/left corner of the Bitmap     if (overallBounds.X < 0) refPoint.X = Math.Abs(overallBounds.X);     if (overallBounds.Y < 0) refPoint.Y = Math.Abs(overallBounds.Y);      // Cancels out the negative values from offset screens     Rectangle correctedBounds = ZeroRectangle(overallBounds, refPoint);      previewBitmap = new Bitmap(correctedBounds.Width / 4, correctedBounds.Height / 4);     desktopBitmap = new Bitmap(correctedBounds.Width, correctedBounds.Height); }

The display preview serves little purpose in its current form, but it could be useful with many monitors.  It's a size-reduced version to scale quicker.  It also shows the display index (1, 2, etc) similar to in Display Properties.  The AddImageToPreview method is a bit long to show here, but it accounts for negative screen values, resizes accordingly, and renders each image onto its corresponding display.  The last step draws the number on in two steps using the GraphicsPath object to convert the supplied text to its vector path information.  It's drawn in black, a bit smaller in white to create the outlined format.  If you only draw with one color, there will always be bitmaps that render it invisible.  A white digit with a black outline takes care of this problem.

Visual Basic

Private Sub RenderCaption(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal caption As String)     Dim captionFont As New Font(FontFamily.GenericSansSerif, bounds.Height / 4)     Dim layoutRect As New Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height)     Dim path As New GraphicsPath()     path.AddString(caption, captionFont.FontFamily, CInt(captionFont.Style), _         CSng(captionFont.Height), layoutRect, StringFormat.GenericDefault)      Dim p As New Pen(Brushes.Black, 5)     g.DrawPath(p, path)     g.FillPath(Brushes.White, path) End Sub

Visual C#

private void RenderCaption(Graphics g, Rectangle bounds, string caption) {     Font captionFont = new Font(FontFamily.GenericSansSerif, bounds.Height/4);     Rectangle layoutRect = new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height);     GraphicsPath path = new GraphicsPath();     path.AddString(caption, captionFont.FontFamily,         (int)captionFont.Style, (float)captionFont.Height,         layoutRect, StringFormat.GenericDefault);          Pen p = new Pen(Brushes.Black, 5);     g.DrawPath(p, path);     g.FillPath(Brushes.White, path); }

The preview appears thus:

image

Image 3: The MultiWall application preview window (photos by the author)

Rendering the actual desktop image is almost the same, though at full-size and without the digit overlay.  One complication is that monitors can be in the "negative" zone (to the left or above the primary display).  The composite bitmap must have its 0,0 corresponding to the 0,0 of the primary display.  To account for the negative images, you must actually render the images to the far right, or far bottom.  The tiling effect performs a "wrap-around" to make it fit.  The worst part, is that, depending on where it is, you must draw the image multiple times to get all directions to tile properly!

Dragging, dropping, and layout

Figuring out a good way to actually set the monitor's wallpapers posed a design challenge.  I originally had Browse buttons in the preview window, but it was unwieldy.  I really wanted to be able to drag-and-drop onto the preview regions, but figuring out the drop coordinates relative to the original images, accounting for negative coordinates and a user-resizeable window wasn't realistic for the purposes of this article, but it's definitely possible!  Perhaps the easiest way would be convert the single PictureBox to dynamically placed PictureBox controls for each display.  But I digress...

My solution was to create a drop region in the top-middle of each display.  If you drag an image file to this region it will update the wallpaper accordingly.  Images are always cached using a WeakReference to avoid reloading if you switch back and forth.  Currently used images are also stored in a collection to provide a hard reference to prevent garbage collection on those.  If an image is reclaimed from cache it just gets reloaded.

image

Image 4: The bitmap drop region

These windows are set partially transparent and will disappear when the preview window is minimized to tray, unless the Show Drop Regions option is checked in the tray menu.  This setting is remembered when the application is closed.

Each time an image is dropped onto a region, the code builds both the preview bitmap and the desktop bitmap.  The application remembers the filenames so upon next startup it can show the individual bitmaps.  It could load the desktop's composite image, but it would need to follow extra steps to chop the composite image back into its constituent parts to redraw when one monitor is updated.

For more information on the registry update and system call required to actually update the wallpaper at the system level, see my earlier articles (referenced in the introduction).  The generated composite Bitmap file is saved to the My Pictures folder with the name "MultiWallImage.bmp".  The file could be hidden as an extra step as well.  Notice that it's quite large (12MB on my system!).  Unfortunately, Windows XP requires a BMP file.  Though you can supply a JPEG or other format using the Display Properties dialog, it converts it to BMP before it shows it.

Next Steps

No program is ever complete!  I wanted to add a way to take a single large image and have it chopped up in the best way to span multiple monitors, but it seemed more complex than expected when I dug into it.  I also wanted to add drag-and-drop to the preview window.  The preview window doesn't really serve much purpose, but it was a good exercise.  The program could be made more efficient with a background thread, and it should only redraw images if display settings change.  Currently if you drag an image to display #3, all display's images are redrawn/rescaled.  Finally, an image randomizer would be easy to add.  Use Windows (Desktop) Search or a given folder and cycle wallpapers upon startup/interval/random.

Conclusion

This article covered some graphics drawing techniques, drag-and-drop, system events, and wallpaper settings.  It's not the first wallpaper application on Coding 4 Fun, but it's probably the best (of mine anyway!).  For any comments, questions, or suggestions, contact me through my blog.  Happy papering!


Avatar80 Arian Kulp is an independent software developer and writer working in the Midwest.  He has been coding since the fifth grade on various platforms, and also enjoys photography, nature, and spending time with his family.  Arian can be reached through his web site at http://www.ariankulp.com.


 
 

Things you can do from here: