【问题标题】:System.Drawing - Parameter is not validSystem.Drawing - 参数无效
【发布时间】:2012-12-15 11:22:42
【问题描述】:

经过更多测试,我发现这个问题可能是由于图像没有及时加载而无法克隆到位图并显示。这可能吗?

注意:是的,标题中还有其他与此错误有关的问题,但从一些研究来看,这似乎是一个含糊不清的错误,有很多可能的原因。我没有发现任何与我的情况相同的问题。

我收到以下错误。

System.ArgumentException was unhandled
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing

它源于此代码。似乎是随机的(即有时有效,有时无效。连续运行的次数越多而不重新启动 VS 并重建项目,失败的可能性就越大):

private Bitmap GetSprite(bool anim, int tsIndex, int tileIdx) {
    System.Drawing.Rectangle cloneRect;
    string prefix = (anim) ? "A" : "S";
    using (Bitmap b = new Bitmap(prefix + tsIndex.ToString() + ".png")) {
        if (anim) {
            cloneRect = new System.Drawing.Rectangle(BaseObjects.A_AnimSpriteSets[tsIndex].StaticRecs[tileIdx].X, BaseObjects.A_AnimSpriteSets[tsIndex].StaticRecs[tileIdx].Y, BaseObjects.A_AnimSpriteSets[tsIndex].RecWidth, BaseObjects.A_AnimSpriteSets[tsIndex].RecHeight);
        } else {
            cloneRect = new System.Drawing.Rectangle(BaseObjects.A_StaticSpriteSets[tsIndex].StaticRecs[tileIdx].X, BaseObjects.A_StaticSpriteSets[tsIndex].StaticRecs[tileIdx].Y, BaseObjects.A_StaticSpriteSets[tsIndex].RecWidth, BaseObjects.A_StaticSpriteSets[tsIndex].RecHeight);
        }
        return b.Clone(cloneRect, b.PixelFormat);
    }
}

具体来说,第四行:

using (Bitmap b = new Bitmap(prefix + tsIndex.ToString() + ".png"))

代码的简化目标是根据 spriteset 索引和 sprite 索引返回包含 spriteset 中的 sprite 的位图。此位图显示在 PictureBox 中,直到它更改为不同的图像。我知道逻辑有效。这不是这里的问题。我用来测试的 .png 是 384*256。

所有参数设置正确,所有引用的文件都在那里,一切似乎都井井有条。最奇怪的是,有时它有效,有时则无效。这让我相信它可能是 System.Drawing 本身内部的内存泄漏,但我似乎无法追踪到它。

编辑:更新了代码并添加了 StackTrace。尽管在不再使用位图时处置了位图,但仍然存在相同的问题(请参阅下面的代码以了解位图的处置方式)。

if (Sprite.Image != null) { Sprite.Image.Dispose(); }
    Sprite.Image = GetSprite(true, tsIdx, tileIdx);

堆栈跟踪:

System.ArgumentException was unhandled
  HResult=-2147024809
  Message=Parameter is not valid.
  Source=System.Drawing
  StackTrace:
       at System.Drawing.Bitmap..ctor(String filename)
       at CreationTool.Main.GetSprite(Boolean anim, Int32 tsIndex, Int32 tileIdx) in F:\~\~\CreationTool\Main.cs:line 420
       at CreationTool.Main.Input_EnemySprite_SelectedIndexChanged(Object sender, EventArgs e) in F:\~\~\CreationTool\Main.cs:line 107
       at System.Windows.Forms.ComboBox.OnSelectedIndexChanged(EventArgs e)
       at System.Windows.Forms.ComboBox.set_SelectedIndex(Int32 value)
       at CreationTool.States.State_Enemy.populateForm() in F:\~\~\CreationTool\States\State_Enemy.cs:line 28
       at CreationTool.States.State_Enemy.Load(String name) in F:\~\~\CreationTool\States\State_Enemy.cs:line 22
       at CreationTool.Main.btnLoad_Click(Object sender, EventArgs e) in F:\~\~\CreationTool\Main.cs:line 174
       at System.Windows.Forms.Control.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
       at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ButtonBase.WndProc(Message& m)
       at System.Windows.Forms.Button.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at CreationTool.Program.Main() in F:\~\~\CreationTool\Program.cs:line 15
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

  • 克隆后需要在Bitmap B上调用Dispose。位图使用非托管资源,这可能会导致内存压力问题。我会用 using 语句包装它。
  • 谢谢,这似乎已经解决了。把它改成答案?
  • 您应该在应用程序的Main 中添加一个try/catch,然后将异常转储到控制台或将其记录下来,以便您可以查看堆栈跟踪并确切知道它来自何处。
  • 等等,没关系。还没修好再进行一些测试,然后回到正题。
  • 您还可以通过在顶部添加一个 if 语句来设置前缀和源矩形(也必须称为 Rectangle 并与 System.Drawing 冲突),从而使此代码更具可读性.Rectangle) 这样你就没有 if 里面的 using 并且你可以使矩形创建看起来像 '(sourceRec.X, sourceRec.Y...') 而不是大的讨厌的数组索引。跨度>

标签: c# .net-4.0 bitmap system.drawing


