对程序错误的处理及C#版的Windows错误码查询

几个知识点:

1.当Windows函数返回值为BOOL时,如果函数运行失败,则返回0值,否则返回非0值。最好对返回值进行测试,以确定它是0还是非0。不要测试返回值是否为TRUE。

2.当返回值为HANDLE时,通常都是失败就返回NULL,但有时函数会返回一个被定义为-1的INVALID_HANDLE_VALUE值。因此,使用函数的时候应该查阅平台SDK文档,以确定返回值是什么。

3.从系统内部来讲,当一个Windows函数检测到一个错误时,它会使用一个称为线程本地存储(thread_local_storage)的机制,将相应的错误代码号与调用的线程关联起来,这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回时,它的返回值就能指明一个错误已经发生。若要确定这个错误,需要调用(DWORD GetLastError())函数。

4.若要在调试器中查看线程的错误信息,选定"Watch"窗口中的一行,并键入"@err,hr",就可以了。

5.如果要向用户显示或者想得到GetLastError所返回错误码的文本显示,需要用到(DWORD FormatMessage(...))函数,本文的后面,会使用代码演示此函数的使用方法。

6.若要设定线程的最后错误代码,使用(VOID SetLastError(DWORD dwErrCode))函数,其中dwErrCode可以是WinError.h中已经存在的定义码,也可以是自定义的错误码。

全书的第一章,通常都是比较浅显易懂的内容,本书的第一章详述了Windows中的对程序错误的处理方式,示例源程序展示了一个类似于Visual Studio中的错误查询功能,作为自己本章的练习,我使用C#来实现界面,免去繁琐的界面编程,但是,我还不晓得怎么直接用C#实现FormatMessage这个函数,所以,我使用平台调用,写一个非托管的dll来实现这个功能,然后在我的C#程序里面调用它。

首先,需要写一个标准的Windows动态库(ErrorLookup.dll)以供C#调用,代码如下:

-----------------------------------------------------error.h-------------------------------------------------------------------

#ifndef __HUST_WING_ERROR_H__
#define __HUST_WING_ERROR_H__

#include "windows.h"

#define DllInterface __declspec(dllexport)

#ifdef __cplusplus
extern "C" {
#endif

DllInterface BOOL GetErrMsgByErrCode(DWORD dwErrCode,PTSTR sErrMsg);

#ifdef __cplusplus
}
#endif

#endif

--------------------------------------------------error.c------------------------------------------------------------------------

#include "stdio.h"
#include "error.h"

BOOL GetErrMsgByErrCode(DWORD dwErrCode,PTSTR psErrMsg)
{
    HLOCAL hlocal = NULL;

    BOOL bOK = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,dwErrCode, MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_CHINESE_SIMPLIFIED),
        (PTSTR)&hlocal,0,NULL);

    if (!bOK)
    {
        //是与网络相关的错误吗?
        HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"),NULL,DONT_RESOLVE_DLL_REFERENCES);

        if (hDll != NULL)
        {
            FormatMessage(FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ALLOCATE_BUFFER,
                hDll,dwErrCode,MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_CHINESE_SIMPLIFIED),
                (PTSTR)hlocal,0,NULL);

            FreeLibrary(hDll);
        }
    }
    if (hlocal != NULL)
    {
        //将字符串拷贝到psErrMsg中
        lstrcpy(psErrMsg,(PTSTR)hlocal);
        LocalFree(hlocal);
    }
    return TRUE;
}

---------------------------------------------------------------------------------------------------------------------------------------

在C#界面端,使用平台调用(p/Invoke)来调用ErrorLookup.dll,精华代码如下:

-----------------------------------------FrmError.cs--------------------------------------------------------------------------------------------

