Friday, July 31, 2009

How to send Emails with Gmail Account with Attachment

First you need to change the settings in your Gmail account

step 1: Login to your Gmail Account with from which you will be sending e-mails.

step 2: Go to Gmail settings then click on Forwarding and POP/IMAP

step 3: In IMAP Access Check Enable IMAP

step 4: Then go to your application use below code

void AttachFile(string attachmentFile)
{
System.Net.Mail.MailAddress toAddress = new System.Net.Mail.MailAddress("your-reciving-email@gmail.com");

System.Net.Mail.MailAddress fromAddress = new System.Net.Mail.MailAddress("fromAddress@yahoo.com");
System.Net.Mail.MailMessage mm = new System.Net.Mail.MailMessage(fromAddress, toAddress);

mm.Subject = "Email Subject";
System.Net.Mail.Attachment mailAttachment = new System.Net.Mail.Attachment(printScreen);
mm.Attachments.Add(mailAttachment);
mm.IsBodyHtml = true;

mm.BodyEncoding = System.Text.Encoding.UTF8;
sendMail(mm);
}

NOTE1:
How do I specify multiple recipients? Printer Friendly
Because the
To, CC, and Bcc properties are MailAddress collections, to add
additional recipients, all we need to do is call .Add(...) on the
respective properties.


Below is an example that demonstrates adding multiple To, CC, and Bcc addresses.