【解决方案1】:

哇,这段代码简直是在泄露句柄。您需要处理所有实现IDisposable 的类型,这在System.Drawing 程序集(GDI+)中是相当多的类型:

private Bitmap GetSprite(bool anim, int tsIndex, int tileIdx)
{
    Rectangle cloneRect;
    string prefix = (anim) ? "A" : "S";
    using (Bitmap b = new Bitmap(prefix + tsIndex.ToString() + ".png"))
    {
        if (anim)
        {
            cloneRect = new Rectangle(BaseObjects.A_AnimSpriteSets[tsIndex].StaticRecs[tileIdx].X, BaseObjects.A_AnimSpriteSets[tsIndex].StaticRecs[tileIdx].Y, BaseObjects.A_AnimSpriteSets[tsIndex].RecWidth, BaseObjects.A_AnimSpriteSets[tsIndex].RecHeight);
        }
        else
        {
            cloneRect = new Rectangle(BaseObjects.A_StaticSpriteSets[tsIndex].StaticRecs[tileIdx].X, BaseObjects.A_StaticSpriteSets[tsIndex].StaticRecs[tileIdx].Y, BaseObjects.A_StaticSpriteSets[tsIndex].RecWidth, BaseObjects.A_StaticSpriteSets[tsIndex].RecHeight);
        }

        return b.Clone(cloneRect, b.PixelFormat);
    }
}

另外,请确保您还处理了此函数返回的位图,方法是将调用者包装在 using 语句中:

using (Bitmap b = GetSprite(true, 0, 5))
{
    // do whatever you needed to do with the bitmap here
}

【讨论】:

  • 我试图清理一下代码并添加使用。哦,好吧,会支持你的:)
  • 按照所有对 GetSprite() 调用的建议修改了代码,但仍然在完全相同的位置出现完全相同的错误。现在我只用红色的 exe 代替图像。
  • 你还没有解释你是如何使用这些图像的。在我提供的示例中,您不能在 using 语句之外使用它们。我猜你正试图在外面使用结果,这就是你得到 Red xses 的原因。
  • 来自我的回答:代码的简化目标是返回一个位图,其中包含基于 spriteset 索引和 sprite 索引的 spriteset 中的 sprite。不是来自我的回答:然后我在 PictureBox 中显示该精灵(GetSprite() 的结果)。当我从 ListBox 中选择精灵索引时,我希望所说的 PictureBox 动态变化。
  • 好的,所以您将在 PictureBox 中显示此函数的结果。那么你不应该立即处理它。您应该在分配新的图片框位图之前处理旧的图片框位图:pictureBox1.Image.Dispose(); pictureBox.Image = GetSprite(...)
【解决方案2】:

泄漏的句柄最终会导致内存问题,但在这种情况下它们不是问题(感谢那些指出它们的人,学到了新东西)。

问题在于,由于我将实际图像加载到内存中的方式,在我的大多数测试中,图像没有得到足够的时间来完全加载。成功的是那些允许有足够时间加载图像的那些。

我通过简单的 try/catch 解决了这个问题。

private Bitmap GetSprite(bool anim, int tsIndex, int tileIdx) {
    string prefix;
    System.Drawing.Rectangle cloneRect;
    SpriteSet set;
    if (anim) {
        prefix = "A";
        set = BaseObjects.A_AnimSpriteSets[tsIndex];
    } else {
        prefix = "S";
        set = BaseObjects.A_StaticSpriteSets[tsIndex];
    }
    cloneRect = new System.Drawing.Rectangle(set.StaticRecs[tileIdx].X, set.StaticRecs[tileIdx].Y, set.RecWidth, set.RecHeight);
    try {
        using (Bitmap b = new Bitmap(prefix + tsIndex.ToString() + ".png")) {
            return b.Clone(cloneRect, b.PixelFormat);
        }
    } catch (Exception ex) {
        MessageBox.Show("Error: " + ex.Message + "\n\nCause: " + "SpriteSet not yet loaded.");
        return null;
    }
}

这就是这个特定程序所需要的全部内容。

另外,pstrjds,感谢您的清理;)这种混乱一定是在某些重构过程中出现的。我想我只是忘了它。

【讨论】:

    【解决方案3】:

    我也发现这试图寻找答案。请注意,它也可能在以下情况下引发异常:

    流包含单个尺寸大于 65,535 像素的 PNG 图像文件。

    http://msdn.microsoft.com/en-us/library/z7ha67kw.aspx

    【讨论】:

      【解决方案4】:

      对我来说,这是由于引用了相对路径,例如 @"Resources\ImageName.png"。在 debug 模式下,我加载图像没有问题,但正如 OP 所暗示的,这实际上可能是资源加载时间问题。我是正确的using 并处理了我的对象,但这实际上与问题无关。一旦在 Release 内部运行,错误“参数无效”就会再次抬头。

      我怀疑编译器优化是这样的,在调试模式下,图像流确实能够及时扩展以供构造函数解析,但在发布构建下,路径仍然需要在 cctor 准备好时进行扩展解析路径,从而导致路径无效错误。只需将路径从相对路径更改为固定路径或预扩展路径即可解决问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-12
        • 2023-03-20
        • 2016-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多