2013-02-09 45 views
5

मैं स्कीरिम के लिए एक सेव गेम मैनेजर बना रहा हूं और एक समस्या में भाग गया हूं। जब मैं स्वयं से SaveGame ऑब्जेक्ट बनाता हूं, तो सहेजें का बिटमैप भाग सही तरीके से काम करता है। जब मैं उस विधि को लूप में कॉल करता हूं, हालांकि, बिटमैप एक गलत मूल्य पर ले जाता है, मुख्य रूप से वह एक जो अन्य सहेजने वाले गेम के समान होता है।बिटमैप में डेटा दायरे से बाहर क्यों जाता है?

टीएल; डीआर - एम्बेडेड तस्वीर को छोड़कर एक चरित्र सहेजने के लिए सही जानकारी प्रदर्शित करने वाले मेरे फॉर्म का सूची बॉक्स क्यों है? सही तस्वीर चुनने के बजाय, यह अंतिम संसाधित का चयन करता प्रतीत होता है। खुली फ़ाइल संवाद के माध्यम से चुनी गई प्रक्रिया से अलग प्रक्रिया कैसे होती है?


संपादित करें: अपडेट - मैं एक savegame वस्तु के साथ संग्रहीत बिटमैप में देखा और पाया कि scanDirectoryForSaves में Savegames के निर्माण के दौरान किसी भी तरह यह खिलवाड़ ऊपर है। क्या बिटमैप्स के साथ कोई ऑब्जेक्ट स्कोप समस्या है और एक बाइट पॉइंटर का उपयोग कर रहा है जिसे मैं नहीं जानता? , मैं एक विधि का उपयोग करें, से बाहर savegame वस्तुओं एक निश्चित निर्देशिका में सभी को बचाने फ़ाइलों को पढ़ने बनाने के लिए

public string Name { get; private set; } 
    public int SaveNumber { get; private set; } 
    public int PictureWidth { get; private set; } 
    public int PictureHeight { get; private set; } 
    public Bitmap Picture { get; private set; } 
    public DateTime SaveDate { get; private set; } 
    public string FileName { get; private set; } 

    public static SaveGame ReadSaveGame(string Filename) 
    { 

     SaveGame save = new SaveGame(); 
     save.FileName = Filename; 

     byte[] file = File.ReadAllBytes(Filename); 

     int headerWidth = BitConverter.ToInt32(file, 13); 
     save.SaveNumber = BitConverter.ToInt32(file, 21); 
     short nameWidth = BitConverter.ToInt16(file, 25); 

     save.Name = System.Text.Encoding.UTF8.GetString(file, 27, nameWidth); 
     save.PictureWidth = BitConverter.ToInt32(file, 13 + headerWidth - 4); 
     save.PictureHeight = BitConverter.ToInt32(file, 13 + headerWidth); 
     save.readPictureData(file, 13 + headerWidth + 4, save.PictureWidth, save.PictureHeight); 

     save.SaveDate = DateTime.FromFileTime((long)BitConverter.ToUInt64(file, 13 + headerWidth - 12)); 

     return save; 
    } 

    private void readPictureData(byte[] file, int startIndex, int width, int height) 
    { 
     IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(file, startIndex); 
     Picture = new Bitmap(width, height, 3 * width, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pointer); 
    } 

मेरी प्रपत्र पर:


यहाँ मेरी बचाने खेल वस्तु के स्थिर कारखाने के लिए कोड है उन्हें, और उन्हें चरित्र नाम के आधार पर एक शब्दकोश में संग्रहित करें।

private Dictionary<string, List<SaveGame>> scanDirectoryForSaves(string directory) 
    { 
     Dictionary<string, List<SaveGame>> saves = new Dictionary<string, List<SaveGame>>(); 
     DirectoryInfo info = new DirectoryInfo(directory); 

     foreach (FileInfo file in info.GetFiles()) 
     { 
      if (file.Name.ToLower().EndsWith(".ess") || file.Name.ToLower().EndsWith(".bak")) 
      { 
       string filepath = String.Format(@"{0}\{1}", directory, file.Name); 
       SaveGame save = SaveGame.ReadSaveGame(filepath); 

       if (!saves.ContainsKey(save.Name)) 
       { 
        saves.Add(save.Name, new List<SaveGame>()); 
       } 
       saves[save.Name].Add(save); 
      } 
     } 

     foreach (List<SaveGame> saveList in saves.Values) 
     { 
      saveList.Sort(); 
     } 

     return saves; 
    } 

