हैलो, मैं त्रुटि 217 हूं और मैं आपको कुछ भी नहीं बताऊंगा

इस तरह के "पक्षपातपूर्ण" के साथ संभवतः कई लोग आवेदन के शुरू या अंत में मिले:



एक बहुत जानकारीपूर्ण संदेश, त्रुटि का कारण, स्थान और इसे हल करने का तरीका तुरंत स्पष्ट है।
हालांकि, अगर मजाक के बिना, यह सब क्या है?
बेशक, यह एक अपवाद है, लेकिन न तो अपवाद का प्रकार और न ही इसका विवरण हमारे लिए उपलब्ध है - बस "रनटाइम त्रुटि 217" और पता, और फिर खुद ...

ईमानदार होने के लिए, इससे पहले कि मैं किसी तरह इस अपवाद के बारे में भी नहीं सोचता, क्योंकि मेरी परियोजनाओं में, यह एक दुर्लभ घटना है, जब तक कि एक दिन में उपयोगकर्ताओं की एक पूरी श्रृंखला ने 217 वीं त्रुटि को फिर से शुरू नहीं किया।
हालांकि, तब भी मैं सही रास्ते पर नहीं गया और बस प्रोजेक्ट में लॉगिंग का एक अतिरिक्त स्तर जोड़ दिया, जिसके परिणामों के अनुसार मैंने जल्दी से इसका कारण ढूंढ लिया और इसे ठीक किया।
लेकिन, वास्तव में, मैंने अभी अपना समय बिताया है ...

और अगर भविष्य में दूसरे दिन विक्टर फेडोरेनकोव ने मुझसे संपर्क नहीं किया और त्रुटि संख्या 217 के बारे में अपने विचारों के बारे में बताया, तो उन्होंने इसे भविष्य में खर्च किया होगा।

सिद्धांत और समस्या विश्लेषण


सिद्धांत के बिना, हम कहीं नहीं हैं, अन्यथा, हम अपने स्वयं के ज्ञान की सीमा के भीतर दफन हो सकते हैं।
इसलिए, हम निश्चित रूप से, सैद्धांतिक भाग के साथ शुरू करते हैं।

शुरू करने के लिए, मैंने अपने विचारों को त्रुटियों के बारे में थोड़ा ताज़ा किया, सिद्धांत रूप में, अलेक्जेंडर अलेक्सेव द्वारा "त्रुटि हैंडलिंग - अध्याय 1.2.2" लेख के भाग को पढ़ने के बाद, जिसमें से मुझे जानकारी मिली कि त्रुटि 217 दिखाई जाएगी यदि SysUtils मॉड्यूल को आरंभीकृत नहीं किया गया है, और यह अलेक्जेंडर द्वारा स्पष्ट रूप से चित्रित किया गया है:


पूर्ण आकार की छवि देखें ...

इस चित्र के आधार पर, एक कठिन निष्कर्ष निकाला जा सकता है: जबकि SysUtils जीवित है, सभी अपवादों को सामान्य रूप में प्रदर्शित किया जाना चाहिए, क्योंकि एक अलग उल्लेख है:

उदाहरण के लिए, यदि आप एक रनटाइम त्रुटि के बारे में संदेश देखते हैं, तो, ऊपर दिए गए आरेख द्वारा निर्णय लेते हुए, यह संभावना नहीं है कि त्रुटि फॉर्म में इवेंट हैंडलर में हुई। लेकिन यह बहुत अधिक संभावना है कि ऐसा होता है, कहते हैं, कुछ अंतिमकरण अनुभाग (जो कि SysUtils मॉड्यूल के अंतिमकरण अनुभाग के बाद किया जाता है) या असाइन किए गए ExitProcessProc प्रक्रिया में होता है। लेकिन, ज़ाहिर है, त्रुटि का कारण कहीं भी बैठ सकता है - जिसमें वर्णित घटना हैंडलर भी शामिल हैं।

