저는 게임 프로그래밍 라이브러리 "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
를 얻을 수 있습니다. 나는 왜 가비지 컬렉터가 언제든지
struct
과
class
es를 재배치 할 수 있다고 생각하지만, 프레임 워크에 의해 생성 된 메소드 스텁은이를 고려하여 유효한 클래스에 호출한다고 생각한다. 그러나 분명히 내가 틀렸어.
기본적으로 구조를 성공적으로 래핑 할 수있는 방법이 있습니까?
은 (그리고 난! 모든 코드에 대 한 미안 해요 ... 너무 많이하지 희망) 내가 수집 한 것과