delphi दशमलव जब नियंत्रण का वर्ग नाम बहुत लंबा होता है तो मुझे एक्सेस उल्लंघन क्यों मिलता है?



वर्गमूल किसे कहते है (1)

मैंने क्रम में एक नियंत्रण को घटाया ताकि मैं कुछ फ़ील्ड जोड़ सकूं जो मुझे चाहिए, लेकिन अब जब मैं रनटाइम पर इसे बनाउंगा तो मुझे Access Violation मिल जाएगा। दुर्भाग्यवश यह एक्सेस उल्लंघन उस स्थान पर नहीं होता है जहां मैं नियंत्रण बना रहा हूं, और यहां तक ​​कि जिनके साथ मैं सभी डीबग विकल्पों के साथ निर्माण कर रहा हूं ("डीसीयू डीबग के साथ बिल्ड करें" सहित) स्टैक ट्रेस मुझे बिल्कुल मदद नहीं करता है !

त्रुटि को पुन: उत्पन्न करने के अपने प्रयास में मैंने कंसोल एप्लिकेशन बनाने का प्रयास किया, लेकिन स्पष्ट रूप से यह त्रुटि केवल एक फॉर्म एप्लिकेशन में दिखाई देती है, और केवल तभी जब मेरा नियंत्रण वास्तव में किसी रूप पर दिखाया गया हो!

त्रुटि को पुन: उत्पन्न करने के लिए चरण यहां दिए गए हैं। एक नया वीसीएल फॉर्म एप्लिकेशन बनाएं, एक बटन ड्रॉप करें, ऑनक्लिक हैंडलर बनाने के लिए डबल-क्लिक करें और इसे लिखें:

type TWinControl<T,K,W> = class(TWinControl);

procedure TForm3.Button1Click(Sender: TObject);
begin
  with TWinControl<TWinControl, TWinControl, TWinControl>.Create(Self) do
  begin
    Parent := Self;
  end;
end;

जब भी मैंने कोशिश की, यह लगातार उल्लंघन उल्लंघन उत्पन्न करता है। केवल डेल्फी 2010 पर इसका परीक्षण किया क्योंकि यह एकमात्र संस्करण है जो मुझे इस कंप्यूटर पर मिला है।

प्रश्न होंगे:

  • क्या यह डेल्फी जेनरिक में ज्ञात बग है?
  • क्या इसके आसपास कोई कार्य है?

संपादित करें

क्यूसी रिपोर्ट का लिंक यहां दिया गया है: http://qc.embarcadero.com/wc/qcmain.aspx?d=112101


सबसे पहले, इसका जेनरिक के साथ कुछ लेना देना नहीं है, लेकिन जेनेरिकों का उपयोग होने पर प्रकट होने की संभावना बहुत अधिक है। यह पता चला है कि TControl.CreateParams में एक बफर ओवरफ्लो बग है। यदि आप कोड देखते हैं, तो आप देखेंगे कि यह एक TCreateParams संरचना को भरता है, और विशेष रूप से महत्वपूर्ण है, यह वर्तमान कक्षा (कक्षा नाम) के नाम से TCreateParams.WinClassName भरता है। दुर्भाग्यवश WinClassName केवल 64 char के एक निश्चित लंबाई बफर है, लेकिन इसमें पूर्ण टर्मिनेटर शामिल करने की आवश्यकता है; इतनी प्रभावी रूप से एक 64 चार लंबे ClassName उस बफर को बह जाएगा!

इस कोड के साथ इसका परीक्षण किया जा सकता है:

TLongWinControlClassName4567890123456789012345678901234567891234 = class(TWinControl)
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  with TLongWinControlClassName4567890123456789012345678901234567891234.Create(Self) do
  begin
    Parent := Self;
  end;
end;

वह वर्ग का नाम बिल्कुल 64 वर्ण लंबा है। इसे एक चरित्र छोटा बनाएं और त्रुटि दूर हो जाए!