[ C# ]

static void MultipleRecipients()
{
//create the mail message
MailMessage mail = new MailMessage();


//set the addresses
//to specify a friendly 'from' name, we use a different ctor
mail.From = new MailAddress("me@mycompany.com"
, "Steve James");

//since the To,Cc, and Bcc accept addresses, we can use the same technique as the From address
//since the To, Cc, and Bcc properties are collections, to add multiple addreses, we simply call .Add(...) multple times
mail.To.Add("you@yourcompany.com");
mail.To.Add("you2@yourcompany.com");
mail.CC.Add("cc1@yourcompany.com");
mail.CC.Add("cc2@yourcompany.com");
mail.Bcc.Add("blindcc1@yourcompany.com");
mail.Bcc.Add("blindcc2@yourcompany.com");

//set the content
mail.Subject = "This is an email";
mail.Body = "this is the body content of the email.";

//send the message
SmtpClient smtp = new SmtpClient("127.0.0.1");
smtp.Send(mail);

string sendMail(System.Net.Mail.MailMessage mm)
{
try
{
string smtpHost = "smtp.gmail.com";

string userName = "your-email-address@gmail.com";//sending Id
string password = "your-password";
System.Net.Mail.SmtpClient mClient = new System.Net.Mail.SmtpClient();

mClient.Port = 587;
mClient.EnableSsl = true;
mClient.UseDefaultCredentials = false;
mClient.Credentials = new NetworkCredential(userName, password);

mClient.Host = smtpHost;
mClient.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
mClient.Send(mm);
}
catch (Exception ex)

{
MessageBox.Show(ex.Message);
}
}
NOTE2

Have to replace \r\n with html line break "br"
in order to show paragraph and line breaks.
string sNewBody = textBoxBody.Text.Replace("\r\
n", "
");
//mm.Body = textBoxBody.Text;
mm.Body = sNewBody;

Monday, July 27, 2009

Showing a Separate Tooltip for Each Item in a ListBox

http://www.codeproject.com/KB/combobox/ToolTipForListBox.aspx

Introduction

ListBox is a very common control used in Windows Forms. Most often we would need to show some information on each item in the ListBox when the user places the mouse pointer above that item. Sadly there is no straight forward way to do that. The reasons are:

  1. There is no separate mouse hover event for individual items in the list.
  2. As each item in the list is not a control (of course!!), we cannot set a tool tip for items of the list separately. A tooltip can be set only for the ListBox as such.

But don't give up!! If there is a will, there is a way…

Using the Code

The steps required are explained below:

  1. Drag and drop a ToolTip control into the form having the ListBox. I am calling it ListToolTip.

  2. We have to know the ToolTip text for each item in the ListBox. I have an array of strings as items in the ListBox. So I maintain the ToolTip text for each item in another string array. So if I know the index of the Item in the array of items, I can get the corresponding ToolTip text from the ToolTip array using the same index.

  3. Create a handler for the Mouse Move event of the ListBox. Calculate the index of the item over which the mouse pointer is placed now. (This is the tricky part!!)

    1. Calculate the Offset of the item from the top of the list box. This can be obtained by dividing the Y coordinate of the house position by the height of each item. Height of each item is a property of the list box:

      Collapse
      itemIndex = e.Y / objListBox.ItemHeight; 
    2. But the topmost item will not always be the first item in the list. If there is a scrollbar and user scrolls down, then the topmost item visible will change. This means that the index obtained above is not the index of the required item in the list of items. Fortunately ListBox has another property called TopIndex. If we add the TopIndex with the index obtained in the above step, then we will get the correct index.

      Collapse
      itemIndex += objListBox.TopIndex;
  4. Now we know the index of the item over which the mouse pointer is placed. Just take the Tooltip of that item from the Tooltip array and associate it with the ToolTip control.

    Collapse
     if ((itemIndex >= 0) && (itemIndex < m_arrItemToolTips.Length))
    {
    ListToolTip.SetToolTip(objListBox, m_arrItemToolTips[itemIndex]);

    }
  5. If the calculated index is out of the range of valid indexes, hide the ToolTip.

    Collapse
      else
    {
    ListToolTip.Hide(objListBox);
    }

The entire code for the mouse move event handler is given below:

Collapse
private void ItemsListBox_MouseMove(object sender, MouseEventArgs e)

{
try
{
ListBox objListBox = (ListBox)sender;
int itemIndex = -1;
if (m_arrItemToolTips != null)

{
if (objListBox.ItemHeight != 0)
{
itemIndex = e.Y / objListBox.ItemHeight;

itemIndex += objListBox.TopIndex;
}
if ((itemIndex >= 0) && (itemIndex < m_arrItemToolTips.Length))

{
ListToolTip.SetToolTip(objListBox, m_arrItemToolTips[itemIndex]);
}
else
{
ListToolTip.Hide(objListBox);

}
}
}
catch (Exception ex)
{
}
}

Thursday, July 16, 2009

C#: Resize An Image While Maintaining Aspect Ratio and Maximum Height


Function works, but need 2 changes:

1. Set image properties, otherwise resized pics lose image information like date, camera type etc.
2. Need to specify image type when saving
// This allows us to resize the image. It prevents skewed images and
// also vertically long images caused by trying to maintain the aspect
// ratio on images who's height is larger than their width

public void ResizeImage(string OriginalFile, string NewFile, int NewWidth, int MaxHeight, bool OnlyResizeIfWider)
{
System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(
OriginalFile);


// Prevent using images internal thumbnail

FullsizeImage.RotateFlip(
System.Drawing.RotateFlipType.Rotate180FlipNone);

FullsizeImage.RotateFlip(
System.Drawing.RotateFlipType.Rotate180FlipNone);


if (OnlyResizeIfWider)
{
if (FullsizeImage.Width <= NewWidth) { NewWidth = FullsizeImage.Width; } } int NewHeight = FullsizeImage.Height * NewWidth / FullsizeImage.Width; if (NewHeight > MaxHeight)
{

// Resize with height instead

NewWidth = FullsizeImage.Width * MaxHeight / FullsizeImage.Height;

NewHeight = MaxHeight;
}


System.Drawing.Image NewImage = FullsizeImage.GetThumbnailImage(NewWidth, NewHeight, null, IntPtr.Zero);

//save all the photo properties
foreach (PropertyItem e in FullsizeImage.PropertyItems)
{
NewImage.SetPropertyItem(e);
}

// Save resized picture
NewImage.Save(NewFile, ImageFormat.Jpeg);
// Clear handle to original file so that we can overwrite it if necessary

FullsizeImage.Dispose();


}

Thursday, June 25, 2009

How to create button with picture(icon) and text for Window CE/Mobile

For desktop program with full compact framework, it is very easy to create button with picture and text. Just drag the button from toolbox. Then choose the image from properties. But compact framework has no such properties for button.

What we need to do is create our own customized button control like following (button.cs):

/*

Requirements: Microsoft Development Environment 2003 Version 7.1.3088
Microsoft .NET Framework Version 1.1.4322
*/

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Imaging;

// Assembly attribute that points to the run-time version of the assembly.
// This attribute is needed for this control to function properly during design time.
// When this control is added to the designer, the assembly indicated by
// this attribute is added to the reference collection of the project.
// The run-time assembly must be located in the reference path of the project,
// and the design-time version must appear in the "designer reference path".

//// The NETCFDESIGNTIME proprocessor variable is used to indicate whether the build is for a designer build or run-time device build.
//// This lets you add device specific routines and also desktop requirements (such as design-time attributes) without needlessly increasing your footprint.
//#if NETCFDESIGNTIME
// [assembly: System.CF.Design.
RuntimeAssemblyAttribute("xxxxxxx, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null")]
//#endif

namespace DJHome.SpecialButton
{
///
/// Button class.
///

public class Button : System.Windows.Forms.Control
{
#region Component Designer generated code

///
/// Clean up any resources being used.
///

/// True to dispose of control.
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}

// Required method for Designer support - do not modify
// the contents of this method with the Code Editor.
private void InitializeComponent()
{
this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnPaint);
}

#endregion

#region Constructors

///
/// Constructor.
///

public Button()
{
m_penBlack = new System.Drawing.Pen(Color.Black);
m_penGray = new System.Drawing.Pen(Color.Gray);
m_penLightGray = new System.Drawing.Pen(Color.Gainsboro);
m_penWhite = new System.Drawing.Pen(Color.White);

// This call is required by the Windows.Forms Designer.
InitializeComponent();
}

#endregion

#region Enumerations

///
/// Used to set the button style.
///

public enum ButtonStyle
{
///
/// Push button style.
///

PushButton,
///
/// Toggle button style.
///

ToggleButton
}

///
/// Used to set the vertical alignment of the text on the button.
///

public enum TLocation
{
///
/// Text at bottom of button.
///

Bottom,
///
/// Text at left of button.
///

Left,
///
/// Text at middle of button.
///

Middle,
///
/// Text at right of button.
///

Right,
///
/// Text at top of button.
///

Top
}

#endregion

#region Variables

private System.ComponentModel.Container components = null;
private Pen m_penBlack;
private Pen m_penGray;
private Pen m_penLightGray;
private Pen m_penWhite;
private Bitmap m_bitBackground = null;
private object m_oControlTag = false;
private Image m_imgDisabled = null;
private Image m_imgPressed = null;
private Image m_imgUnPressed = null;
private bool m_bPressed = false;
private ButtonStyle m_ButtonStyle = ButtonStyle.PushButton;
private TLocation m_textLocation = TLocation.Middle;
private string m_sText2 = "";
private Color m_textColor;

#endregion

#region Properties

// Store a bitmap that is created when the button gets the focus.
// Used to show the dotted lines when the button has the focus.
private Bitmap BitBackground
{
get
{
return m_bitBackground;
}
set
{
m_bitBackground = value;
}
}

///
/// Get or set the tag for the control.
///

public object ControlTag
{
get
{
return m_oControlTag;
}
set
{
m_oControlTag = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its disabled state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image to be displayed on the button when the button is disabled.
///

public Image ImageDisabled
{
get
{
return m_imgDisabled;
}
set
{
m_imgDisabled = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its pressed state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image displayed on the button when the button is in the pressed state.
///

public Image ImagePressed
{
get
{
return m_imgPressed;
}
set
{
m_imgPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its unpressed state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image displayed on the button when the button is in the unpressed state.
///

public Image ImageUnPressed
{
get
{
return m_imgUnPressed;
}
set
{
m_imgUnPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("True sets the button in the pushed state, false otherwise."),Category("Custom Settings"), DefaultValue(typeof(bool), "False")]
#endif
///
/// Get or set the pressed stated of the button.
///

public bool Pressed
{
get
{
return m_bPressed;
}
set
{
m_bPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("Set the button style to be push or toggle button."),Category("Custom Settings"), DefaultValue(typeof(ButtonStyle), "PushButton")]
#endif
///
/// Get or Set the button style.
///

public ButtonStyle Style
{
get
{
return m_ButtonStyle;
}
set
{
m_ButtonStyle = value;
Pressed = false;
this.Invalidate();
}
}

///
/// Set the text when changed.
///

public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The vertical location of the button text."),Category("Custom Settings"), DefaultValue(typeof(TLocation), "Middle")]
#endif
///
/// Location where the text is to be displayed on the button.
///

public TLocation TextLocation
{
get
{
return m_textLocation;
}
set
{
m_textLocation = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The text for the second line."),Category("Custom Settings"), DefaultValue(typeof(string), "")]
#endif
///
/// Set the text when changed.
///

public string Text2
{
get
{
return m_sText2;
}
set
{
m_sText2 = value;
this.Invalidate();
}
}

///
/// Color of the text to be displayed.
///

private Color TextColor
{
get
{
return this.m_textColor;
}
set
{
this.m_textColor = value;
}
}

#endregion

#region Functions

///
/// Create a bitmap image.
///

/// Rectangle.
/// Returns a bitmap image.
private Bitmap CreateBitmap(Rectangle r)
{
Bitmap bit = new Bitmap(this.Width, this.Height);
Graphics g = Graphics.FromImage(bit);

// Fill the bitmap with the background color.
g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, bit.Width, bit.Height);

// Set the pixels for the horizontal dashed lines.
for(int i = 5; i < r.Width - 4; i++)
{
bit.SetPixel(i, 4, Color.FromArgb(64, 64, 64));
i += 1;
}
for(int i = 6; i < r.Width - 4; i++)
{
bit.SetPixel(i, r.Height - 5, Color.FromArgb(64, 64, 64));
i += 1;
}

// Set the pixels for the vertical dashed lines.
for(int i = 5; i < r.Height - 3; i++)
{
bit.SetPixel(4, i, Color.FromArgb(64, 64, 64));
i += 1;
}
for(int i = 4; i < r.Height - 5; i++)
{
bit.SetPixel(r.Width - 5, i, Color.FromArgb(64, 64, 64));
i += 1;
}

return bit;
}

///
/// Draw the text on the graphics object.
///

/// PaintEventArgs.param>
/// Client rectangle.
/// Image width.
private void DrawText(PaintEventArgs e, Rectangle r, int iImageWidth)
{
SizeF size = e.Graphics.MeasureString(this.Text, this.Font);
SizeF size2 = e.Graphics.MeasureString(this.Text2, this.Font);
int iGap = 2;

// Shift the text if the button is pressed.
if (Pressed)
{
if (Text2.Length == 0)
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - size.Height) - 2);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) - iImageWidth, (r.Height - size.Height) / 2);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - size.Height) / 2);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) + iImageWidth, (r.Height - size.Height) / 2);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, 5);
break;
}
}
else
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - (2*size.Height)) - 2 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (r.Height - size.Height) - 2 - iGap);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) - iImageWidth, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width + 2) / 2) - iImageWidth, (r.Height / 2) - iGap);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (r.Height / 2) - iGap);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) + iImageWidth, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width + 2) / 2) + iImageWidth, (r.Height / 2) - iGap);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, 6 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (size2.Height + 6) - iGap);
break;
}
}
}
else
{
if (Text2.Length == 0)
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - size.Height - 1) - 2);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) - iImageWidth, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) + iImageWidth, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, 4);
break;
}
}
else
{
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - (2*size.Height) - 1) - 2 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (r.Height - size.Height - 1) - 2 - iGap);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) - iImageWidth, ((r.Height / 2) - size.Height) - 1 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width) / 2) - iImageWidth, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, ((r.Height / 2) - size.Height - 1) + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) + iImageWidth, ((r.Height / 2) - size.Height - 1 + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width) / 2) + iImageWidth, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, 5 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (size2.Height + 5 - iGap));
break;
}
}
}
}