मैं सूची बॉक्स में कुंजी जोड़ता हूं। जब सूची बॉक्स पर कोई नाम चुना जाता है, तो चरित्र के लिए नवीनतम सहेज फ़ॉर्म पर प्रदर्शित होता है। प्रत्येक चरित्र के लिए नाम, दिनांक और अन्य फ़ील्ड सही हैं, लेकिन बिटमैप एक निश्चित वर्णों की एक भिन्नता है जो गेम चित्र को सहेजता है।

मैं खुले फ़ाइल संवाद के साथ-साथ सूची बॉक्स से सहेजने के लिए दोनों में फ़ील्ड अपडेट करने के लिए एक ही विधि को कॉल कर रहा हूं।

private void updateLabels(SaveGame save) 
    { 
     nameLabel.Text = "Name: " + save.Name; 
     filenameLabel.Text = "File: " + save.FileName; 
     saveNumberLabel.Text = "Save Number: " + save.SaveNumber; 

     saveDateLabel.Text = "Save Date: " + save.SaveDate; 

     saveGamePictureBox.Image = save.Picture; 
     saveGamePictureBox.Image = ScaleImage(
      saveGamePictureBox.Image, saveGamePictureBox.Width, saveGamePictureBox.Height); 
     saveGamePictureBox.Invalidate(); 
    } 

उत्तर

4

जब आप constructor कि एक IntPtr लेता है का उपयोग कर एक Bitmap बनाने के लिए, IntPtr स्मृति का एक ब्लॉक है कि Bitmap वस्तु के जीवनकाल तक मान्य रहता है को इंगित करना चाहिए। आप यह सुनिश्चित करने के लिए ज़िम्मेदार हैं कि स्मृति का ब्लॉक स्थानांतरित नहीं हो जाता है या हटाया नहीं जाता है।

हालांकि, आपका कोड IntPtr गुजर रहा है जो file पर इंगित करता है, जो एक प्रबंधित बाइट सरणी है। चूंकि रिटर्न के बाद file का संदर्भ कुछ भी नहीं है, कचरा कलेक्टर स्मृति को पुनः प्राप्त करने और अगली फ़ाइल के लिए इसका पुन: उपयोग करने के लिए स्वतंत्र है। नतीजा: दूषित बिटमैप्स।

हालांकि आप GCHandle के साथ मेमोरी में सरणी को पिन करके इस समस्या को ठीक कर सकते हैं, तो शायद Bitmap अपनी याददाश्त को प्रबंधित करने के लिए यह संभवतः आसान और सुरक्षित है। सबसे पहले एक खाली Bitmap बनाते हैं, तो इसकी बिट्स सेट: बस सुनिश्चित करें कि मैं समझता हूँ, मेरे कोड में, फ़ाइल सामग्री दायरे से बाहर जाने के समारोह समाप्त होता है जब क्योंकि यह `बाइट [] फ़ाइल में सौंपा गया था बनाने के लिए

private void readPictureData(byte[] file, int startIndex, int width, int height) 
{ 
    Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb); 
    BitmapData data = bitmap.LockBits(
     new Rectangle(0, 0, width, height), 
     ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); 
    Marshal.Copy(file, startIndex, data.Scan0, width * height * 3); 
    bitmap.UnlockBits(data); 
    Picture = bitmap; 
} 
+0

= File.ReadAllBytes (फ़ाइल का नाम); '? उत्तर के लिए बहुत बहुत धन्यवाद! – Gilbrilthor

+0

हां; 'फाइल' बाइट्स की सरणी का एकमात्र संदर्भ है, इसलिए जब विधि वापस आती है, कचरा कलेक्टर को जब भी चाहें मेमोरी को पुनः प्राप्त करने की अनुमति दी जाती है। (हालांकि आपके 'IntPtr' सरणी को इंगित करता है, यह एक * प्रबंधित * संदर्भ नहीं है, इसलिए कचरा कलेक्टर इसे अनदेखा करता है।) लेकिन जीसी तुरंत नहीं चल सकता है, यही कारण है कि जब आप केवल एक फ़ाइल थी तो आप इससे दूर हो गए। –