डेल्फी ClassName निर्माण करने के तरीके के कारण जेनेरिक का उपयोग करते समय यह बहुत अधिक होने की संभावना है: इसमें इकाई नाम शामिल है जहां पैरामीटर प्रकार घोषित किया गया है, साथ ही एक बिंदु, फिर पैरामीटर प्रकार का नाम। उदाहरण के लिए, TWinControl<TWinControl, TWinControl, TWinControl> कक्षा में निम्न TWinControl<TWinControl, TWinControl, TWinControl> :

TWinControl<Controls.TWinControl,Controls.TWinControl,Controls.TWinControl>

63 सीमाओं पर 75 वर्ण लंबा है।

वैकल्पिक हल

मैंने संभावित-त्रुटि-उत्पन्न वर्ग से एक साधारण त्रुटि संदेश अपनाया। इस तरह कुछ, कन्स्ट्रक्टर से:

constructor TWinControl<T, K, W>.Create(aOwner: TComponent);
begin
  {$IFOPT D+}
  if Length(ClassName) > 63 then raise Exception.Create('The resulting ClassName is too    long: ' + ClassName);
  {$ENDIF}
  inherited;
end;

कम से कम यह एक सभ्य त्रुटि संदेश दिखाता है कि कोई तुरंत कार्य कर सकता है।

बाद में संपादित करें, सही वर्कअराउंड

पिछला समाधान (एक त्रुटि उठाना) गैर-जेनेरिक वर्ग के लिए ठीक काम करता है जिसमें वास्तविक-वास्तविक लंबा नाम होता है; कोई भी इसे कम करने में सक्षम होगा, इसे 63 वर्ण या उससे कम बना देगा। जेनेरिक प्रकारों के साथ यह मामला नहीं है: मैं इस समस्या में एक TWINControl वंश के साथ भाग गया जिसमें 2 प्रकार के पैरामीटर थे, इसलिए यह फ़ॉर्म का था:

TMyControlName<Type1, Type2>

इस जेनेरिक प्रकार के आधार पर एक ठोस प्रकार के लिए जेनरेट क्लासनाम फॉर्म लेता है:

TMyControlName<UnitName1.Type1,UnitName2.Type2>

इसलिए इसमें 5 पहचानकर्ता (2x इकाई पहचानकर्ता + 3x प्रकार पहचानकर्ता) + 5 प्रतीकों ( <.,.> ) शामिल हैं; उन 5 पहचानकर्ताओं की औसत लंबाई 12 वर्णों से कम होने की आवश्यकता है, या फिर कुल लंबाई 63: 5x12 + 5 = 65 से अधिक है। प्रति पहचानकर्ता केवल 11-12 वर्णों का उपयोग करना बहुत कम है और सर्वोत्तम प्रथाओं (यानी: लंबे वर्णनात्मक नामों का उपयोग करें क्योंकि कीस्ट्रोक मुक्त हैं!)। फिर, मेरे मामले में, मैं बस अपने पहचानकर्ताओं को कम नहीं कर सका।

ClassName को कितना छोटा करना हमेशा संभव नहीं है, मुझे लगा कि मैं समस्या के कारण को हटाने का प्रयास करूंगा (बफर ओवरफ्लो)। दुर्भाग्य से यह बहुत मुश्किल है क्योंकि त्रुटि CreateParams पदानुक्रम के नीचे TWinControl.CreateParams से उत्पन्न होती है। हम inherited कॉल नहीं कर सकते हैं क्योंकि विंडो निर्माण पैरामीटर बनाने के लिए विरासत श्रृंखला के साथ CreateParams का उपयोग किया जाता है। इसे कॉल करने के लिए आधार TWinControl.CreateParams में सभी कोड डुप्लिकेट करने की आवश्यकता होगी। TWinControl.CreateParams प्लस मध्यस्थ कक्षाओं में सभी कोड प्लस करें; यह बहुत पोर्टेबल भी नहीं होगा, क्योंकि उस कोड में से कोई भी VCL भविष्य के संस्करणों के साथ बदल सकता है (या तीसरे पक्ष के नियंत्रण के भविष्य के संस्करण जो हम सबक्लासिंग कर सकते हैं)।