///
/// Get the image attributes to set the transparent color.
///

/// Image.
/// ImageAttributes of the image.
private ImageAttributes GetImageAttributes(Image img)
{
Bitmap bit = new Bitmap(img);

// Set the Attributes for the Transparent color
ImageAttributes imageAttr = new ImageAttributes();
Color colorTrans = bit.GetPixel(0, 0);
imageAttr.SetColorKey(colorTrans, colorTrans);

return imageAttr;
}

#endregion

#region Controls

///
/// Repaints the button after the button is clicked.
///

/// EventArgs.
protected override void OnClick(EventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style == ButtonStyle.ToggleButton)
{
// Toggle the pressed state.
if (Pressed)
{
Pressed = false;
}
else
{
Pressed = true;
}
}
else
{
this.Pressed = false;
}

this.Invalidate();
base.OnClick (e);
}
}

///
/// Repaints the button in the enabled/disabled state.
///

/// EventArgs.
protected override void OnEnabledChanged(EventArgs e)
{
this.Invalidate();
base.OnEnabledChanged(e);
}

///
/// Sets the focus to the control.
///

/// EventArgs.
protected override void OnGotFocus(EventArgs e)
{
if (this.Enabled)
{
this.Focus();
this.Invalidate();
base.OnGotFocus(e);
}
}