ठीक है, आइए देखें, उस कोड को लिखें जिसमें SysUtils को Unit1 मॉड्यूल की तुलना में बाद में अंतिम रूप दिया जाना चाहिए, जिसमें हम कृत्रिम रूप से एक अपवाद उत्पन्न करते हैं:

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} initialization finalization raise Exception.Create('finalization exception'); end. 

बनाएँ, चलाएँ, प्रपत्र बंद करें और ... रनटाइम त्रुटि 217।

SysUtils को अंतिम रूप देने के बाद 217 का प्रदर्शन पूरी तरह से सही है, लेकिन चलो अंतिमकरण कोड को ही देखें

 procedure FinalizeUnits; ... begin ... Count := InitContext.InitCount; Table := InitContext.InitTable^.UnitInfo; ... try while Count > 0 do begin Dec(Count); InitContext.InitCount := Count; P := Table^[Count].FInit; if Assigned(P) then ... TProc(P)(); ... end; end; except FinalizeUnits; { try to finalize the others } raise; end; end; 

देखें कि क्या होता है: अंतिम रूप देने की प्रक्रिया में, सभी अंतिम प्रक्रियाओं को कहा जाता है, जिनके पते InitContext.InitTable ^ .UnitInfo सरणी में उस क्रम में स्थित होते हैं, जिसमें वे आरंभिक थे, अर्थात्। बहुत पहले वाले सरणी की शुरुआत में स्थित हैं (और अंतिम रूप अंत से है)।
SysUtils + सिस्टम कहीं बहुत नीचे स्थित है, लेकिन हम, हमारे Unit1 मॉड्यूल के साथ, कहीं बहुत ऊपर हैं।
लेकिन अचानक हमारे मॉड्यूल और "ब्रॉड" में एक अपवाद होता है, कैथार्सिस ऑर्डर का उल्लंघन होता है।

"ब्रॉड्स" के बाद फ़ाइनलीइज़िट्स को फिर से बुलाया जाता है, हमारे मॉड्यूल को छोड़ दिया जाता है, जो एक अपवाद का कारण बनता है, जिसके परिणामस्वरूप रास्ते में आने वाले SysUtils और विभिन्न क्लास डिस्ट्रक्टर्स नष्ट हो जाते हैं, मेमोरी मैनेजर के साथ सिस्टम हीप में क्रैश हो जाता है (सूची के शीर्ष पर पहले से बैठे) माथे में एक नियंत्रण शॉट है - RAISE, और यहां हम रवाना हुए - हेलो 217।

लेकिन क्या होगा अगर किसी मॉड्यूल के आरंभीकरण अनुभाग में कोई अपवाद होता है?

हाँ, सभी समान:

 procedure InitUnits; ... begin ... try ... except FinalizeUnits; raise; end; end; 

हम निष्कर्ष निकालते हैं: आरंभीकरण या अंतिमकरण खंडों में कोई भी अखंडित अपवाद अपवाद के वर्णन को नुकसान पहुंचाएगा और त्रुटि 217 तक ले जाएगा।

इस सिद्धांत के साथ, मुझे लगता है, हम खत्म करेंगे।
रनटाइम त्रुटि 217 की घटना के कारण की समझ होने के बाद, हम अपवाद संदेश के अधिक परिचित संस्करण पर अपने हाथ लाने की कोशिश करेंगे।

मॉड्यूल को अंतिम रूप देना अक्षम करें


चर्चा की शुरुआत में, विक्टर ने इस त्रुटि को दरकिनार करने के लिए एक काफी प्रभावी तरीका प्रस्तावित किया।

उसका विश्लेषण इस प्रकार था: अपवाद हैंडलर का सामान्य आरंभीकरण SysUtils मॉड्यूल की InitException प्रक्रिया में किया जाता है, और DoneException को कॉल करके अंतिम रूप दिया जाता है।

