通过引用传递给非托管C DLL函数后,struct保持不变

我正在为C#编写一个非托管C DLL包装器。 在DLL中我有以下方法返回指针结构(接近结尾的结构代码):

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; } 

我使用的外部方法是:

 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渲染图像并将位图保存在我的结构中名为bitmap的变量中。 ZBarcode_Encode_and_Print呈现图像并将其保存到文件系统。 它们在成功时都返回0 ,在失败时返回1-8之间的数字。 每次对我来说,他们都会返回0。

我的C#如下所示:

 [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); } 

s所有变量保持不变 ,即使DLL应该更改其中的一些bitmap_width ,如bitmapbitmap_widthbitmap_height等。

我怀疑内存中有两个zint_symbol副本; 一个我的C#代码(由ZintLib.Create()创建)和另一个DLL写入。 但我确信该库正常工作。

C结构:

 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; }; 

C#结构:

 [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; } 

我已经编写了一个小的Windows窗体示例 ( 这里有DLL,也可以在这里找到 。库文档(第18/61页解释了struct变量) 可以在这里找到 ,整个C源代码可以在这里找到(zint-2.4.3) .tar.gz) 。特别感兴趣的文件是zint.h,library.c和datamatrix.c.C源代码是相同版本的DLL。

编辑

结构布局似乎不匹配。 C中的sizeof(zint_symbol)!= C#中的sizeof(zint_symbol)。

你有一个奇怪的错误:

 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 25454)] public byte[] encoded_data; 

.h文件中的内容是:

 #define ZINT_ROWS_MAX 178 #define ZINT_COLS_MAX 178 uint8_t encoded_data[ZINT_ROWS_MAX][ZINT_COLS_MAX]; 

只需以同样的方式声明它:

 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 178 * 178)] public byte[] encoded_data; 

还有一个错误,FILENAME_MAX是260:

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string outfile; 

现在你应该得到一个正确的匹配:

 static void Main(string[] args) { var len = Marshal.SizeOf(typeof(zint_symbol)); // 33100 var offs = Marshal.OffsetOf(typeof(zint_symbol), "errtxt"); // 32984 } 

并在C测试程序中:

 #include  //... int main() { size_t len = sizeof(zint_symbol); // 33100 size_t offs = offsetof(zint_symbol, errtxt); // 32984 return 0; } 

查看P / Invoke声明中的差异,我们可以看到成功的接口使用[In,Out]属性。 你也应该将它们添加到其他function中。

请参阅此处: 编组数组的P / Invoke [In,Out]属性是否可选?

zint_symbol结构在您链接到的PDF中的定义不同。 看起来您的结构与dll版本使用的结构之间存在不兼容性。

例如,在PDF中, scale显示在option_3之后,而您在option_1之前option_1 。 PDF中的最后一个元素是bitmap_height ,而在此之后你的继续。 可能还有其他差异,这些正是我注意到的。

虽然您无法访问dll源,但在这种情况下,您可以通过自己创建一个简单的dll来测试您的API。 创建一个简单的WinForms解决方案,它只有一个带有一个按钮的表单,并在解决方案中添加CPP Win32项目(项目类型:Win32项目;应用程序类型:DLL)。 为简单起见,您只需在现有的dllmain.cpp中添加struct和stub函数:

 struct zint_symbol { /* . . . */ }; extern "C" { __declspec(dllexport) int ZBarcode_Encode_and_Print(struct zint_symbol *symbol, unsigned char *input, int length, int rotate_angle) { symbol->bitmap_width = 456; symbol->errtxt[0] = 'e'; symbol->errtxt[1] = 'r'; symbol->errtxt[2] = 'r'; symbol->errtxt[3] = 0; return 123; } } 

并在WinForms按钮单击事件处理程序等:

 public static void doStuff() { zint_symbol symbol = new zint_symbol(); int retval = EncodeAndPrint(ref symbol, "input string", 123, 456); Console.WriteLine(symbol.bitmap_width); Console.WriteLine(symbol.errtxt); } 

在dll中设置断点以根据需要设置/检查其他值。 为此,您需要在C#项目属性调试选项卡上设置“启用非托管代码调试”。

使用上面的代码和你发布的C和C#结构,我发现没有问题; 我在dll中设置的值对于struct中的C#是显而易见的。

您可以使用此方法进一步尝试,但最重要的是,您需要为您正在使用的dll版本使用正确的结构定义,并且当前您的结构与您链接的PDF不匹配。 希望您能找到正确版本的dll,或者为现有dll找到正确的结构定义。