C#下利用移植的qrencode生成二维码

来源:互联网 发布:淘宝买便宜东西的软件 编辑:程序博客网 时间:2024/06/11 19:56

       windows下二维码生成工具很多,比如ThoughtWorks.QRCode,zxing的,这些我都使用过,最近在搞Linux使用到了开源的qrencode(纯C编写),该库小巧、快速且稳定,于是想把qrencode移植到windows下,利用C#来调用。步骤如下:


1、下载qrencode源码,网址https://fukuchi.org/works/qrencode/


2、windows下编译qrencode成动态库,可参考http://blog.csdn.net/liyuanbhu/article/details/44647139,网上都是编译成静态库的,但是C#需要动态库,在vs里建立win32项目的时候需选择生成动态库,关键几点:

①加入.h和.c文件到项目,注意不要加入qrenc.c文件(里面包含Linux下的getopt不能编译)

②将原文件config.h.in改为config.h加入项目,并在最后加入‘#undef inline #define inline;’,在项目预处理加入HAVE_CONFIG_H

③由于编译为动态库,所以需要调用的函数需改写,改写的函数为QRcode_encodeString,函数定义头部加入extern _declspec(dllimport),这样windows下调用时才会发现该函数

④执行生成,生成的release版动态库仅有27KB,非常小巧,在嵌入式上也有优势


3、生成完动态库已成功一大半,C#调用的时候需要注意。

QRcode_encodeString函数在c语言里定义如下:

extern _declspec(dllimport) QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);

其中QRcode 结构体定义:

typedef struct {
int version;         ///< version of the symbol
int width;           ///< width of the symbol
unsigned char *data; ///< symbol data
} QRcode;

转换为C#里的入口函数定义如下:

        /// <summary>
        /// 二维码信息生成
        /// </summary>
        /// <param name="msg">要生成的信息</param>
        /// <param name="version">版本(即大小等级1~40)</param>
        /// <param name="level">容错等级</param>
        /// <param name="hint">模式</param>
        /// <param name="casesensitive">区分大小写,1是0否</param>
        /// <returns>QRcode结构体指针</returns>

 [DllImport("qrencode.dll")]

 private extern static IntPtr QRcode_encodeString(string msg, int version, QRecLevel level, QRencodeMode hint, int casesensitive);

QRcode 结构体C#定义:

[StructLayout(LayoutKind.Sequential)]
    public struct QRcode
    {
        public int version;         //< version of the symbol
        public int width;           //< width of the symbol
        public IntPtr data;         //< symbol data
    }

①函数具体调用参数意义可参见源码定义,该函数返回QRcode结构体指针,里面包含二维码信息高宽信息及标识数据data;data类型为byte数组,每个数据的第0位(bit0)代表每个像素点(1为黑0为白),需要根据高宽信息进行转换。调用全过程如下:


 var data = QRcode_encodeString(msg, 2, QRecLevel.QR_ECLEVEL_L, QRencodeMode.QR_MODE_8, casesensitive ? 1 : 0);//调用dll获取二维码信息指针
 QRcode result = (QRcode)Marshal.PtrToStructure(data, typeof(QRcode));//生成二维码结构体
 var datas = new byte[result.width * result.width];
 Marshal.Copy(result.data, datas, 0, datas.Length);//生成二维码标识数据,长度为width * width,每个代表每个像素点信息


②获取到二维码信息后可以通过位图来展示也可以通过gdi+来画,我选择的是位图来展示:

 var bmp = new Bitmap(result.width, result.width);//申请一个位图对象
            for (var i = 0; i < result.width; i++)
            {
                for (var j = 0; j < result.width; j++)
                {
                    if ((datas[i * result.width + j] & 0x01) == 1)//数据最低位1为黑0为白,其它数据位参见动态库源码定义
                    {
                        bmp.SetPixel(i, j, Color.Black);
                    }
                    else
                    {
                        bmp.SetPixel(i, j, Color.White);
                    }
                }
            }


③生成的位图为原始小图,生成的大小根据输入字符串的长度,可以转换成稍大尺寸的便于展示。我在网上找的放大函数(难得自己写了),由于使用了指针移位,项目需允许非安全代码,如下。


 /// <summary>
        /// 图片放大
        /// </summary>
        /// <param name="srcbitmap">原始图片</param>
        /// <param name="multiple">倍数</param>
        /// <returns></returns>
        public static Bitmap Magnifier(Bitmap srcbitmap, int multiple)
        {
            if (multiple <= 0) { multiple = 0; return srcbitmap; }
            Bitmap bitmap = new Bitmap(srcbitmap.Size.Width * multiple, srcbitmap.Size.Height * multiple);
            BitmapData srcbitmapdata = srcbitmap.LockBits(new Rectangle(new Point(0, 0), srcbitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            BitmapData bitmapdata = bitmap.LockBits(new Rectangle(new Point(0, 0), bitmap.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            unsafe
            {
                byte* srcbyte = (byte*)(srcbitmapdata.Scan0.ToPointer());
                byte* sourcebyte = (byte*)(bitmapdata.Scan0.ToPointer());
                for (int y = 0; y < bitmapdata.Height; y++)
                {
                    for (int x = 0; x < bitmapdata.Width; x++)
                    {
                        long index = (x / multiple) * 4 + (y / multiple) * srcbitmapdata.Stride;
                        sourcebyte[0] = srcbyte[index];
                        sourcebyte[1] = srcbyte[index + 1];
                        sourcebyte[2] = srcbyte[index + 2];
                        sourcebyte[3] = srcbyte[index + 3];
                        sourcebyte += 4;
                    }
                }
            }
            srcbitmap.UnlockBits(srcbitmapdata);
            bitmap.UnlockBits(bitmapdata);
            return bitmap;
        }


4、最后我自己封装了一个类库Qrencode.Net调用生成的qrencode.dll,写了个界面调用,效果如下:


生成的时间消耗主要在图片生成上,可以根据具体需求改进生成效率。

我把类库已经上传,和各位分享:http://download.csdn.net/download/fanshijian21/10036152。

第一次写博客,还望各位多多指教!

原创粉丝点击