///
/// Removes focus from the control.
///

/// EventArgs.
protected override void OnLostFocus(EventArgs e)
{
this.Invalidate();
base.OnLostFocus(e);
}

///
/// Sets the button to be pressed.
///

/// MouseEventArgs.param>
protected override void OnMouseDown (MouseEventArgs e)
{
if (this.Enabled)
{
// Check if the left mouse button was pressed.
if (e.Button == MouseButtons.Left)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
this.Pressed = true;
}

this.Focus();
this.Invalidate();
}

base.OnMouseDown(e);
}
}

///
/// Repaints the button when the mouse is moved.
///

/// MouseEventArgs.param>
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
// Check if the left mouse button was pressed.
if (e.Button == MouseButtons.Left)
{
// Paint the button in the unpressed state when the mouse is not over the button.
if ((Pressed) && (!this.ClientRectangle.Contains(e.X, e.Y)))
{
Pressed = false;
this.Invalidate();
}
else if ((!Pressed) && (this.ClientRectangle.Contains(e.X, e.Y)))
{
Pressed = true;
this.Invalidate();
}
}
}

base.OnMouseMove(e);
}
}

///
/// Resets the button.
///

/// MouseEventArgs.param>
protected override void OnMouseUp (MouseEventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
this.Pressed = false;
}

this.Invalidate();
base.OnMouseUp(e);
}
}

///
/// This Paint function uses routines common to both platforms.
///

private void OnPaint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Rectangle rcClient = this.ClientRectangle;
int intImageWidth = 0;

// Set the text color to be grayed or the forecolor.
if (this.Enabled == false)
{
TextColor = System.Drawing.SystemColors.GrayText;
}
else
{
TextColor = this.ForeColor;

// Draw the dotted lines that shows the button has the focus.
if (this.Focused)
{
// Create the bitmap image.
if (BitBackground == null)
{
BitBackground = CreateBitmap(rcClient);
}

// Draw the bitmap that was created and stored.
e.Graphics.DrawImage((Image)BitBackground, 0, 0);
}
}

// Draw the lines that makeup the button image.
e.Graphics.DrawLine(m_penGray, 0, 0, rcClient.Width - 1, 0);
e.Graphics.DrawLine(m_penGray, 0, 0, 0, rcClient.Height - 1);
e.Graphics.DrawLine(m_penLightGray, 1, 1, rcClient.Width - 2, 1);
e.Graphics.DrawLine(m_penLightGray, 1, 1, 1, rcClient.Height - 2);
e.Graphics.DrawLine(m_penGray, 1, rcClient.Height - 2, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penGray, rcClient.Width - 2, 1, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penBlack, 0, rcClient.Height - 1, rcClient.Width - 1, rcClient.Height - 1);
e.Graphics.DrawLine(m_penBlack, rcClient.Width - 1, 0, rcClient.Width - 1, rcClient.Height - 1);

// Check if the button is using an image.
if (ImageUnPressed != null)
{
intImageWidth = ImageUnPressed.Width / 2;
int intImageLeft = (rcClient.Width - ImageUnPressed.Width) / 2;
int intImageTop = (rcClient.Height - ImageUnPressed.Height) / 2;
Rectangle imgRect;

// If the text is being used, set the location of the image to be above, below, or in the middle of the text.
if (this.Text.Length > 0)
{
switch(TextLocation)
{
case TLocation.Bottom:
intImageTop = intImageTop - 5;
break;
case TLocation.Left:
intImageLeft = (rcClient.Width - ImageUnPressed.Width) - 5;
break;
case TLocation.Middle:
// Nothing to do here.
break;
case TLocation.Right:
intImageLeft = 5;
break;
case TLocation.Top:
intImageTop = intImageTop + 5;
break;
}
}

// Shift the image if the button is pressed.
if (Pressed)
{
imgRect = new Rectangle(intImageLeft + 1, intImageTop + 1, ImageUnPressed.Width, ImageUnPressed.Height);

// Draw the unpressed image if it was set.
if (ImagePressed != null)
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImagePressed);

// Draw the pressed image if set.
e.Graphics.DrawImage(ImagePressed, imgRect, 0, 0, ImagePressed.Width, ImagePressed.Height, GraphicsUnit.Pixel, imageAttr);
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

// Draw the unpressed image.
e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
else
{
// Draw the unpressed image if set.
imgRect = new Rectangle(intImageLeft, intImageTop, ImageUnPressed.Width, ImageUnPressed.Height);

// Check which image to set.
if (this.Enabled == false)
{
// Set the ImageDisabled if the button is disabled.
if (ImageDisabled != null)
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageDisabled);

e.Graphics.DrawImage(ImageDisabled, imgRect, 0, 0, ImageDisabled.Width, ImageDisabled.Height, GraphicsUnit.Pixel, imageAttr);
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
}

// Draw the text if needed.
if (this.Text.Length > 0)
{
DrawText(e, rcClient, intImageWidth);
}

// Set the button appearance when pressed.
if (Pressed)
{
// Draw the lines that makeup the button image in the pressed position.
e.Graphics.DrawLine(m_penBlack, 0, 0, rcClient.Width - 1, 0);
e.Graphics.DrawLine(m_penBlack, 0, 0, 0, rcClient.Height - 1);
e.Graphics.DrawLine(m_penBlack, 1, 1, rcClient.Width - 2, 1);
e.Graphics.DrawLine(m_penBlack, 1, 1, 1, rcClient.Height - 2);
e.Graphics.DrawLine(m_penLightGray, 1, rcClient.Height - 2, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penLightGray, rcClient.Width - 2, 1, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penWhite, 0, rcClient.Height - 1, rcClient.Width - 1, rcClient.Height - 1);
e.Graphics.DrawLine(m_penWhite, rcClient.Width - 1, 0, rcClient.Width - 1, rcClient.Height - 1);
}
}