//public const Int32 FORMAT_MESSAGE_FORM_SYSTEM = 0x00001000;

        //[DllImport("Kernel32.dll")]
        //public static extern int FormatMessage(int flags, IntPtr source, int messageId, int languageId, StringBuilder buffer, int size, IntPtr arguments);

        [DllImport("ErrorLookup.dll", CharSet = CharSet.Unicode, EntryPoint = "GetErrMsgByErrCode")]
        public static extern bool GetErrMsgByErrCode(Int32 errCode, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder errMsg);

        private void tbErrCode_TextChanged(object sender, EventArgs e)
        {
            if (tbErrCode.Text == "")
            {
                return;
            }
            Int32 code = 0;
            try
            {
                code = Convert.ToInt32(tbErrCode.Text);
            }
            catch (Exception) //InvalidCastException FarmatException etc.
            {
                tbErrorMsg.Text =tbErrCode.Text+ ": 将值内容转换为错误码出错!";
                tbErrCode.Text = "";
                return;
            }
            StringBuilder sbFormatMessage = new StringBuilder(1024);
            //int retVal = FormatMessage(FORMAT_MESSAGE_FORM_SYSTEM, IntPtr.Zero, cd, 0,
            //                            sbFormatMessage, sbFormatMessage.Capacity, IntPtr.Zero);
           GetErrMsgByErrCode(code,sbFormatMessage);
           if (!String.IsNullOrEmpty(sbFormatMessage.ToString()))
           {
               tbErrorMsg.Text =code.ToString() +":" + sbFormatMessage.ToString();
           }
           else
           {
               tbErrorMsg.Text = code.ToString() + ":" + "未找到与之对应的错误信息!";
           }
        }

-----------------------------------------------------------------------------------------------------------------------------------------------------

以上代码的注释部分还演示了如何直接使用p/Invoke来实现FormatMessage,而且这个时候,你也不再需要ErrorLookup.dll。代码和原理都很简单,但是在具体的编程实践上,还是有一些注意事项值得提醒,不然会浪费很多不必要的时间:

1.关于封送处理时的类型问题。Windows中的DWORD类型是unsigned long,但是切不可在封送处理的时候将其与.Net中的ulong对应起来,DWORD表示32位整数,而.Net中的ulong是64位整数(对应于Int64);所以在写封送函数签名的时候,DWORD对应Int32(int),而不是Int64(ulong)。

2.在封送处理StringBuilder的时候,不需要指定ref或out参数。具体内容和原理请参见MSDN杂志2008年1月刊:

CLR完全介绍:托管代码与非托管代码之间的封送处理。

网址:http://msdn.microsoft.com/zh-cn/magazine/cc164193.aspx(点进去吧,别怕,文章为简体中文:))

3.在编译ErrorLookup.dll的时候,在项目属性"字符集"里面选择"使用UNICODE字符集",否则调用时,显示出来的是乱码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的示例,演示如何使用C#Windows窗体应用程序中创建学生账号登录查询窗口。 1. 创建一个新的Windows窗体应用程序项目,并在窗体设计器中添加以下控件: - Label:用于显示“用户名”文本。 - TextBox:用于输入用户名。 - Label:用于显示“密码”文本。 - TextBox:用于输入密码。 - Button:用于提交登录信息。 - DataGridView:用于显示查询结果。 2. 在窗体代码中添加以下代码: ```csharp using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Data.SqlClient; namespace StudentLogin { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { string connectionString = "Data Source=(local);Initial Catalog=StudentDB;Integrated Security=True"; string selectQuery = "SELECT ID, Name, Age, Gender, Class FROM Students WHERE Username=@Username AND Password=@Password"; using (SqlConnection connection = new SqlConnection(connectionString)) { SqlCommand command = new SqlCommand(selectQuery, connection); command.Parameters.AddWithValue("@Username", textBox1.Text); command.Parameters.AddWithValue("@Password", textBox2.Text); SqlDataAdapter adapter = new SqlDataAdapter(command); DataTable table = new DataTable(); adapter.Fill(table); dataGridView1.DataSource = table; } } } } ``` 这个示例代码中,我们首先创建一个名为“StudentDB”的数据库,其中包含一个名为“Students”的表,用于存储学生信息。在单击登录按钮时,我们将连接到数据库并执行查询,以检查输入的用户名和密码是否存在于数据库中。如果存在,则将查询结果显示在DataGridView中。 注意,这个示例代码仅作为学习和参考用途。在实际应用中,您需要更加细致地考虑安全性和错误处理等方面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值