यदि आप किसी तरह से DoneException plus call को डिसेबल कर देते हैं और मेमोरी मैनेजर को कॉल को सिस्टम फाइनल करने वाले ब्लॉक में ब्लॉक करने से रोकते हैं, तो हमें स्वीकार्य फॉर्म में एक अपवाद संदेश प्राप्त होगा।

एक समाधान के रूप में, निम्न कोड प्रस्तावित किया गया था, जिसे बहुत पहले मॉड्यूल के साथ प्रोजेक्ट फ़ाइल से जोड़ा जाना चाहिए (यह D2005 और उच्चतर से शुरू होकर काम करेगा):

 unit suShowExceptionsInInitializeSections; interface uses SysUtils; implementation uses Windows; //  PackageInfo   // System     InitTable,       function GetInitTable: PackageInfo; var Lib: PLibModule; TypeInfo: PPackageTypeInfo; begin Result := nil; Lib := LibModuleList; if not Assigned(Lib) then Exit; //    (BPL ),  , //      / BPL,    //  if Assigned(Lib^.Next) then Exit; Typeinfo := Lib^.TypeInfo; if Assigned(TypeInfo) then begin //  TPackageTypeInfo //     PackageInfo //  . // IDA ,   TypeInfo     //PackageInfo  //      PackageInfo     //TypeInfo    Result := PackageInfo(PByte(TypeInfo) - (LongWord(@PackageInfoTable(nil^).TypeInfo))); end; end; //      procedure DisableAllFinalization; var Loop: Integer; OldProtect: LongWord; InitTable: PackageInfo; Table: PUnitEntryTable; begin InitTable := GetInitTable; if Assigned(InitTable) then begin Table := InitTable^.UnitInfo; if Assigned(Table) then //        /   if VirtualProtect(Table, SizeOf(PackageUnitEntry) * InitTable^.UnitCount, PAGE_READWRITE, OldProtect) then for Loop := 0 to InitTable^.UnitCount - 1 do Table^[Loop].FInit := nil; end; end; initialization finalization //    ,  SysUtils  ,  //   .       , //       (  System), //      . //       //1.  Exception    -  //2.    // //    ,      ,   //      . //   ,     DLL ,    //   if IsLibrary then Exit; //          //      RTTI.   ,    //  ,       //.         DPR , //   . //       , //          . //      DisableAllFinalization; end. 

ईमानदार होने के लिए - उन्होंने खड़े होने पर सराहना की।
यहाँ यह है: गंदगी रूप में एक हैक के रूप में यह है - केवल उन लोगों को जो वास्तव में समझते हैं कि इस खतरे से ऐसी चीजें क्या कर सकती हैं :)
और यह मॉड्यूल हमारे आईटी विभाग के काम को लगभग तीन घंटे तक लाया - यह एक कठिन चर्चा थी :)

लेकिन, वैसे, आइए इस कोड के तर्क का विश्लेषण करें:
इसका सार सरल है, आपको डेटा को लोड किए गए मॉड्यूल (बीपीएल सहित) के बारे में उस रूप में जाने की जरूरत है जिसमें उन्हें डेल्फी एप्लिकेशन द्वारा समझा जाता है। यह TLibModule संरचनाओं की यूनिडायरेक्शनल सूची के शीर्ष पर पहुंचकर किया गया था। सूची का पहला तत्व एक संरचना होगी जो वर्तमान छवि का वर्णन करती है, जहां से हमें केवल UnitInfo संरचना के बारे में डेटा प्राप्त करने की आवश्यकता होती है, जिसमें आरंभिक मॉड्यूल की संख्या और पैकेजुइनिटइंट्री रिकॉर्ड के रूप में उनके प्रारंभ और अंतिमकरण प्रक्रियाओं के पते शामिल हैं।

मॉड्यूल के अंतिमकरण को अवरुद्ध करना प्रत्येक पैकेज यूनाइट्री एंट्री के लिए FInit पैरामीटर को nil में सेट करने से होता है।