///
/// Resize the control in the design view.
///

/// EventArgs.
protected override void OnResize(EventArgs e)
{
this.Refresh();
}

#endregion
}
}


You can always directly include this file to your project then use button in the code to dynamically generate a button. But if you want to use it like the button from toolbar, you have to do special thing if you are using Visual studio 2003.

If you are using Visual Studio 2005 or 2008, it is very easy.

1)Add you button.cs to you project.
2)Include its namespace.
3)From toolbar, you will find out there is one more category with "Pointer" "button" in it, which is the customized button you just created!!!

Wednesday, June 24, 2009

Understanding Garbage Collection in .NET

17 June 2009

Once you understand how .NET's garbage collector works, then the reasons for some of the more mysterious problems that can hit a .NET application become much clearer. NET may have promised the end to explicit memory management, but it is still necessary to profile the usage of memory when you're developing .NET applications if you wish to avoid memory-related errors and some performance issues.

.NET’s garbage collector has been sold to us as the end of explicit memory management, and of memory leaks, in Windows applications: the idea is that, with a garbage collector running in the background, developers no longer need to worry about the need to manage the life-cycle of the objects they create - the garbage collector will take care of them once the application has finished with them.

The reality is more complicated than this, however. The garbage collector certainly solves the most common leaks in unmanaged programs - those caused by developers forgetting to release memory when they have finished with it. It also solves the related problem of memory being released too soon, but the way in which this is solved can lead to memory leaks when the garbage collector has a different opinion to the developer about whether or not an object is still ‘live’ and able to be used. Before fixing these problems, you need some understanding of how the collector works.

How the Garbage Collector works

How, then, does the garbage collector achieve its magic? The basic idea is pretty simple: it examines how objects are laid out in memory and identifies all those objects that can be ‘reached’ by the running program by following some series of references.

When a garbage collection starts, it looks at a set of references called the ‘GC roots’. These are memory locations that are designated to be always reachable for some reason, and which contain references to objects created by the program. It marks these objects as ‘live’ and then looks at any objects that they reference; it marks these as being ‘live’ too. It continues in this manner, iterating through all of the objects it knows are ‘live’. It marks anything that they reference as also being used until it can find no further objects.

An object is identified, by the Garbage Collector, as referencing another object if it, or one of its superclasses, has a field that contains the other object.

Once all of these live objects are known, any remaining objects can be discarded and the space re-used for new objects. .NET compacts memory so that there are no gaps (effectively squashing the discarded objects out of existence) - this means that free memory is always located at the end of a heap and makes allocating new objects very fast.

GC roots are not objects in themselves but are instead references to objects. Any object referenced by a GC root will automatically survive the next garbage collection. There are four main kinds of root in .NET:

A local variable in a method that is currently running is considered to be a GC root. The objects referenced by these variables can always be accessed immediately by the method they are declared in, and so they must be kept around. The lifetime of these roots can depend on the way the program was built. In debug builds, a local variable lasts for as long as the method is on the stack. In release builds, the JIT is able to look at the program structure to work out the last point within the execution that a variable can be used by the method and will discard it when it is no longer required. This strategy isn’t always used and can be turned off, for example, by running the program in a debugger.

Static variables are also always considered GC roots. The objects they reference can be accessed at any time by the class that declared them (or the rest of the program if they are public), so .NET will always keep them around. Variables declared as ‘thread static’ will only last for as long as that thread is running.

If a managed object is passed to an unmanaged COM+ library through interop, then it will also become a GC root with a reference count. This is because COM+ doesn’t do garbage collection: It uses, instead, a reference counting system; once the COM+ library finishes with the object by setting the reference count to 0 it ceases to be a GC root and can be collected again.

If an object has a finalizer, it is not immediately removed when the garbage collector decides it is no longer ‘live’. Instead, it becomes a special kind of root until .NET has called the finalizer method. This means that these objects usually require more than one garbage collection to be removed from memory, as they will survive the first time they are found to be unused.

The Object Graph

Taken as a whole, memory in .NET forms a complicated, knotted graph of references and cross-references. This can make it difficult to determine the amount of memory used by a particular object. For instance, the memory used by a List object is quite small, as the List class only has a few fields. However, one of these is the array of objects in the list: this can be quite large if the list has many entries. This is almost always exclusively ‘owned’ by the list, so the relationship is fairly simple: The total size of the list is the size of the small initial object and the large array it references. The objects in the array could be another matter entirely, though: it’s possible that there could be some other path through memory through which they could be reached. In this case, it doesn’t make sense to count them as part of the ‘size’ of the list as they would remain even if the list ceased to exist, but neither does it make sense to count them via the alternative path - they’d remain if that was removed as well.

Things become even more confusing when circular references come into play.

When developing code, it’s more usual to think of memory as being organized into a more easily understood structure: a tree starting at individual roots:

Thinking in this way does, indeed, make it easier (or indeed possible) to think about how objects are laid out in memory. This is also how data is represented when writing the program or using a debugger, but this makes it easy to forget that an object can be attached to more than one root. This is usually where memory leaks in .NET come from: the developer forgets, or doesn’t ever realize, that an object is anchored to more than one root. Consider the case shown here: setting GC root 2 to null will not actually allow the garbage collector to remove any objects, which can be seen from looking at the complete graph but not from the tree.

A memory profiler makes it possible to view the graph from another perspective, as a tree rooted at an individual object and following the references backwards to put the GC roots at the leaves. For the ClassC object referenced by root 2, we can follow the references backwards to get the following graph:

