|
Greeting fellow geeks & geekettes,
I'm running into a difficult problem that I'm finding very hard to troubleshoot, and I hope some people here have had the same issue and can help me resolve it.
I have a couple of websites on our server that use GDI+ to generate some graphics on the fly. The idea is to generate menu items with a specific font.
All it does is create a bitmap, read a background image file and copy over, then loads a local font file using a PrivateFontCollection and write the text onto it. The bitmap is then outputted straight onto the response stream. Pretty straight forward.
Now the problem is that server runs fine for a while, then after a while (few hours, a day at most), the server stops responding. Now the clue is that it only stops responding on those pages that use those generated labels, other pages seem ok.
An IIS restart solves the problem for another few hours, or so.
I would think it's because GDI+ resources are being wasted/leaked somehow.. yeah?
I've tried making the code as tidy as possible, closing/disposing of resources & streams but it's not helping (I actually think it lasts longer if I don't manually clean up but that's just a crazy thought). I've even tried caching the resulting bitmaps to avoid recreating all the GDI+ objects every time.
I have also the impression that it's the font loading/drawing part that's causing the problem, as similar code works fine on another project that doesn't actually write using fonts (just generate thumbnails).
Some pages can have up to 25 generated labels on them, which means 25 requests at a time on the server to generate these. Maybe too many simultaneous access to resources?
Finally, the server is a Windows 2000 Server with IIS 5. My development machine is a Windows 2003 and it doesn't seem to have that problem. However I suspect this is not a valid assertion that Windows 2003 works better as I'm the only user doing requests to the development machine and that is not reflecting the load put on the live server.
Anyway, basically, I'm a bit lost with this, and not being able to reproduce on the development machine doesn't help either.
Any suggestion or idea to test this would be greatly appreciated!
Cheers,
Ben
|
|
|
|
|
Ben[dog] wrote: I would think it's because GDI+ resources are being wasted/leaked somehow.. yeah?
I'd say so
Ben[dog] wrote: and not being able to reproduce on the development machine doesn't help either.
That never bodes well.
You should post some code, if you want us to comment on it.
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Hi Christian,
Thanks for trying to help me out..
Christian Graus wrote: Ben[dog] wrote:
I would think it's because GDI+ resources are being wasted/leaked somehow.. yeah?
I'd say so
Do you know or can you suggest any way to test for this?
Christian Graus wrote: You should post some code, if you want us to comment on it.
Didn't want to scare everybody right away with all the code.
Here's the generateLabel.aspx.cs essential:
public class generateLabel : System.Web.UI.Page
{
private ImageFormat Format;
private Color TextColor;
private Color TextColorOver;
private bool Over;
private string Text;
private string Background;
private void Page_Load(object sender, System.EventArgs e)
{
TextColorOver = Color.FromArgb(255,255,255);
TextColor = Color.FromArgb(200,200,200);
if (Request["Over"]!=null)
Over = true;
else
Over = false;
if (Request["Format"]!=null)
{
switch (Request["Format"].ToLower())
{
case "png":
ContentType = "image/png";
Format = ImageFormat.Png;
break;
case "gif":
ContentType = "image/gif";
Format = ImageFormat.Gif;
break;
case "jpg":
case "jpeg":
ContentType = "image/jpeg";
Format = ImageFormat.Jpeg;
break;
}
}
else
{
ContentType = "image/png";
Format = ImageFormat.Png;
}
if (Request["Text"]!=null)
Text = Request["Text"];
else
Text = "Sample";
if (Request["Background"]!=null)
{
switch (Request["Background"])
{
case "On":
Background = "images/RootMenuBackgroundOn.png";
break;
}
}
else
Background = "images/RootMenuItemBackground.png";
if (Cache[CacheKey]!=null)
generateFromCacheKey();
else
generate();
}
private string key;
private string CacheKey
{
get
{
if (key==null)
{
key = String.Format("Label_{0}{1}{2}",Text,Format.ToString(),Over);
}
return key;
}
}
private void generateFromCacheKey()
{
Bitmap b = (Bitmap) Cache[CacheKey];
MemoryStream io = new MemoryStream();
b.Save(io, Format);
Response.BinaryWrite(io.ToArray());
io.Close();
}
private void generate()
{
try
{
PrivateFontCollection pfc=new PrivateFontCollection();
pfc.AddFontFile(Server.MapPath("LTe50342.ttf"));
FontFamily family= new FontFamily("Avenir LT 45 Book",pfc);
Font myFont=new Font(family,14,FontStyle.Bold, GraphicsUnit.Pixel);
Bitmap tmpBitmap = new Bitmap(100,100,PixelFormat.Format32bppArgb);
Graphics objGraphics = Graphics.FromImage(tmpBitmap);
SizeF textSize=objGraphics.MeasureString(Text,myFont);
textSize.Width+=5;
objGraphics.Dispose();
tmpBitmap.Dispose();
System.Drawing.Image imgBackground = System.Drawing.Image.FromFile(Server.MapPath(Background));
tmpBitmap = new Bitmap((int)textSize.Width,imgBackground.Height);
objGraphics = Graphics.FromImage(tmpBitmap);
Brush b = new TextureBrush(imgBackground,WrapMode.Tile);
objGraphics.FillRectangle(b,0,0,(int)textSize.Width,imgBackground.Height);
objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
objGraphics.TextContrast = 12;
objGraphics.DrawString(Text, myFont, new SolidBrush(Over?TextColorOver:TextColor),0,(imgBackground.Height-textSize.Height)/2);
MemoryStream io = new MemoryStream();
tmpBitmap.Save(io, Format);
Response.BinaryWrite( io.GetBuffer() );
io.Close();
b.Dispose();
pfc.Dispose();
imgBackground.Dispose();
}
catch (Exception ex)
{
Utility.WriteLog(ex.Message);
}
}
Thanks, much appreciated help,
Cheers
Ben
|
|
|
|
|
I see a Bitmap object and a Graphics object that you never dispose.
---
single minded; short sighted; long gone;
|
|
|
|
|
Ah, cool. Thanks for that.
Well noted for the Graphics object.
The bitmap shouldn't be disposed though, I cut out one too many line while cleaning up before posting it, the bitmap is also saved in the cache.
So if the code would be that then:
b.Dispose();
pfc.Dispose();
imgBackground.Dispose();
objGraphics.Dispose();
Cache[CacheKey] = tmpBitmap;
Would that seem correct then?
Ben
|
|
|
|
|
Yes, that would take care of those.
---
single minded; short sighted; long gone;
|
|
|
|
|
Ben[dog] wrote: Bitmap b = (Bitmap) Cache[CacheKey];
This is not a leak, as it's in your cache, you don't want to dispose of it.
The easy way to write code that makes clear that yo dispose of resources is a using block
using (BItmap bm = Bitmap.FromFile(path))
{
// do stuff with bm here
} // bm.Dispose is automatically called here.
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Christian Graus wrote: The easy way to write code that makes clear that yo dispose of resources is a using block
I agree this is often a neat way to do it, though a bit tiresome with GDI+ as there's so many objects created you need to wrap too many using() statements within another, making it a bit hard to move code around or re-arrange things...
But if the individual Dispose() are explicitely called, that shouldn't really make a difference, should it?
Ben
|
|
|
|
|
No, it simply gives you a way to see easily that they are going to be called. I didn't track through all your code, but Guffa said he found two items that were not.
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Christian Graus wrote: Guffa said he found two items that were not.
Yes, I replied to him, actually one one of them was not disposed, the other is kept in the cache so shouldn't be disposed.
Anyway, thanks for your help, I appreaciate it.
I will do more testing with that last object disposed and see what happens.
Cheers
Ben
|
|
|
|
|
Ben[dog] wrote: But if the individual Dispose() are explicitely called, that shouldn't really make a difference, should it?
Yes, there is a difference.
The using statement makes a copy of the reference and creates a try...finally block, which ensures that the Dispose method is called no matter what happens in the code.
If you just call Dispose, it will depend on two things: that the code is not interrupted before that and that the reference is still valid at that point.
---
single minded; short sighted; long gone;
|
|
|
|
|
Aaaah, cool.
That's interesting. Thanks for that explanation, makes plenty of sense. I'll see to convert my label-thingie to that.
You should be a writer for the MSDN, they've got some trouble making things that clear.
Ben
|
|
|
|
|
Side question to this thread:
is
MemoryStream io = new MemoryStream();
tmpBitmap.Save(io, Format);
Response.BinaryWrite(io.ToArray());
io.Close();
The most appropriate way to send the image to the output stream?
i've seen variations of this elsewhere.. wondering if there is the right way, and the right way
|
|
|
|
|
I had this working in my project, then I wanted to use this code in another project and now it refuses to save a BMP file.
Please someone tell my what I overlook.
Ranger.
float range = 1.5F;
float startRe = -0.7F;
float startIm = 0.27015F;
int startColor = 60;
public Bitmap objBitmap = new Bitmap(300, 300);
public Graphics objGraphic;
public Pen myPen = new Pen(Color.Red);
protected void enterButton_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
objGraphic = Graphics.FromImage(objBitmap);
MakeJuliaSetBmp(); // Do the drawing
Response.ContentType = "image/bmp";
MakeUniqueFileName(); // makes a unique filename for the BMP file
objBitmap.Save(Request.PhysicalApplicationPath + "/pictures/" + filename, ImageFormat.Bmp);
Response.Flush();
objGraphic.Dispose();
objBitmap.Dispose();
myPen.Dispose();
}
}
Novice
|
|
|
|
|
Ranger49 wrote: now it refuses to save
What do you mean? An exception is thrown? The files isn't created? Are you sure the proper rights have been assigned to the destination folder?
only two letters away from being an asset
|
|
|
|
|
Ranger49 wrote: public Bitmap objBitmap = new Bitmap(300, 300);
public Graphics objGraphic;
public Pen myPen = new Pen(Color.Red);
Why are these public ? Why are they members at all ?
Ranger49 wrote: MakeUniqueFileName
Why does this not return the filename ?
It's probable you don't have write permissions on the folder, or it doesn't exist. Hard to say without seeing the error
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
The problem is that the enterButton_Click method (which should call a class to compute a fractal which then saves it to a Bmp pic file) never runs. Also my codefile in the App_Code ProduceImageFile.cs never is even run.
I wanted the enter.aspx to call the ProduceImageFile.cs but somehow that never happens.
In enter.aspx is a startButton_Click method which also never seems to fire, even though it does move you to the display.aspx which is intended to display the computed picture, the wanted calculations are never made on this click.
So there may not be any bugs in the code where I expected it, this code never is reached and executed!
About the App_Code folder, do you need to do something with the namespace or something to make sure the aspx (cs) file can find it?
Hope this is clearer now.
Thanks everybody.
Ranger.
Novice
|
|
|
|
|
Ranger49 wrote: About the App_Code folder, do you need to do something with the namespace or something to make sure the aspx (cs) file can find it?
No.
How are the buttons hooked up to events ? If they are created on the fly, they need to be created in the LoadViewState event, so that they exist when view state is restored, and their events are not lost.
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Christian Graus wrote: How are the buttons hooked up to events ?
I only got a enterButton_Click method.
Is there a LoadViewState event in the properties window of the button? I couldn't find it.
Ranger.
Novice
|
|
|
|
|
As it turned out the code was never even reached.
So I made an instance of the class that should generate a bmp file in the Page_Load handler of my enter.aspx.
Now it did run, but now I get the message:
Response.ContentType = "image/bmp"; not available in this context...! Which is the ProduceImageFile.cs (a class file)I checked all using statements, and I think I got all the ones I need.
When you type R you get Response and then you type '.' and C and get Response.ContentType, so I feel this should be available, but for some reason I get an exception that my application cannot handle the
Response.ContextType = "image/bmp"; statement.
Could anybody tell me how to fix this? It is kind of frustrating to have to spend days on simple things that don't want to work, simply because all those online help files only address the hard issues.
I tried these concepts in small projects and got them to run properly, then I tried to encorporate them in a larger application and again it refuses to function!
Ranger.
Novice.
|
|
|
|
|
how can I send email via asp.net 1.1 and C# and sqlserver2000?
I dont know how should I write for mail.smtpserver
plz tell me.
thanks alot. bye
|
|
|
|
|
Do you know the mail server you want to use ? If not, check your outlook settings.
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
using System.Web.Mail;
#region SendMail
public bool SendMail(string TargetEmailAddress,string MessageBody)
{
try
{
MailMessage mailMsg = new MailMessage();
mailMsg.To = TargetEmailAddress;
mailMsg.From = "AnyEmailAddress";
mailMsg.Subject = "Subject Of Email";
mailMsg.BodyFormat = MailFormat.Html;
mailMsg.BodyEncoding = Encoding.UTF8;
mailMsg.Body = MessageBody;
SmtpMail.Send(mailMsg);
return true;
}
catch
{
return false;
}
}
#endregion
We Can Do Anything, If We Want It
|
|
|
|
|
Hi,
I have 2 web application hosted on 2 separate servers.In first application i had one anchor tag whose target=_None and href="url of 2nd application"
Is it possible to access the cookie which is created by first application in second application ? if yes how?
Any help on this.........
Regards,
Ritesh
|
|
|
|
|
I want to have a website with two sections; Admin and user section . How can I have two cookie path for authenticating purpose. As you might know we sepcify a path in web.config file for cookie path.
<forms name=".SiteAUTH" protection="All" timeout="120" path="/">
<credentials passwordformat="SHA1">
Is it neccessary for me to have two paths for the two section . How is this possible ?
What I want is to have to kind of users ; administrators and normal users .
|
|
|
|