जब इस पैरामीटर को शून्य कर दिया जाता है, तो फ़ाइनलीज्यूनिट्स हैंडलर को कॉल करने में सक्षम नहीं होंगे, और इसके परिणामस्वरूप, मैंने जो ऊपर लिखा था, वही उठानें वाले अपवाद को सही ढंग से प्रदर्शित करने में सक्षम होगा।



लेकिन फिर सब कुछ अधिक जटिल है।

एक अच्छा विचार कंघी करने की कोशिश कर रहा है


विचार ध्वनि है और कारण स्पष्ट हैं, लेकिन किसी भी तरह, संसाधन जारी नहीं किए जाते हैं, फास्टम सामान्य रूप से काम करना बंद कर देगा (यह अंतिम रूप से लीक के दौरान एकत्र करता है), और संगतता पर्याप्त नहीं है, उदाहरण के लिए, जैसा कि मैंने ऊपर कहा, डेल्फी के तहत। 7, यह कोड बिल्कुल काम नहीं करेगा।

आईटी विभाग में पहले घंटे की चर्चा के बाद, हम भी निम्नलिखित निष्कर्ष पर पहुंचने में कामयाब रहे: "और सिसटिल्स और सिस्टम के साथ उनके साथ नरक करने के लिए - वे उनके पीछे कुछ भी महत्वपूर्ण नहीं ले जाते हैं।"
और फिर, उन्होंने फिर से बहस शुरू कर दी - ठीक है, यह दृष्टिकोण हमें शोभा नहीं देता, सब कुछ ठीक लग रहा है, लेकिन बड़े करीने से किसी तरह नहीं।

यहां तक ​​कि अपवाद विध्वंसक के ढेर को अंतिम रूप देने के प्रत्यक्ष स्प्लिसिंग के लिए विकल्पों पर विचार किया गया था - लेकिन मौजूदा हैक पर एक अतिरिक्त हैक किसी को भी सूट नहीं करता था।

और यहाँ, डिबगर में बैठे और 70 वीं बार कोड चला रहे थे, एक विचार आया।
तो यह ... लेकिन जो अपवाद हुआ उसके बारे में संदेश कैसे है?

और यह कंट्रोल को हेंडलहैंडलर में स्थानांतरित करके प्रदर्शित किया जाता है, जिसके कोड में कुछ भी गुप्त नहीं है।
और मॉड्यूल फाइनल को हटाकर हम क्या करते हैं?
यह सही है, हम उसे वही लागू करते हैं।

आइए एक असाधारण हैंडलर कॉल का अनुकरण करने का प्रयास करें।
हम परीक्षण इकाई लिखते हैं और इसे पहले परियोजना से जोड़ते हैं:

 unit Test; interface uses SysUtils; var E: Exception; implementation initialization finalization E := AcquireExceptionObject; if E <> nil then begin ShowException(E, ExceptAddr); E.Free; Halt(1); end; end. 

हम इसे लॉन्च करते हैं और ...


यह निकला।

अंतिमकरण चक्र में एकीकृत करते हुए, हमने अपवाद को प्रदर्शित किया और हॉल्ट (1) को कॉल करके आगे अंतिम रूप देना जारी रखा।

नतीजतन, समस्या हल, सक्षम और प्रलेखित है, और डेल्फी 7 के साथ संगत है, लेकिन ...

लेकिन एक विचार विकसित करने के लिए नहीं?


"प्रेरित त्रुटियों" के रूप में ऐसी एक चीज है, अर्थात् त्रुटियां जो इस तथ्य के कारण हुईं कि उनमें एक त्रुटि भी थी।

