我正在尝试创建一个 C# 应用程序来在一个名为 VZones 的古老社交 MMO 中托管自动文字游戏。许多类似的现有应用程序使用一个名为 wadapi.dll 的 DLL 文件,其代码不可用,它封装了允许与 VZones 主机通信的 DDE 功能。下面的代码(这是框架应用程序的完整代码——由于 DLL 和回调处理的不同部分,我将其全部包含在内,并且因为无论如何我最终都会将完整代码作为开源项目提供)只需使用一个计时器,其 TimerGrabTextTick 事件调用 DLL 的 DapiGetAllText 过程。然后,DLL 通过回调将客户端窗口中的所有对话文本传递回我的应用程序,该回调由我的 ProcessGetAllText 函数接收。前几分钟效果很好 当单个线程正在运行时;但是一旦生成了辅助线程,它就会在“DapiGetAllText("vzWordyHoster");”上崩溃并出现 NullReferenceException 错误 线。
这是我在 C# 中的第一次实验,所以我还在摸索中,对于代码中的任何混乱,我深表歉意。虽然我涉足过其他几种编程语言(作为爱好者,而不是作为专业程序员),但这也是我第一次不得不担心线程,因为这是第一次给我带来任何问题。我在谷歌上搜索和阅读这个主题让我相信我应该能够通过使用互斥锁或锁来避免这个异常,但是经过几个令人沮丧的小时后,我仍然遇到同样的异常。(我现在已经剥离了所有的互斥锁/等代码。)有人可以建议我可以做些什么来修复它吗?
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