[Mono-list] interop problem

Lloyd Dupont lloyd@galador.net
Wed, 7 Nov 2001 17:15:47 +0100


i have a strange interop bug.
just after coming back from a C# delegate into a C (interop function)

in fact the callback was passed to C previously.
i wonder if the delegate could be "managed" and see some of its intern data moving....

and how could i prevent this ?
or pass stable data to long living interop code ?

here my failing interop code: (i remove the println code...)
(one other bug is that i allways get tab.Lenght = 1 if i use SAFE_DELEGATE)

could anyone advice me, please ?

___ makefile ___
run: all
 tdelegate
    
all: tdelegate.dll tdelegate.exe

tdelegate.dll: tdelegate.c
 gcc -shared -o tdelegate.dll tdelegate.c
    
tdelegate.exe: tdelegate.cs
 csc /unsafe tdelegate.cs
___ tdelegate.c _
// csc /unsafe tdelegate.cs && tdelegate
//#define SAFE_DELEGATE
using System;
using System.Collections;
using System.IO;
using System.Runtime.InteropServices;

public class Test
{
 [DllImport("tdelegate")]
 static extern void DMreadAll(SDL_RWops ctxt);

    public static void Main(string[] arg)
    {
        Stream s = new FileStream("tdelegate.cs", FileMode.Open);
        SDL_RWops io = new SDL_RWops(s);
        DMreadAll(io);
    }
}

[StructLayout(LayoutKind.Sequential)]
public unsafe struct SDL_RWops : IDisposable
{
 IntPtr handle;
 
 // a table holding all currently opened & used stream
 static Hashtable streams = new Hashtable();
  
 [DllImport("tdelegate")]
 static extern IntPtr DMRWopsFromStream(s_seek s, s_read r, s_write w, s_close c);
 [DllImport("tdelegate")]
 static extern void DMRWopsFree(IntPtr io);
  
 public SDL_RWops(Stream s)
 {
  handle = DMRWopsFromStream(seeker, reader, writer, closer);
  if(handle == IntPtr.Zero)
   throw new SystemException("not enough memory");
  streams[handle] = s;
 }
 public void Dispose()
 {
  Stream s = (Stream) streams[handle];
  if(s != null) {
   DMRWopsFree(handle);
   streams.Remove(handle);
  }
  handle = IntPtr.Zero;
 }
  
 const int SEEK_SET = 0;
 const int SEEK_CUR = 1;
 const int SEEK_END = 2;

 delegate int s_seek(IntPtr ctxt, int offset, int whence);
 static int seek(IntPtr ctxt, int offset, int whence)
 {
  Stream s = (Stream) streams[ctxt];
  if(s == null)
   return -1;

  switch(whence) {
   case SEEK_SET:
   s.Seek(offset, SeekOrigin.Begin);
    break;
   case SEEK_CUR:
    s.Seek(offset, SeekOrigin.Current);
    break;
   case SEEK_END:
    s.Seek(offset, SeekOrigin.End);
    break;
  }
  return (int) s.Position;
 }

#if SAFE_DELEGATE
 delegate int s_read(IntPtr ctxt, int len, byte[] ptr);
 static int read(IntPtr ctxt, int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] tab)
 {
  Stream s = (Stream) streams[ctxt];
        if(s == null)
   return -1;

System.Console.WriteLine("tab.Length = " + tab.Length);
  return s.Read(tab, 0, tab.Length);
 }
    
 delegate int s_write(IntPtr ctxt, int len, byte[] ptr);
 static int write(IntPtr ctxt, int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] tab)
 {
  Stream s = (Stream) streams[ctxt];
  if(s == null)
   return -1;

System.Console.WriteLine("tab.Length = " + tab.Length);
  s.Write(tab, 0, len);
  return tab.Length;
 }
#else
 delegate int s_read(IntPtr ctxt, int len, byte *ptr);
 static int read(IntPtr ctxt, int len, byte *ptr)
 {
  Stream s = (Stream) streams[ctxt];
        if(s == null)
   return -1;

  byte[] buf = new byte[len];
  int ret = s.Read(buf, 0, len);
  fixed(byte * bufp = &buf[0])
   Pointer.memcpy(ptr, bufp, ret);
System.Console.WriteLine("ptr = "+((int) ptr));
  return ret;
 }
    
 delegate int s_write(IntPtr ctxt, int len, byte * ptr);
 static int write(IntPtr ctxt, int len, byte *ptr)
 {
  Stream s = (Stream) streams[ctxt];
  if(s == null)
   return -1;

  byte[] buf = new byte[len];
  fixed(byte * bufp = &buf[0])
   Pointer.memcpy(bufp, ptr, len);
  s.Write(buf, 0, len);        
  return len;
 }
#endif

 delegate int s_close(IntPtr ctxt);
 static int close(IntPtr ctxt)
 {
  Stream s = (Stream) streams[ctxt];
  if(s != null) {
   s.Close();
   DMRWopsFree(ctxt);
   streams.Remove(ctxt);
  }
  return 1;
 }
  
 static s_seek  seeker = new s_seek(seek);
 static s_read  reader = new s_read(read);
 static s_write writer = new s_write(write);
 static s_close closer = new s_close(close);
}

public unsafe class Pointer
{
 public static void memcpy(void * dst, void * src, int size)
 {
  byte * bdst = (byte *) dst;
  byte * bsrc = (byte *) src;
  for(int i=0; i<size; i++)
   * bdst ++ = * bsrc ++;
 }
}
___ tdelegate.cs _
// gcc -shared -o tdelegate.dll tdelegate.c
#include <stdlib.h>
#include <stdio.h>
#define DLLOBJ __declspec(dllexport)

typedef struct SDL_RWops {
 int (*seek)(struct SDL_RWops *context, int offset, int whence);
 int (*read)(struct SDL_RWops *context, int len, void *ptr);
 int (*write)(struct SDL_RWops *context, int len, void *ptr);
 int (*close)(struct SDL_RWops *context);
} SDL_RWops;

// ---------------------------------------

DLLOBJ SDL_RWops * DMRWopsFromStream(
 int (* fseek) (struct SDL_RWops *context, int offset, int whence), 
 int (* fread) (struct SDL_RWops *context, int len, void * ptr), 
 int (* fwrite)(struct SDL_RWops *context, int len, void * ptr), 
 int (* fclose)(struct SDL_RWops *context))
{
 struct SDL_RWops * io = (struct SDL_RWops *) malloc(sizeof(SDL_RWops));
 if(!io)
  return NULL;
 io->seek    = fseek;
 io->read    = fread;
 io->write   = fwrite;
 io->close   = fclose;
 return io;
}
DLLOBJ void DMRWopsFree(SDL_RWops * io)
{
 if(!io)
  return;
 free(io);
}

#define BUF_SIZE 50
DLLOBJ void DMreadAll(SDL_RWops * io)
{
 char buf[BUF_SIZE+1];
 int n;
 
    buf[BUF_SIZE] = 0;
 while((n = io->read(io, BUF_SIZE, buf)) == BUF_SIZE)
     printf("%d : %s", buf, buf);
    buf[n] = 0;
    printf("%s\n", buf);
 io->close(io);
}