Below...

On Fri, 2004-09-03 at 20:10, Jim Fehlig wrote:
> I am having difficulty with a C# wrapper for some unmanaged C code
> that allocates a list.  Unmanaged code snippet: 
>    typedef struct CupsPrinterListStruct 
>    { 
>        char printerUri[1024]; 
>        char printerCupsUri[1024]; 
>        char printerName[1024]; 
>        char printerMakeAndModel[256]; 
>        struct CupsPrinterListStruct *nextPtr; 
>    }CupsPrinterList; 
>    int ListLocalPrinters(CupsPrinterList **printerList); 
>    int FreeLocalPrinterList(CupsPrinterList *listHead); 

As Marcus mentioned, changing your C# struct to a class allows your code
to work.  Whether that is correct is another matter.
 
Correctness depends upon the ListLocalPrinters() documentation.  What is
`printerList' supposed to be?  Just a pointer to a
CupsPrinterListStruct*?  Or should it be an array of pointers?  How many
elements will be addressed?

This is rather important because if ListLocalPrinters() uses
`printerList' as an array, then it will corrupt memory.  If
ListLocalPrinters() *allocates* an array, then a C# class will only pick
up the first element of the array, and leak the rest.  Further, you have
memory allocator issues: a C# "class" will cause the runtime to free the
returned pointer using CoTaskMemAlloc() (Windows) or g_free() (Linux). 
Given that presumably FreeLocalPrinterList() should be used, this could
also lead to memory corruption.

The "safe" thing to do is (1) continue to use a C# struct, and (2) use
"unsafe" code and casting to retrieve information.  The following
pseudo-code assumes that ListLocalPrinters() takes a pointer to a
CupsPrinterListStruct* (e.g. pointer to a pointer to a single element,
and ListLocalPrinters() will allocate that element).

        // Warning: untested code, but should get the point across

        // public user-visible class, NOT used for interop
        public class PrinterList {
                public string Uri, CupsUri, 
                        Name, MakeModel;
        }

        public unsafe class PrintLibWrapper {
                // Convert linked-list to an array
                [DllImport]
                private static extern int ListLocalPrinters (ref IntPtr p);
                [DllImport]
                private static extern int FreeLocalPrinterList(IntPtr p);

                // struct for interop purposes
                private unsafe struct CupsPrinterList {
                        // TODO: add element attributes
                        public string printerUri, printerCupsUri, printerName,
                                printerMakeModel;
                        public CupsPrinterList* nextElement;
                }

                // Convert linked list to array
                public unsafe PrinterList[] ListLocalPrinters ()
                {
                        IntPtr list = IntPtr.Zero;
                        
                        try {
                                // TODO: check return value
                                ListLocalPrinters (ref list);
                                
                                CupsPrinterList* printer = (PrinterList*) list;
                                
                                ArrayList printers = new ArrayList();
                                
                                // traverse linked list, adding each element to 
                                // printers
                                while (printer != null) {
                                        // copy data
                                        PrinterList pl = new PrinterList ();
                                        pl.Uri = printer->printerUri;
                                        pl.CupsUri = printer->printerCupsUri;
                                        pl.Name = printer->printerName;
                                        pl.MakeModel = printer->printerMakeModel;
                                
                                        printers.add (pl);
                                        printer = printer->nextElement;
                                }
                                
                                PrinterList[] rval = new PrinterList[printers.Count];
                                printers.CopyTo (rval);
                                
                                return rval;
                        }
                        finally {
                                if (list != IntPtr.Zero)
                                        FreeLocalPrintersList (list);
                        }
                        return new PrinterList[0];
                }
        }

The above proves I can't write short pseudo-code.  You also need to be
careful about exception-safety, as you don't want to leak unmanaged
memory.

For more information, see:

        http://www.jprl.com/~jon/interop.html

 - Jon


_______________________________________________
Mono-list maillist  -  [EMAIL PROTECTED]
http://lists.ximian.com/mailman/listinfo/mono-list

Reply via email to