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

时间:2014-08-30 21:29:05

标签: c# c pinvoke dllimport

我正在用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应该更改其中一些变量,例如bitmapbitmap_width,{{ 1}}等等。

我怀疑内存中有两个bitmap_height副本;一个我的C#代码(由zint_symbol创建)和 另一个DLL正在写入。然而,我某些该库正常工作。

C结构:

ZintLib.Create()

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

我写了一个小Windows forms example(这里的DLL也在available here。库文档(第18/61页解释了结构变量)is available here和整个C源代码代码可用here (zint-2.4.3.tar.gz)。特别感兴趣的文件是zint.h,library.c和datamatrix.c.C源代码是相同版本的DLL。

修改

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

3 个答案:

答案 0 :(得分:3)

你有一个奇怪的错误:

[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 <stddef.h>
//...
int main()
{
    size_t len = sizeof(zint_symbol);                // 33100
    size_t offs = offsetof(zint_symbol, errtxt);     // 32984
    return 0;
}

答案 1 :(得分:0)

查看P / Invoke声明的差异,我们可以看到成功的接口使用[In,Out]属性。你也应该把它们添加到另一个函数中。

见这里:Are P/Invoke [In, Out] attributes optional for marshaling arrays?

答案 2 :(得分:0)

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

例如,在PDF中,scale出现在option_3之后,而option_1之前出现bitmap_height。 PDF中的最后一个元素是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; } } ,而你的后续元素则继续。{1}}可能还有其他差异,这些正是我注意到的。

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

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

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

{{1}}

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

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

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