खैर, उदाहरण के लिए, फ़ंक्शन ए, जिसे एक निश्चित वर्ग की एक आवृत्ति वापस करनी चाहिए, और फ़ंक्शन बी, जो काम में इस उदाहरण का उपयोग करता है। उदाहरण के लिए, फ़ंक्शन ए में एक अनहेल्ड अपवाद उत्पन्न हुआ (उदाहरण के लिए, फ़ाइल तक कोई पहुंच नहीं है) और यह एक वर्ग नहीं बनाता था, और फिर कहीं और बाद में, आवेदन कोड का उपयोग करते हुए, प्रक्रिया बी इस उदाहरण तक पहुंचता है और, परिणामस्वरूप, एक्सेस उल्लंघन होता है।

एक ही बात इनिशियलाइज़ेशन / फ़ाइनलाइज़ेशन प्रक्रियाओं में हो सकती है, और फ़ाइनलिज़ेशन में जो अपवाद हुआ है, वह हमारे कारण को छिपा देगा।

प्रदर्शन के लिए, हम एक ऐसा कोड लिखेंगे, जिसमें आवेदन के आरंभ होने पर, एक निश्चित लकड़हारा बनाया जाएगा, जिसमें आवेदन के चरणों को लिखा जाएगा, और अंतिम रूप देने में काम पूरा होने का रिकॉर्ड होगा।
एक अपवाद बढ़ाने के लिए, लकड़हारे को एक गैर-मौजूद पथ के साथ बनाया जाए:

 uses Classes; var Logger: TFileStream; const StartLog: AnsiString = '  ' + sLineBreak; EndLog: AnsiString = '  ' + sLineBreak; implementation initialization Logger := TFileStream.Create('A:\MyLog,txt', fmCreate); Logger.WriteBuffer(StartLog[1], Length(StartLog)); finalization Logger.WriteBuffer(EndLog[1], Length(EndLog)); Logger.Free; end. 

कुछ लोगों के पास सिस्टम में "ए" ड्राइव है, इसलिए इस कोड का परिणाम या तो "रनटाइम त्रुटि 216" होगा (अर्थात 216, 217 नहीं), या यदि हम पिछले अध्याय से कोड कनेक्ट करते हैं:

001B1593 पर मॉड्यूल Project2.exe में अपवाद अपवाद।
मॉड्यूल 'Project2.exe' में पता 005B1593 पर पहुंच उल्लंघन। पता 00000000 का पढ़ें

लेकिन इसका कारण बहुत पहले अपवाद है, जिसे हम प्रदर्शित नहीं करते हैं, और हम एक स्नैप से त्रुटि के कारण का पता लगाने में सक्षम नहीं हैं।

इस अन्याय को ठीक करने के लिए, आप कोड को थोड़ा कंघी करके इस अवस्था में ला सकते हैं:

 unit ShowExceptSample; interface uses SysUtils, Classes; implementation type PRaiseFrame = ^TRaiseFrame; TRaiseFrame = packed record NextRaise: PRaiseFrame; ExceptAddr: Pointer; ExceptObject: TObject; ExceptionRecord: PExceptionRecord; end; var //      CurrentRaiseList: Pointer = nil; //       function GetNextException: Pointer; begin if CurrentRaiseList = nil then CurrentRaiseList := RaiseList; if CurrentRaiseList <> nil then begin Result := PRaiseFrame(CurrentRaiseList)^.ExceptObject; PRaiseFrame(CurrentRaiseList)^.ExceptObject := nil; CurrentRaiseList := PRaiseFrame(CurrentRaiseList)^.NextRaise; end else Result := nil; end; var ExceptionStack: TList; E: Exception; initialization finalization // ,    ? E := GetNextException; if E <> nil then begin ExceptionStack := TList.Create; try //  ,     while E <> nil do begin ExceptionStack.Add(E); E := GetNextException; end; //      ,     while ExceptionStack.Count > 0 do begin E := ExceptionStack[ExceptionStack.Count - 1]; ExceptionStack.Delete(ExceptionStack.Count - 1); ShowException(E, ExceptAddr); E.Free; end; finally ExceptionStack.Free; end; //     Halt(1); end; end. 

