我目前正在开发一个应用程序来帮助我在工作中扫描和显示图像。
我的应用程序是使用多种形式构建的,这里最重要的形式是我的mainForm
,用于显示当前扫描的统计信息和具有不同功能的menustrip。我还有ImageViewerForm
PictureBox
,显示在辅助显示器上以查看当前扫描图像。
我使用Timer
轮询扫描图像的文件夹。扫描新图片并解锁图片后,我会将其抓取到FileStream
并在PictureBox
中显示,见下文:
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;
}
这样,PictureBox
中显示的图片不会被锁定,但确实如此。
问题是扫描的图像可能必须重新扫描,如果我这样做,我会在尝试从扫描软件覆盖图像文件时收到共享冲突错误。
任何人都能得到我能做的答案吗?
解
感谢@SPFiredrake我有一个解决方案来创建一个临时文件,以便在PictureBox中显示,让原始图像保持解锁状态。
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;
}
答案 0 :(得分:3)
这里的问题是图像是从FileStream加载的,它正被PictureBox锁定,因为它持有对流的引用。你应该做的是首先将图片加载到本地内存(通过byte []数组),然后从MemoryStream加载图像。在SetPicture
方法中,您应该尝试以下更改,看看它是否有效:
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));
....
}
编辑:在聊天中进行对话后,使用您最终使用的修补程序进行更新:
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))
{
...
这样您就可以使用临时文件来实际加载图片框,保持原始文件不变(在初始副本之外)。
答案 1 :(得分:1)
一旦加载了Bitmap,你就不再保留文件流了,所以一切都应该有效。但是,如果您正在谈论正在进行加载并且扫描尝试覆盖该文件的瞬间 - 请始终扫描到“临时”或垃圾命名文件(使用GUID作为名称)。扫描完成后,将该文件重命名为JPG - 然后您的显示形式将被正确显示并显示。
这样,重新扫描只会涉及尝试多次重命名临时文件并使用“等待”来防止这个小区域重叠。
答案 2 :(得分:1)
您的代码适用于我。我拿了一个精确的副本并用相同的图像文件重复调用它。
SetPicture(@"c:\temp\logo.png", pictureBox1);
其他东西正在锁定文件。你能分享你的主叫代码吗?
答案 3 :(得分:0)
我猜你现在已经完成了你的工作 不过,我发帖是为了防止其他人有同样的问题 我遇到了同样的问题:我在PictureBox控件中加载图像
picture.Image = new Bitmap(imagePath);
并且在尝试移动时
File.Move(source, destination);
mscorlib抛出异常:
该进程无法访问该文件,因为它正在被其他进程使用
我在PictureBox "locks" file, cannot move/delete
找到了一个解决方案(虽然在VB.Net而不是C#中)帖子的作者克隆原始图像并将克隆的图像加载到PictureBox控件 我稍微改变了代码并想出了这个:
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;
}
所以,我们的代码将是:
picture.Image = (Image)CloneImage(imagePath);
这样做,移动文件时我没有例外 我认为这是一种很好的替代方法,而且你不需要临时文件。
答案 4 :(得分:0)
这是Jack代码,但在Visual Basic .NET中,转换进入函数
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
所以打电话
picture.Image = CloneImage(imagePath)
谢谢杰克,
答案 5 :(得分:0)
Response to this question from MS ...
对我而言,工作正常......
<script type="text/javascript">
var appendAnalytics = function (i, s, o, g, r, cb) {
i["GoogleAnalyticsObject"] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)}, i[r].l = 1 * new Date();
var a = s.createElement(o);
var m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
a.onload = cb;
m.parentNode.insertBefore(a, m)};
if (window.ga === undefined) {
appendAnalytics(window, document, "script", "//www.google-analytics.com/analytics.js", "ga", sendPageView);
} else {
sendPageView();
}
function sendPageView() {
window.ga("create", "UA-XXXXXX”, "auto", {
name: “YourTrackerName”
});
window.ga("YourTrackerName.send", "pageview");
}
</script>
答案 6 :(得分:0)
在尝试找出我的C#Windows窗体的解决方案时,我遇到了一篇有用的文章,提到如何在没有&#34;锁定&#34;的情况下加载图片框中的图片。原始图片本身,但它的一个实例。 因此,如果您尝试删除,重命名或对原始文件执行任何操作,则不会通过提及的错误消息通知&#34;其他进程正在使用的文件&#34;或其他任何东西!
这是文章的reference。
总结我认为在处理 SMALL 数量的图片时,此解决方案非常非常有用,因为大量应用此方法可能会导致内存不足。