2010-04-26 3 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 내로 ALLEGRO_FILE_INTERFACE 변하므로 같은 간단한 보조 래퍼가
#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를 얻을 수 있습니다. 나는 왜 가비지 컬렉터가 언제든지 structclass es를 재배치 할 수 있다고 생각하지만, 프레임 워크에 의해 생성 된 메소드 스텁은이를 고려하여 유효한 클래스에 호출한다고 생각한다. 그러나 분명히 내가 틀렸어.

기본적으로 구조를 성공적으로 래핑 할 수있는 방법이 있습니까?

은 (그리고 난! 모든 코드에 대 한 미안 해요 ... 너무 많이하지 희망) 내가 수집 한 것과

답변

0

이 문제는 이전에 나열된 코드 아니었다. 문제는 호출 규칙에 있습니다. 기본값은 stdcall이지만, cdecl이어야합니다. 거기에 놀라운 쇼 스토퍼. 다음은이 문제를 해결했습니다 :

[UnmanagedFunctionPointer(AllegroFunctions.AllegroCallingConvention)] 

... 모든 대리인 앞에 붙습니다. 완료 및 완료.

관련 문제