Thinking in this way reveals that the ClassC object has two ultimate ‘owners’, both of which must relinquish it before the garbage collector can remove it. Any of the links between GC root 3 and the object can be broken in order to remove it once GC root 2 has been set to null.

This situation can arise easily in practical .NET applications. The most common one is that a data object becomes referenced by an element in the user interface, but isn’t removed when the data has been finished with. This situation isn’t quite a leak: the memory will be reclaimed when the UI control is updated with new data, but can mean that the application uses much more memory than would be expected. Event handlers are another common cause: it’s easy to forget that an object will last at least as long as the objects which it receives events from, which is forever in the case of some global event handlers such as those in the Application class.

Real applications, especially those with user interface components, have much more complicated graphs than this. Even something as simple as a label in a dialog box can be referenced from a huge number of different places…

It’s easy to see how the occasional object can become lost in this maze.

Limits of the Garbage Collector

Unused objects that are still referenced

The biggest limitation of the garbage collector in .NET is a subtle one: while it is sold as being able to detect and remove unused objects, it actually finds unreferenced objects. This is an important distinction: an object might never be referred to by a program ever again; but, while there is some path from it leading to an object that might still be used, it will never be released from memory. This leads to memory leaks; in .NET these occur when an object that will not be used again remains referenced.

The source of these leaks can be hard to detect, though the symptoms of rising memory usage are obvious. It’s necessary to determine which unused objects are remaining in memory, and then trace the references to find out why they are not being collected. A memory profiler is essential for this task: By comparing memory states while a leak is occurring, it is possible to find the troublesome unused objects, but no debugger can trace object references backwards.

The garbage collector is designed to deal with resources that are plentiful - that is, where it doesn’t matter when the object is released. On modern systems, memory falls into that category (it doesn’t matter when it’s reclaimed, just so long as it’s done in time to prevent a new allocation failing). There are still resources that don’t fall into this category: file handles need to be closed quickly to avoid causing sharing conflicts between applications, for example. These resources cannot be completely managed by the garbage collector, and so .NET provides the Dispose() method along with the using() construct for objects that manage these resources. In these cases the scarce resources used by the object are released quickly by the implementation of the Dispose method, but the much less critical memory is released later by the garbage collector.

Dispose means nothing special to .NET, so disposed objects must still be de-referenced. This makes objects that have been disposed but have not been reclaimed good candidates for the source of a memory leak.

Fragmentation of the Heap

A less widely known limitation in .NET is that of the large object heap. Objects that become part of this heap are never moved by the runtime, and this can lead to a program running out of memory prematurely. When some objects live longer than others, this causes the heap to form holes where objects used to be - this is known as fragmentation. The problem occurs when the program asks for a large block of memory but the heap has become so fragmented that there is no single region of memory big enough to accommodate it. A memory profiler can estimate the largest object that can be allocated by a program: if this is declining then this is likely to be the cause. An OutOfMemoryException caused by fragmentation will typically happen when the program apparently has a lot of free memory - on a 32-bit system, processes should be able to use at least 1.5Gb, but failures due to fragmentation will often start to occur before it is using that much memory.

Another symptom of fragmentation is that .NET will often have to keep the memory used by the empty holes allocated to the application. This causes it to apparently use much more memory than it needs when viewed in Task Manager. This effect is usually relatively harmless: Windows is quite good at realising that the memory occupied by the holes is not being used and will page it out, and if the fragmentation is not worsening then the program won’t run out of memory. It doesn’t look good to the user, though, who will probably think that the application is wasteful and ‘bloated’. This is often what is happening when a profiler shows that the objects allocated by a program are only using a small amount of memory but Task Manager shows that the process is occupying a large amount of space.

Performance of the Garbage Collector

In terms of performance, the most important characteristic of a garbage collected system is that the garbage collector can start executing at any time. This makes them unsuited for situations where timing is critical, as the timing of any operation can be thrown off by the operation of the collector.

The .NET collector has two main modes of operation: concurrent and synchronous (sometimes known as workstation and server). Concurrent garbage collection is used in desktop applications and synchronous is used in server applications such as ASP.NET by default.

In concurrent mode, .NET will try to avoid stopping the running program while a collection is in progress. This means that the total amount that the application can get done in a given period of time is less but the application won’t pause. It’s good for interactive applications where it’s important to give the impression to the user that the application is responding immediately.

In synchronous mode, .NET will suspend the running application while the garbage collector is running. This is actually more efficient overall than concurrent mode - garbage collection takes the same amount of time, but it doesn’t have to contend with the program continuing to run - but means that there can be noticeable pauses when a full collection has to be done.

The type of garbage collector can be set in the configuration file for the application if the default isn’t suitable. Picking the synchronous collector can be useful when it’s more important that an application has a high throughput instead of appearing responsive.

In large applications, the number of objects that the garbage collector needs to deal with can become very large, which means it can take a very long time to visit and rearrange all of them. To deal with this, .NET uses a ‘generational’ garbage collector, which tries to give priority to a smaller set of objects. The idea is that objects created recently are more likely to be released quickly, so a generational garbage collector prioritises them when trying to free up memory, so .NET first looks at the objects that have been allocated since the last garbage collection and only starts to consider older objects if it can’t free up enough space this way.

This system works best if .NET can choose the time of collection itself, and will be disrupted if GC.Collect() is called, as this will often cause new objects to become old prematurely, which increases the likelihood of another expensive full collection in the near future.

Classes with finalizers can also disrupt the smooth operation of the garbage collector. Objects of these classes can’t be removed immediately: they instead go to the finalizer queue and are removed from memory once the finalizer has been run. This means that any object they reference (and any object referenced by those, and so on) has to be kept in memory at least until this time as well and will require two garbage collections before the memory becomes available again. If the graph contains many objects with finalizers, this can mean that the garbage collector requires many passes to completely release all of the unreferenced objects.