निम्न समाधान TWinControl.CreateParams को बफर को बहने से नहीं रोकता है, लेकिन यह हानिरहित बनाता है और फिर (जब inherited कॉल रिटर्न) समस्या को हल करता है। मैं एक नया रिकॉर्ड उपयोग कर रहा हूं (इसलिए मेरे पास लेआउट पर नियंत्रण है) जिसमें मूल TCreateParams लेकिन इसे TWinControl.CreateParams लिए बहुत अधिक जगहों के साथ पैड करना है। TWinControl.CreateParams चाहता है उसे बहती है, फिर मैं पूरा पाठ पढ़ता हूं और इसे बनाता हूं ताकि यह रिकॉर्ड की मूल सीमाओं को फिट कर सके और यह भी सुनिश्चित कर सके कि परिणामस्वरूप छोटा नाम उचित रूप से अद्वितीय होने की संभावना है। मैं विशिष्टता मुद्दे के साथ मदद करने के लिए WndName में मूल क्लासनाम का एक HASH शामिल कर रहा हूं:

type
  TWrappedCreateParamsRecord = record
    Orignial: TCreateParams;
    SpaceForCreateParamsToSafelyOverflow: array[0..2047] of Char;
  end;

procedure TExtraExtraLongWinControlDescendantClassName_0123456789_0123456789_0123456789_0123456789.CreateParams(var Params: TCreateParams);
var Wrapp: TWrappedCreateParamsRecord;
    Hashcode: Integer;
    HashStr: string;
begin
  // Do I need to take special care?
  if Length(ClassName) >= Length(Params.WinClassName) then
    begin
      // Letting the code go through will cause an Access Violation because of the
      // Buffer Overflow in TWinControl.CreateParams; Yet we do need to let the
      // inherited call go through, or else parent classes don't get the chance
      // to manipulate the Params structure. Since we can't fix the root cause (we
      // can't stop TWinControl.CreateParams from overflowing), let's make sure the
      // overflow will be harmless.
      ZeroMemory(@Wrapp, SizeOf(Wrapp));
      Move(Params, Wrapp.Orignial, SizeOf(TCreateParams));
      // Call the original CreateParams; It'll still overflow, but it'll probably be hurmless since we just
      // padded the orginal data structure with a substantial ammount of space.
      inherited CreateParams(Wrapp.Orignial);
      // The data needs to move back into the "Params" structure, but before we can do that
      // we should FIX the overflown buffer. We can't simply trunc it to 64, and we don't want
      // the overhead of keeping track of all the variants of this class we might encounter.
      // Note: Think of GENERIC classes, where you write this code once, but there might
      // be many-many different ClassNames at runtime!
      //
      // My idea is to FIX this by keeping as much of the original name as possible, but
      // including the HASH value of the full name into the window name; If the HASH function
      // is any good then the resulting name as a very high probability of being Unique. We'll
      // use the default Hash function used for Delphi's generics.
      HashCode := TEqualityComparer<string>.Default.GetHashCode(PChar(@Wrapp.Orignial.WinClassName));
      HashStr := IntToHex(HashCode, 8);
      Move(HashStr[1], Wrapp.Orignial.WinClassName[High(Wrapp.Orignial.WinClassName)-8], 8*SizeOf(Char));
      Wrapp.Orignial.WinClassName[High(Wrapp.Orignial.WinClassName)] := #0;
      // Move the TCreateParams record back were we've got it from
      Move(Wrapp.Orignial, Params, SizeOf(TCreateParams));
    end
  else
    inherited;
end;




buffer-overflow