2010-04-26 24 views
0

我正在爲遊戲編程庫「Allegro」及其不太穩定的4.9分支寫封裝。現在,除了包裝函數指針結構之外,我已經做得很好了。基本上,儘管可以訪問它,我仍然無法更改原始代碼,因爲這需要我以某種方式進行分支。我需要知道如何以某種方式將代理的結構從託管傳遞給本地,而不會導致迄今爲止發生的AccessViolationException是否可以將委託結構從託管傳遞給本地?

現在,代碼。下面是結構的快板定義:

typedef struct ALLEGRO_FILE_INTERFACE 
{ 
    AL_METHOD(ALLEGRO_FILE*, fi_fopen, (const char *path, const char *mode)); 
    AL_METHOD(void, fi_fclose, (ALLEGRO_FILE *handle)); 
    AL_METHOD(size_t, fi_fread, (ALLEGRO_FILE *f, void *ptr, size_t size)); 
    AL_METHOD(size_t, fi_fwrite, (ALLEGRO_FILE *f, const void *ptr, size_t size)); 
    AL_METHOD(bool, fi_fflush, (ALLEGRO_FILE *f)); 
    AL_METHOD(int64_t, fi_ftell, (ALLEGRO_FILE *f)); 
    AL_METHOD(bool, fi_fseek, (ALLEGRO_FILE *f, int64_t offset, int whence)); 
    AL_METHOD(bool, fi_feof, (ALLEGRO_FILE *f)); 
    AL_METHOD(bool, fi_ferror, (ALLEGRO_FILE *f)); 
    AL_METHOD(int,  fi_fungetc, (ALLEGRO_FILE *f, int c)); 
    AL_METHOD(off_t, fi_fsize, (ALLEGRO_FILE *f)); 
} ALLEGRO_FILE_INTERFACE; 

我在包裝簡單的嘗試:

public delegate IntPtr AllegroInternalOpenFileDelegate(string path, string mode); 
public delegate void AllegroInternalCloseFileDelegate(IntPtr file); 
public delegate int AllegroInternalReadFileDelegate(IntPtr file, IntPtr data, int size); 
public delegate int AllegroInternalWriteFileDelegate(IntPtr file, IntPtr data, int size); 
public delegate bool AllegroInternalFlushFileDelegate(IntPtr file); 
public delegate long AllegroInternalTellFileDelegate(IntPtr file); 
public delegate bool AllegroInternalSeekFileDelegate(IntPtr file, long offset, int where); 
public delegate bool AllegroInternalIsEndOfFileDelegate(IntPtr file); 
public delegate bool AllegroInternalIsErrorFileDelegate(IntPtr file); 
public delegate int AllegroInternalUngetCharFileDelegate(IntPtr file, int c); 
public delegate long AllegroInternalFileSizeDelegate(IntPtr file); 

[StructLayout(LayoutKind.Sequential, Pack = 0)] 
public struct AllegroInternalFileInterface 
{ 
    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalOpenFileDelegate fi_fopen; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalCloseFileDelegate fi_fclose; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalReadFileDelegate fi_fread; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalWriteFileDelegate fi_fwrite; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalFlushFileDelegate fi_fflush; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalTellFileDelegate fi_ftell; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalSeekFileDelegate fi_fseek; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalIsEndOfFileDelegate fi_feof; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalIsErrorFileDelegate fi_ferror; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalUngetCharFileDelegate fi_fungetc; 

    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public AllegroInternalFileSizeDelegate fi_fsize; 
} 

我有一個簡單的輔助包裝,變成一個ALLEGRO_FILE_INTERFACEALLEGRO_FILE,就像這樣:

#define ALLEGRO_NO_MAGIC_MAIN 
#include <allegro5/allegro5.h> 
#include <stdlib.h> 
#include <string.h> 
#include <assert.h> 

__declspec(dllexport) ALLEGRO_FILE * al_aux_create_file(ALLEGRO_FILE_INTERFACE * fi) 
{ 
    ALLEGRO_FILE * file; 

    assert(fi && "`fi' null"); 

    file = (ALLEGRO_FILE *)malloc(sizeof(ALLEGRO_FILE)); 

    if (!file) 
     return NULL; 

    file->vtable = (ALLEGRO_FILE_INTERFACE *)malloc(sizeof(ALLEGRO_FILE_INTERFACE)); 

    if (!(file->vtable)) 
    { 
     free(file); 

     return NULL; 
    } 

    memcpy(file->vtable, fi, sizeof(ALLEGRO_FILE_INTERFACE)); 

    return file; 
} 

__declspec(dllexport) void al_aux_destroy_file(ALLEGRO_FILE * f) 
{ 
    assert(f && "`f' null"); 
    assert(f->vtable && "`f->vtable' null"); 

    free(f->vtable); 
    free(f); 
} 