There is a simple way to avoid this problem: implement IDisposable on the finalizable classes, move the actions necessary to finalize the object into the Dispose() method and call GC.SuppressFinalize() at the end. The finalizer can then be modified to call the Dispose() method instead. GC.SuppressFinalize() tells the garbage collector that the object no longer needs to be finalized and can be garbage collected immediately, which can result in memory being reclaimed much more quickly.

Conclusion

It becomes easier to understand memory and performance problems in an application if you take time to understand how the garbage collector works. It reveals that, while .NET makes the burden of memory management lighter, it does not completely eliminate the need to track and manage resources. It is, however, easier to use a memory profiler to diagnose and fix problems in .NET. Taking account of the way .NET manages memory early in development can help reduce problems, but even then such problems can still arise because of the complexity of the framework or third-party libraries.



This article has been viewed 3431 times.
Andrew Hunter

Author profile: Andrew Hunter

Andrew Hunter is a Software Engineer at Red Gate who is responsible for much of the recent rewrite of ANTS. Before that, he wrote the SQL Layout utilities for SQL Refactor/SQL Prompt. He has been described as resident master of the dark .NET arts. Dereferenced in a freak accident, he is forced to spend his days hiding from the garbage collector.

Search for other articles by Andrew Hunter

Rate this article: Avg rating: from a total of 32 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:


Subject: Large Object Heap
Posted by: Jon (view profile)
Posted on: Friday, June 19, 2009 at 4:20 PM
Message: If you do run into fragmentation problems with the Large Object Heap, it can sometimes be useful to break up your object into smaller objects that won't be put in the LOH. Here's an example of doing that with a MemoryStream:
http://blueonionsoftware.com/blog.aspx?p=61115cc1-7188-44d3-b8c0-8ef8a618fae6

Subject: Generational Garbage collection demonstration
Posted by: NoMoreHacks (not signed in)
Posted on: Wednesday, June 24, 2009 at 1:47 AM
Message: I created a demo to show the effects of generational garbage collection in systems that require high-throughput with low latency. The code and article are here

http://nomorehacks.wordpress.com/2008/11/27/forcing-the-garbage-collector/

Subject: .NET Memory Management
Posted by: grabnerandi (view profile)
Posted on: Wednesday, June 24, 2009 at 9:07 AM
Message: Hi Andrew
Thanks for the very detailed explaination of the .NET Gargabe Collector. Although we live in the managed world for a while now it is always good to refresh our memory about how GC works internally.
I've been doing some work regarding .NET Memory Management myself and I posted some of these findings at http://blog.dynatrace.com/tag/memory/
I also compared the the different LatencyMode's that the GC has to offer: http://blog.dynatrace.com/2009/04/13/performance-analysis-comparing-interactive-and-low-latency-gc-strategies-in-net/

Cheers
Andi

How to use C++ APIs in C# application?Presenting a C++ library (.lib) for use in C# project?

Presenting a C++ library (.lib) for use in C# project

Q:
I have a collection of C++ programs and the C++ libraries they use. The libraries currently compile to a .lib format. Changing the project property to use a .dll results in multiple reference errors.

Without having to go through the entire collection of source files, is there a way to create any kind of project in a .NET language that would provide a "wrapper" for the existing .lib files so that they could be used in that environment.

Thanks.

A:

Yes (sort of) but it's non-trivial.

Let's start with some background. Your project to compile to a .lib format is generating something known as a "static library". Static libraries are chunks of native code built to be combined with other pieces of code via the linker. You #include a header file that describes the functions/classes that you want to use and then link to the .lib file to provide the implementations of those functions/classes.

You can't use that approach with a C# client because C# generates managed code and you just can't directly link native code to managed code.

There are three ways (I can think of) to consume native code from a managed code client

  1. You turn the native code into a native code COM object using the static library to provide the implementation of the COM object's methods and then use COM Interop to invoke the (native) COM object from the managed code client
  2. You turn the native code into a native code DLL that exports some suitable entry points (again, using the static library to provide the actual implementation) and then call those DLL entry points from the managed code client using P/Invoke
  3. You create a managed code Class Library using C++ and use the static library to provide the underlying implementation of that managed class library

Let's say your static library's header file looks like this




namespace MathFuncs
{
class MyMathFuncs
{
public:
// Returns a + b static double Add(double a, double b);
// Returns a - b static double Subtract(double a, double b);
// Returns a * b static double Multiply(double a, double b);
// Returns a / b // Throws DivideByZeroException if b is 0 static double Divide(double a, double b);
};
}

Using approach #1, you'd create a new project using the Visual C++ | ATL | ATL Server project template. You'd add a new simple ATL object to the project that exported Add, Subtract, Multiply and Divide methods. You'd #include your static library's header file to provide the underlying implementation of Add, Subtract etc and you'd have your COM object's Add method call the underlying MathFuncs::MyMathFuncs::Add method (and return the result). Finally, you'd make sure your COM object project linked to your static library .lib file as part of the build process.

To consume the 'wrapper' from a C# program, you'd use Add | Reference to add a COM reference to your COM object and then just cal the COM object's methods as necessary.

Using approach #2, you'd create a new project using the Visual C++ | Win32 | Win32 Project template and use Application Settings to change Application Type to DLL and set Additional Options to "Export symbols". Then you would create some exported entry points (like Add, Subtract etc) to be consumed by the client. Again, you would #include your static library's header file to get function descriptions and link to the .lib file to get actual implementations. You'd code your exported DLL entry points to call the static library's 'real' implementation methods and return the result.

To consume this kind of wrapper, you would use the C# DllImportAttribute to declare the DLL entry point in C# terms and then just call the function as needed.

