I'm writing a wrapper in C# for an unmanaged C DLL. In the DLL I have the following method which returns pointer struct (struct code near end of post):
struct zint_symbol *ZBarcode_Create()
{
struct zint_symbol *symbol = (struct zint_symbol*)calloc(1, sizeof(struct zint_symbol));
if (!symbol) return NULL;
symbol->symbology = BARCODE_CODE128;
strcpy(symbol->fgcolour, "000000");
strcpy(symbol->bgcolour, "ffffff");
strcpy(symbol->outfile, "out.png");
symbol->scale = 1.0;
symbol->option_1 = -1;
symbol->option_3 = 928; // PDF_MAX
symbol->show_hrt = 1; // Show human readable text
return symbol;
}
The extern methods I am using are:
extern struct zint_symbol* ZBarcode_Create(void);
extern int ZBarcode_Encode_and_Buffer(struct zint_symbol *symbol, unsigned char *input, int length, int rotate_angle);
extern int ZBarcode_Encode_and_Print(struct zint_symbol *symbol, unsigned char *input, int length, int rotate_angle);
ZBarcode_Encode_and_Buffer
renders an image and saves the bitmap in a variable in my struct called bitmap
.
ZBarcode_Encode_and_Print
renders an image and saves it to the file system. They both return 0 when they are successful, and a number between 1-8 when they fail. Each time for me, they return 0.
My C# looks like the following:
[DllImport("zint.dll", EntryPoint = "ZBarcode_Create", CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr Create();
[DllImport("zint.dll", EntryPoint = "ZBarcode_Encode_and_Buffer", CallingConvention = CallingConvention.Cdecl)]
public extern static int EncodeAndBuffer(
ref zint_symbol symbol,
string input,
int length,
int rotate_angle);
[DllImport("zint.dll", EntryPoint = "ZBarcode_Encode_and_Print", CallingConvention = CallingConvention.Cdecl)]
public extern static int EncodeAndPrint(
ref zint_symbol symbol,
string input,
int length,
int rotate_angle);
public static void Render()
{
// call DLL function to generate pointer to initialized struct
zint_symbol s = (zint_symbol)
// generate managed counterpart of struct
Marshal.PtrToStructure(ZintLib.Create(), typeof(zint_symbol));
// change some settings
s.symbology = 71;
s.outfile = "baro.png";
s.text = "12345";
String str = "12345";
// DLL function call to generate output file using changed settings -- DOES NOT WORK --
System.Console.WriteLine(ZintLib.EncodeAndBuffer(ref s, str, str.Length, 0));
// DLL function call to generate output file using changed settings -- WORKS --
System.Console.WriteLine(ZintLib.EncodeAndPrint(ref s, str, str.Length, 0));
// notice that these variables are set in ZBarcode_Create()?
Console.WriteLine("bgcolor=" + s.bgcolour + ", fgcolor=" + s.fgcolour + ", outfile=" + s.outfile);
// these variables maintain the same values as when they were written to in ZBarcode_Create().
if (s.errtxt != null)
Console.WriteLine("Error: " + s.errtxt);
else
Console.WriteLine("Image size rendered: " + s.bitmap_width + " x " + s.bitmap_height);
}
All of the variables in s
remain unchanged, even though the DLL is supposed to change some of them such as bitmap
, bitmap_width
, bitmap_height
, etc.
I suspect that there are two copies of zint_symbol
in memory; one that my C# code has (created by ZintLib.Create()
) and
another one that the DLL is writing to. I am certain that the library works correctly, however.
The C struct:
struct zint_symbol {
int symbology;
int height;
int whitespace_width;
int border_width;
int output_options;
#define ZINT_COLOUR_SIZE 10
char fgcolour[ZINT_COLOUR_SIZE];
char bgcolour[ZINT_COLOUR_SIZE];
char outfile[FILENAME_MAX];
float scale;
int option_1;
int option_2;
int option_3;
int show_hrt;
int input_mode;
#define ZINT_TEXT_SIZE 128
unsigned char text[ZINT_TEXT_SIZE];
int rows;
int width;
#define ZINT_PRIMARY_SIZE 128
char primary[ZINT_PRIMARY_SIZE];
#define ZINT_ROWS_MAX 178
#define ZINT_COLS_MAX 178
unsigned char encoded_data[ZINT_ROWS_MAX][ZINT_COLS_MAX];
int row_height[ZINT_ROWS_MAX]; /* Largest symbol is 177x177 QR Code */
#define ZINT_ERR_SIZE 100
char errtxt[ZINT_ERR_SIZE];
char *bitmap;
int bitmap_width;
int bitmap_height;
struct zint_render *rendered;
};
The C# struct:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct zint_symbol
{
public int symbology;
public int height;
public int whitespace_width;
public int border_width;
public int output_options;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public String fgcolour;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public String bgcolour;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string errtxt;
public float scale;
public int option_1;
public int option_2;
public int option_3;
public int show_hrt;
public int input_mode;
public int rows;
public int width;
public int bitmap_width;
public int bitmap_height;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string text;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string primary;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 31684)]
public byte[] encoded_data;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 178)]
public int[] row_height;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string outfile;
public IntPtr bitmap;
public IntPtr rendered;
}
I have written a small Windows forms example (the DLLs are in there and also available here. The library documentation (page 18/61 explains the struct variables) is available here and the entire C source code is available here (zint-2.4.3.tar.gz). Files of particular interest are zint.h, library.c and datamatrix.c. The C source code is the same version of the DLLs.
Edit
The struct layouts appear to be mismatched. sizeof(zint_symbol) in C != sizeof(zint_symbol) in C#.