Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(#"C:\Temp\printscreen.jpg", ImageFormat.Jpeg);
This is my code for print screen button. The problem is that I press the button few times and it just write over the old image file (printscreen.jpg), it will not create another new image file such as printscreen1.jpg.
Try this. It will generate a unique file each time.
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(#"C:\Temp\printscreen"+Guid.NewGuid()+".jpg", ImageFormat.Jpeg);
OR
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
var date = DateTime.Now.ToString("MMddyyHmmss");
bitmap.Save(#"C:\Temp\printscreen"+date+".jpg", ImageFormat.Jpeg);
it will not create another new image file
You are not asking it to create one, your code
bitmap.Save(#"C:\Temp\printscreen.jpg", ImageFormat.Jpeg);
is always writing to the same file.
new image file such as printscreen1.jpg
If you want to create a new file, you need to generate your filename dynamically. Something like
string fileName = System.IO.Path.GetTempPath() + DateTime.Now.ToString() + ".jpg";
bitmap.Save(fileName, ImageFormat.Jpeg);
You could count how many files already exist in the directory that start with printscreen.
int count = Directory.EnumerateFiles(#"C:\Temp\").Where(x => x.StartsWith("printscreen")).Count();
bitmap.Save(String.Format(#"C:\Temp\printscreen{0}.jpg", count), ImageFormat.Jpeg);
This might be slower if you have a lot of files since it has to iterate through them but it's more automated.
Another option would be to create a file that stores the current counter number.
File.Write("Counter.txt", counter.ToString());
Then when you want to start the counter after your program restarts, you can do this.
int counter = 0;
if(File.Exists("Counter.txt"))
counter = Int32.Parse(File.ReadAllText("Counter.txt"));
bitmap.Save(String.Format(#"C:\Temp\printscreen{0}.jpg", ++counter), ImageFormat.Jpeg);
Of course, if you don't plan on restarting the app and continuing the sequence, you can avoid the storing of the counter in a text file and just increment the counter from zero.
Create a field say counter and increment it on every button click and use it for file name -
bitmap.Save(#"C:\Temp\printscreen" + (counter++) + ".jpg", ImageFormat.Jpeg);
You can also try like this:
int number;
string[] path = Directory.GetFiles(#"C:\Temp\", #"PrintScreen*");
if (path.Length == 0)
{ bitmap.Save(#"C:\Temp\printscreen.jpg", ImageFormat.Jpeg); return; }
Array.Sort(path);
Array.Reverse(path);
int.TryParse(Regex.Match(path[0], #"(\d+)(?=\.jpg)").ToString(), out number);
bitmap.Save(#"C:\Temp\printscreen" + (number + 1).ToString() + ".jpg", ImageFormat.Jpeg);
I think it would be more efficient to pick the last file by sorting it in descending order rather than enumerating over them. Also, no need to keep track of separate counter pick the last count and increment it.
{
//Application.DoEvents();
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
string path = "";
if (forLog)
{
path = LoggerPath + StudentNumber.ToString() + "\\" + SessionID.ToString() + "\\";
}
else
{
path = PrintScreenPath + StudentNumber.ToString() + "\\" + SessionID.ToString() + "\\";
}
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
string fileName = DateTime.Now.Ticks.ToString();
SaveJPGWithCompressionSetting(printscreen, path + fileName + ".jpeg", 17L);
printscreen.Dispose();
graphics.Dispose();
return fileName;
}
And ere is SaveJPGWithCompressionSetting metHod
private void SaveJPGWithCompressionSetting(Image image, string szFileName, long lCompression)
{
try
{
EncoderParameters eps = new EncoderParameters(1);
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, lCompression);
ImageCodecInfo ici = GetEncoderInfo("image/jpeg");
image.Save(szFileName, ici, eps);
}
}
Related
I have document processor that extract thumbnails and Large images from tiff file.
But i get "file already in use" and "A generic error occurred in GDI+" error most frequently.
There three basic functions:
Get tiff pages count
Get all thumbnails
Get all large images
The file stream is used in all three operations.
Is there any way that I can use the file stream once, save a copy of the file in memory and release the file for other use.
I tried this:
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
{
photo = Image.FromStream(fs, true, false);
fs.Flush();
fs.Close();
}
using (photo)
{
//dummy hight and width passed to function.
generateThumbNails(photo,1,90,90);
System.Drawing.Imaging.FrameDimension dimention = new FrameDimension(photo.FrameDimensionsList[0]);
total = photo.GetFrameCount(dimention);
photo.Dispose();
}
generateThumbNails function:
public static Bitmap GenerateThumbNails(System.Drawing.Image img, int curPageNo, int width, int height)
{
System.Drawing.Imaging.FrameDimension dimention = new FrameDimension(img.FrameDimensionsList[0]);
img.SelectActiveFrame(dimention, curPageNo);
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(img, 0, 0, width, height);
g.Dispose();
return bmp;
}
But I get "A generic error occurred in GDI+" while re-sizing the image on the following line:
g.DrawImage(img, 0, 0, width, height);
I have 2 Apps (server - client ) .
The server is modified version of TVsharp (Application that stream local analog tv signal using RTLSDR)
Each frame of the streamed video is a grayscale array of bytes .
I have modified it so it re sizes and sends each frame for a client over TCP socket
The client is supposed to receive the frames through the socket as Image objects and displays them in a picture box
Im getting invalid parameters error .
After i added a delay (Thread.Sleep()) it started to display one frame and then it gives invalid parameter exception (after the sleeping time)
This is the part of TVsharp that dose the sending :
Grayscale is an array that contains the brightness for each pixel
private string drive = "E:\\";
private string file = "0";
private string extension = ".bmp";
private string path2 = "E:\\test\\";
private string fullpath;
private int file_counter = 0;
Bitmap bitmap2 = new Bitmap(_pictureWidth, _pictureHeight);
var data2 = bitmap2.LockBits(new Rectangle(Point.Empty, bitmap2.Size),
ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);
Marshal.Copy(GrayScaleValues, 0, data2.Scan0, GrayScaleValues.Length);
bitmap2.UnlockBits(data2);
bitmap2.Save(fullpath, System.Drawing.Imaging.ImageFormat.Bmp);
string Npath = path2 + file + extension;
Image img = Image.FromFile(fullpath);
// Size size = new Size(982, 543);
// ResizeImage(img, size);
Rectangle cropRect = new Rectangle(80, 0, 240, 175);
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(img, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
}
//double scale = 203 / 96;
int width = (int)(target.Width);
int height = (int)(target.Height);
BinaryWriter brn = new BinaryWriter(s);
System.Drawing.Bitmap bmpScaled = new System.Drawing.Bitmap(target, width, height);
bmpScaled.Save(Npath);
byte[] imageArray = File.ReadAllBytes(Npath);
sw.WriteLine(imageArray.Length.ToString());
sw.Flush();
// Thread.Sleep(500);
brn.Write(imageArray);
//Thread.Sleep(500);
_detectLevel = Convert.ToInt32(_maxSignalLevel * _detectorLevelCoef);
_agcSignalLevel = agcMaxLevel;
}
This client the client segment that suppose to get the frames and display them
flag = true;
TcpClient client = new TcpClient(textBox1.Text, Convert.ToInt32(textBox2.Text));
Stream s = client.GetStream();
StreamReader sr = new StreamReader(s);
StreamWriter sw = new StreamWriter(s);
sw.Flush();
BinaryFormatter formatter = new BinaryFormatter();
string msg = sr.ReadLine();
MessageBox.Show("It's working !! the message is " + msg);
BinaryReader Brn = new BinaryReader(s);
Thread.Sleep(5000);
while (flag)
{
// Thread.Sleep(5000);
int size = Convert.ToInt32(sr.ReadLine());
label3.Text = "Size is " + size;
byte[] imagerray = Brn.ReadBytes(size);
MemoryStream ms = new MemoryStream(imagerray);
Thread.Sleep(10000);
Image image = Image.FromStream(ms);
ResizeImage(image, this.Size);
pictureBox1.Image = image;
Thread.Sleep(10);
}
}
i have no. of tiff files/images, now i want to convert on single PDF...
i wrote code for this one and it's work fine. but issue is, i am storing images on temp folder. and want to delete those files after PDF generate.and it's giving me error.
"Files is used by another process"
my code is:
string RootDirPath = ConfigurationManager.AppSettings["RootDirPath"].ToString();
string PDFDirPath = ConfigurationManager.AppSettings["PDFDirPath"].ToString();
string TmpFolderpath = System.DateTime.Now.ToString("d").Replace('/', '_');
// creation of the document with a certain size and certain margins
iTextSharp.text.Document document = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 0, 0, 0, 0);
// creation of the different writers
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(document, new System.IO.FileStream((PDFDirPath + PDFName + ".pdf"), System.IO.FileMode.Create));
// load the tiff image and count the total images
DirectoryInfo RootDir = new DirectoryInfo(RootDirPath + TmpFolderpath);
FileInfo[] files = RootDir.GetFiles();
System.Drawing.Bitmap bm = null;
document.Open();
for (int i = 0; i < files.Count(); i++)
{
bm = new System.Drawing.Bitmap(RootDirPath + TmpFolderpath + "/" + files[i].Name);
iTextSharp.text.pdf.PdfContentByte cb = writer.DirectContent;
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(bm, System.Drawing.Imaging.ImageFormat.Bmp);
img.ScalePercent(72f / img.DpiX * 100);
img.SetAbsolutePosition(0, 0);
cb.AddImage(img);
}
document.Close();
writer.Close();
bm.Dispose();
plz let me know, what's wrong i made....Thanks
This should be in the loop and not outside:
bm.Dispose();
Looks like I had same problem before. The reason is that when you are loading the images for creating pdf, you are not freeing the resources after use. Use following code to load use images.
private static BitmapImage GetBitmapImage(string imageFilePath)
{
BitmapImage bmpImage = new BitmapImage();
bmpImage.BeginInit();
Uri uri = new Uri(imageFilePath);
bmpImage.UriSource = uri;
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.EndInit();
return bmpImage;
}
This will automatically free the resources (images) you using after use..:)
I can save a screenshot on a timer, but how could I have it save as a new name and not overwrite every time?
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(#"c:tempscreenshot.bmp", ImageFormat.Bmp);
You can use the handy method: Path.GetRandomFileName()
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save("c://" + Path.GetRandomFileName() + ".bmp", ImageFormat.Bmp);
You just need to generate a unique name every time. There are several possibilities. One would be to add a datetime-string at the end:
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(#"c:tempscreenshot" + DateTime.Now.Ticks + ".bmp", ImageFormat.Bmp);
Use an integer (or some other numeric type) and increment it within your timer. Then call your save method with something like:
bitmap.Save(string.Format("c:tempscreenshot.{0}.bmp", counter), ImageFormat.Bmp);
or use a GUID:
bitmap.Save(string.Format("c:tempscreenshot.{0}.bmp", Guid.NewGuid().ToString("N")), ImageFormat.Bmp);
You can generate a new filename everytime based on the current time. Something like this:
string GenerateFilename() {
string file = DateTime.Now.ToString("yy.MM.dd HH.mm.ss") + ".bmp";
return #"C:\" + file;
}
The good thing about using this approach is that when you browse the folder where you're saving the files, they will be sorted.
and then use it in your existing code:
bitmap.Save(GenerateFilename(), ImageFormat.Bmp);
You can also prepend any text (like image- or something) to the filename.
Another option would be append an integer to the end of the filename, like some copy handling programs do.
There is already something for that: Path.GetTempFileName()
I'm currently developing an application to help scanning and showing images here at my work.
My application is build with multiple forms, the most important forms here is my mainForm to show statistics about the current scanning and a menustrip with different functinalities. I also have ImageViewerForm with a PictureBox which shows on the secondary monitor to view the current scanned image.
I'm using a Timer to poll the folder where the images are scanned to. When a new image has been scanned and the image is unlocked, i'll grap it into a FileStream and show it in the PictureBox, see below:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static Image ScaleImage(Image imgToResize, Size size)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap b = new Bitmap(destWidth, destHeight);
using (Graphics g = Graphics.FromImage(b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
}
return b;
}
This way the image shown in the PictureBox shouldn't be locked, but it is.
The problem is that the scanned image might have to be rescanned, and if I do that i'll get a sharing violation error when trying to overwrite the image file from the scanning software.
Anyone who's got an answer to what I can do?
SOLUTION
Thanks to #SPFiredrake I've got a solution to create a temp file to show in the PictureBox, leaving the original image unlocked.
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static string CreateTempFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName");
if (!File.Exists(fileName))
throw new ArgumentException("Specified file must exist!", "fileName");
string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName));
File.Copy(fileName, tempFile);
Log.New("Temp file created: " + tempFile);
return tempFile;
}
The problem here is that the image is being loaded from a FileStream, which is being locked by the PictureBox because it's holding a reference to the stream. What you should do is first load the picture into local memory (via a byte[] array) and then load the image from a MemoryStream instead. In your SetPicture method, you should try the following change and see if it works:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
byte[] imageBytes = File.ReadAllBytes(filename);
using(MemoryStream msImage = new MemoryStream(imageBytes))
{
currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height));
....
}
Edit: After our conversation in Chat, updating with the fix that you ended up using:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename));
File.Copy(filename, tempFile);
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
...
This way you are using the temp files to actually load the picture box, leaving the original file untouched (outside of the initial copy).
Once a Bitmap is loaded, you're not holding onto the filestream anymore, so everything should work. However, if you're talking about the instant when loading is taking place and the scanning tries to overwrite that file - always scan to a "temporary" or junk-named file (use a GUID as the name). Once scanning is complete, rename that file to JPG - which your display form will then pick up and display properly.
This way, re-scans will only involve trying to rename the temporary file multiple times with a "wait" to prevent that little area of overlap.
Your code works fine for me. I took an exact copy and called it repeatedly with the same image file.
SetPicture(#"c:\temp\logo.png", pictureBox1);
Something else is locking the file. Can you share your calling code?
I guess you're done with your work by now.
Still, I am posting in case someone else has the same issue.
I had the same problem : I load an image in a PictureBox control
picture.Image = new Bitmap(imagePath);
and when attempting to move it
File.Move(source, destination);
mscorlib throws an exception :
The process cannot access the file because it is beeing used by another process
I found a solution ( although in VB.Net rather than C# ) here PictureBox "locks" file, cannot move/delete
The author of the post clones the original image and loads the cloned image to the PictureBox control.
I slighty changed the code and came up with this :
private Bitmap CloneImage(string aImagePath) {
// create original image
Image originalImage = new Bitmap(aImagePath);
// create an empty clone of the same size of original
Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height);
// get the object representing clone's currently empty drawing surface
Graphics g = Graphics.FromImage(clone);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
// copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);
// free graphics and original image
g.Dispose();
originalImage.Dispose();
return clone;
}
So, we code will be :
picture.Image = (Image)CloneImage(imagePath);
Doing so, I had no more exception when moving files.
I think it is a good alternative way to do that, and you don't need a temporary file.
this is Jack code but in Visual Basic .NET and the casting goes inside the function
Private Function CloneImage(aImagePath As String) As Image
' create original image
Dim originalImage As Image = New Bitmap(aImagePath)
' create an empty clone of the same size of original
Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height)
' get the object representing clone's currently empty drawing surface
Dim g As Graphics = Graphics.FromImage(clone)
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed
' copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height)
' free graphics and original image
g.Dispose()
originalImage.Dispose()
Return CType(clone, Image)
End Function
So calling will be
picture.Image = CloneImage(imagePath)
Thank you Jack,
Response to this question from MS ...
for me is workikng ok ...
internal void UpdateLastImageDownloaded(string fullfilename)
{
this.BeginInvoke((MethodInvoker)delegate()
{
try
{
//pictureBoxImage.Image = Image.FromFile(fullfilename);
//Bitmap bmp = new Bitmap(fullfilename);
//pictureBoxImage.Image = bmp;
System.IO.FileStream fs;
// Specify a valid picture file path on your computer.
fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
pictureBoxImage.Image = System.Drawing.Image.FromStream(fs);
fs.Close();
}
catch (Exception exc)
{
Logging.Log.WriteException(exc);
}
});
}
While trying to figure out a solution for my C# Windows Form I came across a helpful article mentioning how to load a picture in picture box without "Locking" the original picture itself but an instance of it.
Hence if you try to delete, rename or do whatever you want to the original file you won't be notified by an error message mentioning "The file it's in use by another process" or anything else!
This is a reference to the article.
To Sum Up I believe that this solution is VERY useful when handling with SMALL amount of pictures, because applying this method in larger numbers could lead to lack of memory.