System.Drawing - параметр недействителен

После еще одного тестирования я обнаружил, что эта проблема может быть связана с тем, что изображения как-то не загружаются вовремя, чтобы их можно было клонировать в растровые изображения и отображать. Это возможно или нет?

ПРИМЕЧАНИЕ. Да, в названии есть и другие вопросы с этой ошибкой, но после небольшого исследования это кажется неоднозначной ошибкой со многими возможными причинами. Я не нашел никаких вопросов по тому же сценарию, что и у меня.

Я получаю следующую ошибку.

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"))

Упрощенная цель кода - вернуть растровое изображение, содержащее спрайт, из набора спрайтов на основе индекса набора спрайтов и индекса спрайта. Это растровое изображение отображается в 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()

3 ответа

Решение

Утечка ручек в конечном итоге привела бы к проблемам с памятью, но они не были проблемой в этом случае (спасибо тем, кто указал на них в любом случае, узнал что-то новое).

Проблема заключалась в том, что из-за способа загрузки реальных изображений в память изображениям не было предоставлено достаточно времени для полной загрузки в большинстве моих тестов. Успешными оказались те, которые давали достаточно времени для загрузки изображений.

Я справился с этим с помощью простой попытки / улова.

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, спасибо за очистку;) Этот беспорядок, возможно, возник во время некоторого рефакторинга. Думаю, я просто забыл об этом.

Ничего себе, этот код протекает как чертовы ручки. Вам необходимо избавиться от всех типов, которые реализуют 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
}

Я также нашел это, пытаясь искать ответы. имейте в виду, что он также может выдать исключение, когда:

поток содержит файл изображения PNG с одним размером, превышающим 65 535 пикселей.

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

Для меня это было связано с ссылкой на относительный путь, например @"Resources\ImageName.png". В режиме отладки у меня не было проблем с загрузкой изображения, но, как предполагает OP, на самом деле это может быть проблема времени загрузки ресурса. Я был прав usingи избавляюсь от моих объектов, но на самом деле это не имеет отношения к проблеме. Как только он будет запущен внутри Release, ошибка "Parameter is not valid" снова поднимет свою уродливую голову.

Я подозреваю, что оптимизация компилятора такова, что в режиме отладки поток изображений действительно может быть расширен во времени для анализа конструктором, но при сборке выпуска путь все еще должен подвергаться расширению в то время, когда cctor готов проанализировать путь, который ведет к недопустимой ошибке пути. Простое изменение пути с относительного на фиксированный или предварительно расширенный путь решает проблему.

Другие вопросы по тегам