C#和非托管DLL:避免多线程引起的NullReferenceException

时间:2013-04-21 19:03:25

标签: c# windows multithreading dll dllimport

我正在尝试创建一个C#应用程序来托管一个名为VZones的古老社交MMO中的自动文字游戏。许多类似的现有应用程序使用名为wadapi.dll的DLL文件,其代码不可用,它封装了允许与VZones主机通信的DDE功能。下面的代码(这是骨架应用程序的完整代码 - 我已将它包含在内,因为DLL和回调处理的不同部分,以及因为我最终将完整代码作为开源项目提供)只使用一个计时器,其TimerGrabTextTick事件调用DLL的DapiGetAllText过程。然后,DLL通过回调将客户端窗口中的所有会话文本传递回我的应用程序,该回调由我的ProcessGetAllText函数接收。它在前几分钟工作正常,而单个线程正在运行;但是一旦产生了一个辅助线程,它就会在“DapiGetAllText(”vzWordyHoster“)上发生NullReferenceException错误而崩溃;”线。

这是我在C#中的第一个实验,所以我仍然找到了自己的脚,我为代码中的任何混乱道歉。虽然我已经涉足过其他几种编程语言(作为一个爱好者,而不是专业程序员),这也是我第一次不得不担心线程,因为这是第一次给我带来任何问题。我的谷歌搜索和阅读主题使我相信我应该能够通过使用互斥锁或锁来避免这种异常,但经过几个令人沮丧的时间后,我仍然得到相同的例外。 (我现在已经删除了所有的互斥锁/ etc代码。)有人可以建议我可以做些什么来修复它吗?

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace vzWordyHoster
{
    /// <summary>
    /// A word games hoster for VZones.
    /// </summary>

    public delegate Boolean cbProcessAckData(String sAckData);
    public delegate Boolean cbProcessReceiveData(String sAvatar, Int32 nDataLen, String sData);
    public delegate Boolean cbProcessGetAllText(String sTextData);

    public partial class MainForm : Form
    {

        [DllImport("C:\\Program Files\\VZones\\wadapi.dll")] public static extern Int32 InitDDE(String sAppName, cbProcessAckData pfnProcessAckData, cbProcessReceiveData pfnProcessReceiveData, cbProcessGetAllText pfnProcessGetAllText);
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll")] public static extern Boolean KillDDE();
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll")] public static extern Boolean DapiRegister(String sAppName);
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll")] public static extern Boolean DapiUnregister(String sAppName);
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll", CallingConvention=CallingConvention.StdCall)] public static extern Boolean DapiGetAllText(String sAppName);
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll", CallingConvention=CallingConvention.StdCall)] public static extern Boolean DapiCommunicate(String sAppName, Int32 nMode, String sAvatar, String sText);
        [DllImport("C:\\Program Files\\VZones\\wadapi.dll", CallingConvention=CallingConvention.StdCall)] public static extern Boolean DapiSend(String sAppName, String sAvatar, Int32 nDataLen, String sData);

        private static String ack = "";
        private static String allText = "";

        // http://stackoverflow.com/questions/5754879/usage-of-mutex-in-c-sharp

        public static Boolean ProcessAckData(String sAckData) {
            ack = sAckData; // I've simplified this as compared to the VB6 code. Might need to add in equivalents of format, val and strconv again.         
            return true;
        }

        public static Boolean ProcessReceiveData(String sAvatar, Int32 nDataLen, String sData) {
            // Do nothing!
            return true;
        }

        public static Boolean ProcessGetAllText(String sTextData) {

            String sDapiText = sTextData;  // Again, simplified.
            allText = sDapiText;        
            return true;
        }

        public Boolean ESP(String AviName, String EspText) {
            do {
                DapiCommunicate("vzWordyHoster", 2, AviName, EspText);
            } while(ack == "6");
            return true;
        }

        public Boolean Say(String SayText) {
            do {
                DapiCommunicate("vzWordyHoster", 0, "", SayText);
            } while(ack == "6");
            return true;
        }

        public Boolean Think(String ThinkText) {
            do {
                DapiCommunicate("vzWordyHoster", 1, "", ThinkText);
            } while(ack == "6");
            return true;
        }


        public MainForm()
        {
            InitializeComponent();      

            cbProcessAckData myCbProcessAckData = new cbProcessAckData(vzWordyHoster.MainForm.ProcessAckData);
            cbProcessReceiveData myCbProcessReceiveData = new cbProcessReceiveData(vzWordyHoster.MainForm.ProcessReceiveData);
            cbProcessGetAllText myCbProcessGetAllText = new cbProcessGetAllText(vzWordyHoster.MainForm.ProcessGetAllText);

            InitDDE("vzWordyHoster", myCbProcessAckData, myCbProcessReceiveData, myCbProcessGetAllText);
            DapiRegister("vzWordyHoster");

            menuComboMode.SelectedIndex = 0;

        }  // MainForm


        ~MainForm() {  // Destructor
            DapiUnregister("vzWordyHoster");
            KillDDE();
        }

        void CheckBoxGrabTextEnableCheckedChanged(object sender, EventArgs e)
        {
            timerGrabText.Enabled = checkBoxGrabTextEnable.Enabled;         
        }


        public void TimerGrabTextTick(object sender, EventArgs e)
        {
            try {
                DapiGetAllText("vzWordyHoster");  // Crashes as soon as multiple threads are spawned.
                textBoxAllText.Text = allText;          
            }

            catch (NullReferenceException nre) {
                Debug.WriteLine(nre);
            }
        }


        void TextBoxAllTextTextChanged(object sender, EventArgs e)
        {
            textBoxAllText.SelectionStart = textBoxAllText.Text.Length;
            textBoxAllText.ScrollToCaret();
            textBoxAllText.Refresh();
        }
    } // class MainForm
} // namespace vzWordyHoster

0 个答案:

没有答案