2010-04-26 3 views
0

Я пишу обертку для библиотеки игр программирования «Аллегро» и ее менее устойчивой ветви 4.9. Теперь, я сделал что-то хорошее, за исключением того, когда дело доходит до обертывания структуры указателей функций. В принципе, я не могу изменить исходный код, несмотря на доступ к нему, потому что это потребует от меня какого-то разветвления. Мне нужно знать, как я могу каким-то образом передать структуру делегатов из управляемых в native без возникновения AccessViolationException, который произошел до сих пор.Можно ли передать структуру делегатов из управляемых в родной?

Теперь для кода. Вот определение Allegro структуры:

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_INTERFACE в ALLEGRO_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 и class es всякий раз), но я думаю, что метод-заглушка, созданная инфраструктурой, учитывает это и направляет вызовы действительным классам. Однако, кажется, очевидно, что я ошибаюсь.

Итак, можно ли каким-либо образом успешно обернуть эту структуру?

(И я извиняюсь за весь код! Надеюсь, что это не так уж много ...)

ответ

0

Из того, что я собрал, проблема была не с любым кодом, перечисленным выше. Проблема заключалась в вызывающем соглашении. По умолчанию stdcall, но это должно было быть cdecl. Удивительная демонстрация там. Уточняла проблему:

[UnmanagedFunctionPointer(AllegroFunctions.AllegroCallingConvention)] 

... префикс для всех делегатов. Сделано и сделано.

Смежные вопросы