यहाँ यह विचार सरल है, GetNextException फ़ंक्शन अनिवार्य रूप से AcquireExceptionObject कॉल को दोहराता है, लेकिन इसके कॉल के बाद कतार में अगले अपवाद के लिंक को नहीं खोता है, लेकिन बाहरी चर में अगले फ्रेम के पते को याद करता है।
उसके बाद, सभी अपवादों को सूची में दर्ज किया जाता है (अंतिम सूची में पहला होगा) और प्राथमिकता के क्रम में प्रोग्रामर को प्रदर्शित किया जाता है, जिसके परिणामस्वरूप हम तुरंत समझ जाएंगे कि यह पहली बार हुआ है:



और उसके जाने के बाद ही ए.वी.

अब बाकी एरर कोड्स के बारे में।
मैंने "रनटाइम त्रुटि 217" के साथ शुरुआत क्यों की?
ठीक है, क्योंकि यह सबसे आसानी से प्रतिलिपि प्रस्तुत करने योग्य है, और तकनीकी रूप से, उपरोक्त मॉड्यूल का उपयोग करके, हम सभी संभव रनटाइम त्रुटियों का एक पूरी तरह से सामान्य विवरण प्राप्त करेंगे, जिनमें से हमारे पास बहुत सारे हैं:

  reMap: array [TRunTimeError] of Byte = ( 0, { reNone } 203, { reOutOfMemory } 204, { reInvalidPtr } 200, { reDivByZero } 201, { reRangeError } { 210 Abstract error } 215, { reIntOverflow } 207, { reInvalidOp } 200, { reZeroDivide } 205, { reOverflow } 206, { reUnderflow } 219, { reInvalidCast } 216, { reAccessViolation } 218, { rePrivInstruction } 217, { reControlBreak } 202, { reStackOverflow } 220, { reVarTypeCast } 221, { reVarInvalidOp } 222, { reVarDispatch } 223, { reVarArrayCreate } 224, { reVarNotArray } 225, { reVarArrayBounds } { 226 Thread init failure } 227, { reAssertionFailed } 0, { reExternalException not used here; in SysUtils } 228, { reIntfCastError } 229, { reSafeCallError } 235, { reMonitorNotLocked } 236 { reNoMonitorSupport } {$IFDEF PC_MAPPED_EXCEPTIONS} { 230 Reserved by the compiler for unhandled exceptions } {$ENDIF PC_MAPPED_EXCEPTIONS} {$IF defined(PC_MAPPED_EXCEPTIONS) or defined(STACK_BASED_EXCEPTIONS)} { 231 Too many nested exceptions } {$ENDIF} {$IF Defined(LINUX) or Defined(MACOS)} { 232 Fatal signal raised on a non-Delphi thread } , 233 { reQuit } {$ENDIF LINUX or MACOS} {$IFDEF POSIX} , 234 { reCodesetConversion } {$ENDIF POSIX} , 237, { rePlatformNotImplemented } 238 { reObjectDisposed } ); 

परिणाम


ऐसे लापरवाह कोड के साथ, हम पा सकते हैं कि कोड 217 के तहत त्रुटि के बारे में क्या बात नहीं करना चाहता है।

हालांकि, मुझे नहीं लगता कि यह दृष्टिकोण अनुभवी प्रोग्रामरों के लिए अपरिचित होगा।
सबसे अधिक संभावना है कि यह हैलो बाइक है, क्योंकि सबसे अधिक संभावना है कि इस समस्या को पहले से ही किसी ने हल किया था, लेकिन मुझे अभी इस समाधान के बारे में पता नहीं था।

और अगर नहीं, तो मैं दूसरा होगा।

इस लेख के सह-लेखक और मास्टरमाइंड के लिए एक अलग सम्मान विक्टर फेडोरेनकोव है।

सौभाग्य है।

Source: https://habr.com/ru/post/In209902/


All Articles