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#里的入口函数定义如下:
///
/// 二维码信息生成
///
/// 要生成的信息
/// 版本(即大小等级1~40)
/// 容错等级
/// 模式
/// 区分大小写,1是0否
///
[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);
}
}
}
③生成的位图为原始小图,生成的大小根据输入字符串的长度,可以转换成稍大尺寸的便于展示。我在网上找的放大函数(难得自己写了),由于使用了指针移位,项目需允许非安全代码,如下。
///
/// 图片放大
///
/// 原始图片
/// 倍数
///
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。
第一次写博客,还望各位多多指教!