C# WinForm调用底层DLL实现员工IC卡读写与考勤系统
本文详细介绍如何利用C#和WinForm构建员工IC卡考勤系统。通过封装外部动态链接库(如Mwic_32.dll)进行硬件通信,实现了IC卡的读取验证、数据写入以及自动考勤记录存储。文章包含完整的业务逻辑代码示例,涵盖防重复打卡、员工信息管理及Access数据库交互等关键环节。
效果展示
系统主要界面展示了实时时钟显示、员工信息录入窗口以及考勤打卡时的状态反馈:
核心操作逻辑
第1步:初始化与配置环境
系统运行前需确保已正确连接IC卡读写设备,并将底层驱动提供的 Mwic_32.dll 放置在程序运行目录下。通过声明 DllImport 特性来映射 DLL 中的 API 方法,包括端口初始化 (auto_init)、密码设置 (setsc_md)、蜂鸣器控制 (dv_beep) 以及读写命令 (例如 srd_4442, swr_4442)。
第2步:IC卡写入(发卡注册)
在添加新员工的流程中,用户输入姓名、部门等信息后,系统调用 WriteIC 方法将唯一的员工编号写入IC卡芯片。操作顺序如下:
- 连接设备:调用
auto_init(0, 9600)打开串口并初始化波特率为 9600。 - 身份校验:使用默认密码(十六进制
0xff, 0xff, 0xff)调用Csc_4442进行鉴权。 - 数据写入:遍历待写入的编号字符串,逐个字符通过
swr_4442写入至卡片指定偏移地址(如从地址33开始)。 - 完成提示:若返回值为
0表示成功,触发蜂鸣声确认。
[StructLayout(LayoutKind.Sequential)]
public unsafe class IC
{
//对设备进行初始化
[DllImport("Mwic_32.dll", EntryPoint = "auto_init", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int auto_init(int port, int baud);
//核对卡密码
[DllImport("Mwic_32.dll", EntryPoint = "csc_4442", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern Int16 Csc_4442(int icdev, int len, [MarshalAs(UnmanagedType.LPArray)] byte[] p_string);
//向IC卡中写数据
[DllImport("Mwic_32.dll", EntryPoint = "swr_4442", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int swr_4442(int icdev, int offset, int len, char* w_string);
}
第3步:自动循环读取与考勤判断
开启考勤后,窗体启动定时器 timer1 持续监听读卡器状态。为了避免同一张卡在短时间内重复刷入多条数据,引入了 flag 变量作为状态锁:
- 检测无卡:若返回值
i == -1,说明无卡接触,重置标志位并清空界面上的员工信息文本框。 - 防止重读:若检测到卡片但
flag != -1,则跳过后续逻辑,确保一卡一次。 - 查重逻辑:调用
isCheck(icID)查询数据库,若该卡号在当日CheckNote表中已存在,则弹出警告“已经参加过考勤!”;否则视为有效打卡。
第4步:数据存储与记录更新
对于合法的初次考勤请求,系统执行以下动作:
- 提取信息:根据读取到的
icID在Employee表中查找对应的姓名、性别等详细信息。 - 插入记录:拼接 SQL 语句将考勤数据存入
CheckNote表,字段包含卡号、员工名、部门和当前日期时间。 - UI刷新:更新任务栏标签显示当日已参加考勤的总人数。
// 读取 IC 卡的公共方法
public static int ReadIC(TextBox tb)
{
int flag = -1;
// 初始化设备
int icdev = IC.auto_init(0, 9600);
if (icdev < 0) return flag;
// 鉴权密码 0xFFFFFFFF 对应 byte数组 {0xff, 0xff, 0xff}
unsafe
{
byte[] pwd = new byte[3] { 0xff, 0xff, 0xff };
Int16 checkIC_pwd = IC.Csc_4442(icdev, 3, pwd);
if (checkIC_pwd < 0) return flag;
string ic = "";
char str;
// 读取固定长度数据(假设6位卡号)
for (int j = 0; j < 6; j++)
{
int read = IC.srd_4442(icdev, 33 + j, 1, &str);
ic = ic + Convert.ToString(str);
}
tb.Text = ic;
if (read == 0) flag = 0;
}
IC.ic_exit(icdev); // 必须关闭设备释放资源
return flag;
}
第5步:数据库访问模块
所有数据库交互均指向同级的 db1.mdb 文件。采用 OleDbConnection 进行 Access 数据库操作。为提高安全性与可维护性,实际项目中建议替换为参数化查询以防止注入攻击。核心类方法包括:
ExecuteSQL(string sql):执行增删改操作。GetInfo(string id, ...):根据卡号获取并填充 UI 控件。CheckID(string id)/isCheck(string id):分别用于检查卡号是否存在或是否已打卡。
class baseClass
{
public static bool ExecuteSQL(string sql)
{
bool flag = false;
string strg = Application.StartupPath.ToString();
strg = strg.Substring(0, strg.LastIndexOf("\\"));
strg = strg.Substring(0, strg.LastIndexOf("\\"));
strg += @"\db1.mdb";
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data source=" + strg);
conn.Open();
OleDbCommand cmd = new OleDbCommand(sql, conn);
int i = cmd.ExecuteNonQuery();
if (i > 0) { flag = true; conn.Close(); }
return flag;
}
}
关键注意事项
- 资源回收:每次读写操作结束前务必调用
IC.ic_exit(icdev),否则可能导致设备无法再次识别或内存泄漏。 - 安全编码:草稿中的 SQL 语句拼接方式较为原始,在生产环境中请务必升级至参数化查询以保障数据安全。
- 兼容性:Access 驱动 (Jet OLEDB) 依赖于操作系统安装的数据访问组件,Windows 10/11 可能需要额外配置。
简要总结
上述就是基于C#实现员工IC卡读写及考勤功能的详细内容,更多关于C# IC卡开发技术趋势的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