Finally, using appoach #3, you'd create a managed C++ project using Visual C++ | CLR | Class Library. You'd create a managed class (a 'ref' class) which would be called by your C# client. Again, you'd #include the native code header file and link to the .lib file to get the actual implementation of the methods you want the client to be able to call. The managed code wrapper for the above header file might look something like this...




#include "MathFuncsLib.h" #pragma once
using
namespace System; namespace ManagedMathFuncsLib {
public ref class Class1
{
public:
Class1 ()
{
};
// Returns a + b static double Add(double a, double b)
{
return MathFuncs::MyMathFuncs::Add (a, b);
}
// Returns a - b static double Subtract(double a, double b)
{
return MathFuncs::MyMathFuncs::Subtract (a, b);
};
// Returns a * b static double Multiply(double a, double b)
{
return MathFuncs::MyMathFuncs::Multiply (a, b);
};
// Returns a / b // Throws DivideByZeroException if b is 0 static double Divide(double a, double b)
{
return MathFuncs::MyMathFuncs::Divide (a, b);
};
};}

To consume this managed code wrapper, you'd use Add | Reference to add a .NET Reference (to your managed wrapper) to your client program and then would call the managed (ref) class's methods as necessary.

In all three cases above, I simplified things by using static methods in the original native code implementation. If your static library implements classes with non-static functions then in all three approaches above you would have to conceal a private member variable that pointed to an instance of your native class and would have to new up that instance at some suitable point (in the constructor of the wrapper for example) and would have to use that private member variable to actually invoke the actual implementation of the method you were trying to call.


Q: Could you go into a little more detail regarding the last paragraph of your post. I'm unclear as to dealing with functions that are not declared static.

Where are you creating the private member?

How do you invoke that private member, PInvoke?

Would it be possible to see another example?

Thanks

Blue


A:

There's no need to use P/Invoke because the wrapper is written in Managed C++ so it can invoke the private member's functions directly.

Let's change the example slightly and create a statistical functions library that can be used to calculate a number of statistical properties of a group of numbers. Since I'm lazy I'll just calculate average but you could imagine calculating standard deviation and median and all sorts of things. To keep the example simple, I'll not store the actual data items, I'll just incorporate them into a sum variable as I go. Again though you could imagine a much more elaborate data structure underneath this native class.

The header file for the native code static library implementation of this statistical functions library might look something like this




// StatFuncsLib.h namespace StatFuncs
{
class MyStatFuncs
{
private:
double m_Count;
double m_Sum;

public:
// Constructs an instance and initialises private members
MyStatFuncs ();

// Adds item and returns updated count
double AddItem(double item);

// Returns average
// Throws DivideByZeroException if count is 0
double Average ();

// Returns count of items
double Count ();
};
}

The AddItem function will increment m_Count by 1 and will add item to m_Sum while the Average function will return m_Sum divided by m_Count.

Because data is accumulated in the private member variables m_Count and m_Sum we can't just use static functions, we have to use individual instances of the MyStatFuncs class. That means when we create our managed C++ wrapper class it will have to create and use an instance of the MyStatFuncs class. The wrapper class might look something like this




// ManagedStatFuncsLib.h #pragma once
#include
"StatFuncsLib.h" using namespace System; namespace ManagedStatFuncs {

public ref class MyManagedStatFuncs
{

private:
StatFuncs::MyStatFuncs * m_NativeStatFuncs;

public:

// Constructs an instance of the class and an instance
// of the underlying native class MyStatFuncs
MyManagedStatFuncs ()
{
m_NativeStatFuncs =
new StatFuncs::MyStatFuncs ();
};

// Adds item and returns updated count
double AddItem(double item)
{
return m_NativeStatFuncs->AddItem (item);
};

// Returns average
// Throws DivideByZeroException if b is 0
double Average ()
{
return m_NativeStatFuncs->Average ();
};

// Returns count of items
double Count ()
{
return m_NativeStatFuncs->Count ();
};
};
}

The managed C++ wrapper will compile up into an assembly that can be referenced from a C# program (using Add Reference). Then the C# program just creates instances of the managed wrapper class and invokes the various functions as needed. For example




ManagedStatFuncs.MyManagedStatFuncs myStatOne =
new ManagedStatFuncs.MyManagedStatFuncs();
ManagedStatFuncs.MyManagedStatFuncs myStatTwo =
new ManagedStatFuncs.MyManagedStatFuncs();

myStatOne.AddItem(1.0);
myStatOne.AddItem(3.0);
myStatOne.AddItem(5.0);

myStatTwo.AddItem(2.0);
myStatTwo.AddItem(4.0);
myStatTwo.AddItem(6.0);
myStatTwo.AddItem(8.0);

Console.WriteLine(
"First group consists of {0} items with an average value of {1}",
myStatOne.Count(), myStatOne.Average());
Console.WriteLine(
"Second group consists of {0} items with an average value of {1}",
myStatTwo.Count(), myStatTwo.Average());
Q:

This is a good example! Thanks!

I am trying to get this code to work on a Pocket PC device. Is it possible to build a managed C++ assembly for the Pocket PC. So far I have only been able to build the managed C++ assembly for Win32. Visual studio will not let me do Add Reference for a Win32 assembly in C#. When I try to add reference I get an error saying that ManagedStatFuncs can not be added because it is not a device project. Maybe this is because Win32 managed code is not compatible with compact frameworks. Maybe I need a different project template. Does anyone have any thoughts on this?


A:

Brian Wall wrote:
Is it possible to build a managed C++ assembly for the Pocket PC?

In a word, no. I can't find an authoritative reference to cite but if you search the web (or these forums) for "Managed C++" and "Smart Device" you'll find several posts that say it can't be done. For example this chat transcript

Q: Will there be support for Managed C++ with .NET CF 2.0?
A: Sorry, no. Only VB and C#.

http://msdn.microsoft.com/chats/transcripts/mobileembedded/netcf_020805.aspx