最後,我有一個接受Stream的類,並提供了與流進行交互的正確方法。只是爲了確保,那就是:

/// <summary> 
/// A semi-opaque data type that allows one to load fonts, etc from a stream. 
/// </summary> 
public class AllegroFile : AllegroResource, IDisposable 
{ 
    AllegroInternalFileInterface fileInterface; 
    Stream fileStream; 

    /// <summary> 
    /// Gets the file interface. 
    /// </summary> 
    internal AllegroInternalFileInterface FileInterface 
    { 
     get { return fileInterface; } 
    } 

    /// <summary> 
    /// Constructs an Allegro file from the stream provided. 
    /// </summary> 
    /// <param name="stream">The stream to use.</param> 
    public AllegroFile(Stream stream) 
    { 
     fileStream = stream; 

     fileInterface = new AllegroInternalFileInterface(); 
     fileInterface.fi_fopen = Open; 
     fileInterface.fi_fclose = Close; 
     fileInterface.fi_fread = Read; 
     fileInterface.fi_fwrite = Write; 
     fileInterface.fi_fflush = Flush; 
     fileInterface.fi_ftell = GetPosition; 
     fileInterface.fi_fseek = Seek; 
     fileInterface.fi_feof = GetIsEndOfFile; 
     fileInterface.fi_ferror = GetIsError; 
     fileInterface.fi_fungetc = UngetCharacter; 
     fileInterface.fi_fsize = GetLength; 

     Resource = AllegroFunctions.al_aux_create_file(ref fileInterface); 

     if (!IsValid) 
      throw new AllegroException("Unable to create file"); 
    } 

    /// <summary> 
    /// Disposes of all resources. 
    /// </summary> 
    ~AllegroFile() 
    { 
     Dispose(); 
    } 

    /// <summary> 
    /// Disposes of all resources used. 
    /// </summary> 
    public void Dispose() 
    { 
     if (IsValid) 
     { 
      Resource = IntPtr.Zero; // Should call AllegroFunctions.al_aux_destroy_file 

      fileStream.Dispose(); 
     } 
    } 

    IntPtr Open(string path, string mode) 
    { 
     return IntPtr.Zero; 
    } 

    void Close(IntPtr file) 
    { 
     fileStream.Close(); 
    } 

    int Read(IntPtr file, IntPtr data, int size) 
    { 
     byte[] d = new byte[size]; 
     int read = fileStream.Read(d, 0, size); 

     Marshal.Copy(d, 0, data, size); 

     return read; 
    } 

    int Write(IntPtr file, IntPtr data, int size) 
    { 
     byte[] d = new byte[size]; 

     Marshal.Copy(data, d, 0, size); 

     fileStream.Write(d, 0, size); 

     return size; 
    } 

    bool Flush(IntPtr file) 
    { 
     fileStream.Flush(); 

     return true; 
    } 

    long GetPosition(IntPtr file) 
    { 
     return fileStream.Position; 
    } 

    bool Seek(IntPtr file, long offset, int whence) 
    { 
     SeekOrigin origin = SeekOrigin.Begin; 

     if (whence == 1) 
      origin = SeekOrigin.Current; 
     else if (whence == 2) 
      origin = SeekOrigin.End; 

     fileStream.Seek(offset, origin); 

     return true; 
    } 

    bool GetIsEndOfFile(IntPtr file) 
    { 
     return fileStream.Position == fileStream.Length; 
    } 

    bool GetIsError(IntPtr file) 
    { 
     return false; 
    } 

    int UngetCharacter(IntPtr file, int character) 
    { 
     return -1; 
    } 

    long GetLength(IntPtr file) 
    { 
     return fileStream.Length; 
    } 
} 

現在,當我做這樣的事情:

AllegroFile file = new AllegroFile(new FileStream("Test.bmp", FileMode.Create, FileAccess.ReadWrite)); 
bitmap.SaveToFile(file, ".bmp"); 

...我得到一個AccessViolationException。我想我明白爲什麼(垃圾收集器可以隨時重新定位struct s和class es),​​但我認爲由框架創建的方法存根會考慮到這一點,並將調用路由到有效類。但是,似乎很明顯,我錯了。

所以基本上,有什麼辦法可以成功地包裝這種結構?

(我對所有的代碼對不起!希望這不是太多...)

回答

0

從我所收集的,這個問題是不是與之前列出的任何代碼。問題在於調用約定。默認值是stdcall,但應該是cdecl。驚人的表演塞在那裏。下面解決了這個問題:

[UnmanagedFunctionPointer(AllegroFunctions.AllegroCallingConvention)] 

...前面的所有代表。完成並完成。

相關問題