为何Indy框架OnExecute事件中AContext->Data为NULL?
背景与问题
我在实现StackOverflow问题《How to display messages in every Indy event?》中的方案2——放弃阻塞式同步,改用异步同步(TIdNotify、TThread::Queue()或等效方法),避免服务器线程等待主线程更新UI。已将自定义类改为继承自TIdNotify,把原Synchronize方法替换为Notify,移除了TIdSync对象的delete语句(TIdNotify会自动释放)。
当前在OnExecute事件中使用如下代码:
String sTemp; TIdBuffer *Buffer = static_cast<TIdBuffer*>(AContext->Data); Buffer->Write(static_cast<TIdMappedPortContext*>(AContext)->NetData); Buffer->CompactHead(); TTextToDisplay *TextToDisplay = new TTextToDisplay; CDecoder *Decoder = new CDecoder(); TStringList *slDecodedLines = NULL; TIdBytes ucBuffer; int offset = 0; while (ReadMessageData(Buffer, offset, ucBuffer)) { sTemp = String().sprintf(_D("%s"), ToHex(ucBuffer).c_str()); slDecodedLines = Decoder->formatInput(sTemp); if(slDecodedLines) { TextToDisplay->AddStringList(slDecodedLines); TextToDisplay->Notify(); } } // delete TextToDisplay; delete Decoder; delete slDecodedLines; if (offset > 0) Buffer->Remove(offset);
代码执行到第3行时抛出空指针异常,原因是AContext->Data为NULL,导致Buffer指针为空。已实现OnBeforeConnect、OnDisconnect、OnConnect事件(这些事件能正常输出消息),即使注释这些事件内的代码,错误依然存在。
解决思路
手动初始化AContext->Data:Indy的
AContext->Data不会自动初始化,必须在连接建立阶段手动创建TIdBuffer并赋值。在OnConnect事件中添加初始化代码:void __fastcall TForm1::IdMappedPortServer1Connect(TIdContext *AContext) { AContext->Data = new TIdBuffer(); }同时在
OnDisconnect事件中释放对象,避免内存泄漏:void __fastcall TForm1::IdMappedPortServer1Disconnect(TIdContext *AContext) { delete static_cast<TIdBuffer*>(AContext->Data); AContext->Data = NULL; }确认AContext类型转换合法性:代码中把
AContext强制转换为TIdMappedPortContext,需确保当前使用的Indy组件是TIdMappedPortServer——只有该组件的上下文才属于TIdMappedPortContext类型。若误用其他服务器组件(如TIdTCPServer),该转换会引发未定义行为,甚至间接影响AContext->Data的有效性。排查自定义函数的影响:检查
ReadMessageData函数的实现,确保它不会意外修改AContext->Data或Buffer的状态(比如提前释放或置空指针)。添加空指针保护:在使用
Buffer前增加判空逻辑,避免直接访问空指针崩溃,同时输出调试信息定位问题:TIdBuffer *Buffer = static_cast<TIdBuffer*>(AContext->Data); if (!Buffer) { OutputDebugString(_D("AContext->Data is NULL in OnExecute")); return; }
内容的提问来源于stack exchange,提问作者Beto

