2013-02-09 18 views
5

Skyrim için bir kayıt oyun yöneticisi oluşturuyorum ve bir sorunla karşılaştım. Bir SaveGame nesnesini kendim oluşturduğumda, kaydetmenin bitmap bölümü düzgün çalışır. Ancak, bu yöntemi bir döngüde çağırdığımda, bitmap, genellikle başka bir kayıt oyununa benzeyen hatalı bir değer alır.Neden bitmap'teki veriler kapsam dışında kalıyor?

TL; DR - Neden resmin dışında bir kurtarış karakter için doğru bilgileri gösteren benim formun listbox gömülü olduğu olduğunu? Doğru resmi seçmek yerine, en son işlenmiş olanı seçer. İşlem açık bir dosya diyaloğu ile seçildiğinde nasıl farklıdır?


Düzenleme: Güncelleme - Ben her SaveGame olayına nesne ile saklanan bitmap'lerin içine baktı ve scanDirectoryForSaves içinde savegames oluşturulması sırasında her nasılsa o berbat ediyor bulundu. Bitmap'lerle bir nesne kapsamı sorunu var mı ve farkında olmadığım bir bayt işaretçisi mi kullanıyorsunuz? Ben dışarı SaveGame olayına nesneleri belli bir dizindeki tüm kurtarmak dosyaları okumak oluşturmak için bir yöntemi kullanmak Benim form üzerinde

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

: Burada


benim kurtarmak oyunu nesnenin statik fabrikanın kodudur Onları ve karakter adına dayalı bir sözlükte saklayın.

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

Anahtarları bir liste kutusuna ekliyorum. Liste kutusunda bir isim seçildiğinde, karakter için en son kayıt formda görüntülenir. İsim, tarih ve diğer alanlar her karakter için doğrudur, ancak bitmap, oyun resmini saklayan belirli bir karakterin varyasyondur.

Ben açık bir dosya iletişim yanı sıra liste tasarruf seçerek hem form alanları güncelleştirmek için aynı yöntemi arıyorum. Bir IntPtr alır constructor kullanarak Bitmap oluşturduğunuzda

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

cevap

4

, IntPtrBitmap nesnenin kullanım ömrü boyunca geçerlidir kalır bellek bloğu göstermelidir. Hafıza bloğunun taşınmamasını veya tahsis edilmemesini sağlamaktan siz sorumlusunuz. Ancak, kodunuz yönetilen bir bayt dizisi olan file'a işaret eden IntPtr kodunu geçiriyor. hiçbir şey fileReadSaveGame sonra döner başvuran Çünkü, çöp toplayıcı bellek geri ve bir sonraki dosyaya için yeniden serbesttir. Sonuç: bozuk bitmapler.

Dizideki diziyi GCHandle ile sabitleyerek bu sorunu giderebilirsiniz, ancak Bitmap'un kendi belleğini yönetmesine izin vermek daha kolay ve güvenlidir. İlk boş Bitmap oluşturmak, ardından da bit ayarlayın: Sadece byte [] dosya `de atandı çünkü fonksiyonu sona erdiğinde benim kodunda, dosya içeriği kapsam dışına çıkar, anlıyorum emin olmak için

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 (Dosya); '? Cevabınız için çok teşekkürler! – Gilbrilthor

+0

Evet; 'dosya', bayt dizisine yapılan tek referanstır, bu nedenle yöntem döndüğünde, çöp toplayıcısının istediği zaman belleği geri almasına izin verilir. sadece bir dosya varken ondan kurtardı, bu yüzden de (dizi sizin 'IntPtr' noktaları olsa da, çöp toplayıcı bunu görmezden yüzden., * referans yönetilen bir * değil) Ama GC hemen yayınlanmayabilir. –

İlgili konular