8

After some more testing I've found that this problem may be due to the images somehow not being loaded in time to be cloned into bitmaps and displayed. Is this possible or no?

NOTE: Yes, there are other questions with this error in the title, but from a bit of a research it seems to be an ambiguous error with many possible causes. I haven't found any questions with the same scenario as mine.

I'm getting the following error.

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

It arises from this code. seemingly at random (i.e., sometimes it works and sometimes it doesn't. The more times it's run in a row without restarting VS and rebuilding the project, the more likely it is to fail):

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); } } 

Specifically, the fourth line:

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

The simplified objective of the code is to return a bitmap containing a sprite from a spriteset based on a spriteset index and a sprite index. This bitmap is displayed in a PictureBox until it is changed to a different image. I know for a fact that the logic works; that's not the issue here. The .png I'm using to test is 384*256.

All the parameters are set properly, all the referenced files are there, everything seems to be in order. Strangest thing of all is that sometimes it works, sometimes it doesn't. This has led me to believe that it may be a memory leak within System.Drawing itself but I can't seem to track it down.

EDIT: Updated the code and added the StackTrace. Still having the same issue despite disposing the Bitmaps when they are no longer used (see code below for example of how Bitmap is disposed).

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

StackTrace:

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() 
7
  • 2
    You need to call Dispose on the Bitmap B after you have cloned it. Bitmap's use unmanaged resources and that could be lead to memory pressure issues. I would wrap it with a using statement. Commented Dec 31, 2012 at 8:24
  • Thanks, that seems to have fixed it. Change that into an answer? Commented Dec 31, 2012 at 8:27
  • You should add a try/catch to the Main of your application and either dump the exception to the console or log it so that you can see the stack trace and know exactly where it came from. Commented Dec 31, 2012 at 8:28
  • Wait, never mind. Hasn't fixed it. A few more tests and back to square one. Commented Dec 31, 2012 at 8:29
  • You could also make this code a but more readable by adding an if statement at the top that would set both the prefix and the source rectangle (which must also be called Rectangle and conflict with the System.Drawing.Rectangle) this way you don't have the if inside the using and you can make the rectangle creation look like '(sourceRec.X, sourceRec.Y...') as opposed to the big nasty array indexing thing. Commented Dec 31, 2012 at 8:39

4 Answers 4

6

Wow, this code is leaking handles like hell. You need to dispose all types that implement IDisposable, which is quite a lot of types in the System.Drawing assembly (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); } } 

Also make sure that you have also disposed the bitmap returned by this function by wrapping the caller in a using statement:

using (Bitmap b = GetSprite(true, 0, 5)) { // do whatever you needed to do with the bitmap here } 
Sign up to request clarification or add additional context in comments.

9 Comments

I was trying to clean the code up a little bit as well as add the using. Oh well, will upvote yours :)
Modified the code as suggested on all calls to GetSprite() and still getting the exact same error at the exact same place. And now I'm only getting red exes in place of images.
You haven't explained how you are using those images. In the example I provided you cannot use them outside the using statement. I guess that you are attempting to use the result outside, that's why you are getting Red xses.
From my answer: The simplified objective of the code is to return a bitmap containing a sprite from a spriteset based on a spriteset index and a sprite index. Not from my answer: I then display that sprite (the result of GetSprite()) in a PictureBox. I want said PictureBox to change dynamically as I select sprite indices from a ListBox.
Alright, so you are displaying the result of this function inside a PictureBox. Then you should not dispose it immediately. You should dispose the old picturebox bitmap, just before assigning the new one: pictureBox1.Image.Dispose(); pictureBox.Image = GetSprite(...).
|
5

The leaking handles would have eventually led to memory issues but they weren't the problem in this case (thanks to those who pointed them out anyway, learnt something new).

The problem was that, due to the way I load the actual images into memory, the images weren't being given enough time to fully load in most of my tests. The ones that succeeded were the ones that allowed enough time for the images to load.

I got around this with a simple 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; } } 

This is all I need for this particular program.

Also, pstrjds, thanks for the cleanup ;) That messiness must have arisen during some refactor. Guess I just forgot about it.

Comments

1

I found also this trying to look for answers. be aware that it can also throw the exception when:

stream contains a PNG image file with a single dimension greater than 65,535 pixels.

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

Comments

1

For me, this was due to referencing a relative path, such as @"Resources\ImageName.png". In debug mode, I had no issues loading the image, but as OP suggests, this may actually be a resource load time issue. I was correctly using and disposing my objects, but this is not actually relevant to the issue. As soon as it was run inside of Release the error "Parameter is not valid" would rear its ugly head again.

I suspect that the compiler optimisations are such that in debug mode the image stream is indeed able to be expanded in time for the constructor to parse, but under release build the path still needs to undergo expansion at the time the cctor is ready to parse the path, which leads to the path is invalid error. Simply changing the path from a relative, to a fixed, or pre-expanded path resolves the issue.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.