C ++ จับข้อยกเว้นทั้งหมด


244

มี c ++ เทียบเท่าของ Java หรือไม่

try {
    ...
}
catch (Throwable t) {
    ...
}

ฉันพยายามดีบักโค้ด Java / jni ที่เรียกใช้ฟังก์ชัน windows ดั้งเดิมและเครื่องเสมือนหยุดทำงาน รหัสเนทีฟดูเหมือนดีในการทดสอบหน่วยและดูเหมือนว่าจะพังเมื่อเรียกผ่าน jni กลไกการยกเว้นแบบทั่วไปจะพิสูจน์ว่ามีประโยชน์มาก



2
โปรดทราบว่าการขัดข้องส่วนใหญ่ไม่ได้เกิดจากข้อยกเว้นใน C ++ คุณสามารถตรวจจับข้อยกเว้นทั้งหมด แต่นั่นจะไม่ป้องกันการล่มจำนวนมาก
Mooing Duck

คำตอบ:


335
try{
    // ...
} catch (...) {
    // ...
}

จะจับข้อยกเว้น C ++ ทั้งหมด แต่ควรพิจารณาการออกแบบที่ไม่ดี คุณสามารถใช้กลไก current_exception ใหม่ของ c ++ 11 แต่ถ้าคุณไม่มีความสามารถในการใช้ c ++ 11 (ระบบรหัสดั้งเดิมที่ต้องการการเขียนซ้ำ) คุณจะไม่มีตัวชี้ข้อยกเว้นที่มีชื่อเพื่อใช้รับข้อความหรือชื่อ . คุณอาจต้องการเพิ่มส่วนคำสั่ง catch แยกต่างหากสำหรับข้อยกเว้นต่างๆที่คุณสามารถตรวจจับได้และจะจับเฉพาะทุกสิ่งที่ด้านล่างเพื่อบันทึกข้อยกเว้นที่ไม่คาดคิด เช่น:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
เป็นวิธีปฏิบัติที่ดีในการตรวจจับข้อยกเว้นโดยอ้างอิง const เช่นเดียวกับใน: catch (std :: exception const & ex) {/ * ... * /}
coryan

12
@ คอรี่: ทำไมมันเป็นวิธีที่ดีที่จะจับโดยอ้างอิง const?
ทิม MB

19
การหลีกเลี่ยงการทำสำเนาโดยไม่จำเป็นเป็นประโยชน์อย่างหนึ่ง
Greg D

21
-1: ข้อเสนอแนะที่ว่า "จะจับข้อยกเว้นทั้งหมดใน C ++" นี้ทำให้เข้าใจผิด ลองสร้างหารด้วยศูนย์ข้อผิดพลาดภายในลองบล็อก คุณจะเห็นว่ามันจะสร้างข้อยกเว้นที่ไม่ถูกจับ แต่รหัสนั้นชัดเจนใน C ++ มันจะมีประโยชน์มากขึ้นในการระบุว่าสิ่งนี้จะ "ตรวจจับข้อยกเว้น C ++ ทั้งหมด" แล้วเพิ่มการกล่าวถึงข้อยกเว้นเชิงโครงสร้างบางอย่างลงในบันทึกย่อที่มีประโยชน์ จำกัด
omatai

42
@omatai: แก้ไขแล้วจะจับข้อยกเว้น C ++ ทั้งหมด การหารด้วยศูนย์คือพฤติกรรมที่ไม่ได้กำหนดและไม่สร้างข้อยกเว้น C ++
Mooing Duck

151

บางคนควรเพิ่มที่ไม่สามารถจับ "ล่ม" ในรหัส C ++ คนเหล่านั้นไม่ได้ทำข้อยกเว้น แต่ทำอะไรก็ได้ที่พวกเขาชอบ เมื่อคุณเห็นโปรแกรมหยุดทำงานเนื่องจากการกล่าวถึงข้อผิดพลาดแบบ null-pointer มันเป็นการทำงานที่ไม่ได้กำหนด std::null_pointer_exceptionไม่มี การพยายามจับข้อยกเว้นจะไม่ช่วย

ในกรณีที่มีคนอ่านกระทู้นี้และคิดว่าเขาสามารถได้รับสาเหตุของการขัดข้องของโปรแกรม ควรใช้ Debugger เช่น gdb แทน


4
อย่างที่ Shy ชี้ให้เห็นมันเป็นไปได้ที่คอมไพเลอร์ VC ไม่ใช่ความคิดที่ดี แต่เป็นไปได้
Shog9

7
ใช่กับ SEH แต่ไม่ได้มีสติ c ++ มาตรฐานเทคนิค :) ดีถ้าคุณติดกับหน้าต่างที่คุณสามารถทำทุกอย่างเกือบ :)
โยฮันเน Schaub - litb

1
อืม ... ขอบคุณสำหรับอาหารอันโอชะนี้ ฉันกำลังหาคำตอบว่าทำไมข้อยกเว้นตัวชี้โมฆะของฉันถึงไม่ถูกจับ!
Dalin Seivewright

10
คุณสามารถจับ segfaults ด้วย SEH บน Windows และสัญญาณ (2) / sigaction (2) บนระบบ POSIX ซึ่งครอบคลุมระบบส่วนใหญ่ที่ใช้อยู่ในปัจจุบัน แต่ก็เหมือนกับการจัดการข้อยกเว้นไม่ใช่สิ่งที่ควรใช้สำหรับการควบคุมการไหลปกติ มันเป็นมากกว่า "ทำสิ่งที่มีประโยชน์ก่อนตาย"
Adam Rosenfield

1
@ AdamRosenfield จนกว่าคุณจะดำเนินการtry { .. } catch(...) { ... }เพื่อจับโดยใช้สัญญาณ / sigaction ฉันจะไม่เรียกมันว่า "การจับ" :) ถ้าในตัวจัดการสัญญาณมันค่อนข้างยากสำหรับโปรแกรมเมอร์ที่จะรู้ว่ารหัสผิดพลาดเกิดขึ้นที่ไหน (ฉันกำลังพูด เกี่ยวกับการตรวจจับโดยทางโปรแกรม) เปรียบเทียบกับลอง / จับ
Johannes Schaub - litb

72

นี่คือวิธีที่คุณสามารถทำวิศวกรรมย้อนกลับประเภทข้อยกเว้นจากภายในcatch(...)หากคุณต้องการ (อาจมีประโยชน์เมื่อไม่รู้จักจากห้องสมุดบุคคลที่สาม) ด้วย GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

และถ้าคุณสามารถจ่ายได้โดยใช้Boostคุณสามารถทำให้ส่วนที่จับได้ง่ายขึ้น (ด้านนอก) และข้ามแพลตฟอร์ม

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

โปรดทราบว่า...ภายในcatchคือจุดไข่ปลาจริง สามจุด

อย่างไรก็ตามเนื่องจากข้อยกเว้น C ++ ไม่จำเป็นต้องเป็นคลาสย่อยของคลาสพื้นฐานExceptionจึงไม่มีวิธีใดที่จะเห็นตัวแปรข้อยกเว้นที่ถูกโยนทิ้งเมื่อใช้โครงสร้างนี้


24
ใน C ++ 11 มี: ลอง {std :: string (). ที่ (1); // นี่สร้าง std :: out_of_range} catch (... ) {eptr = std :: current_exception (); // capture}
Mohammad Alaggan

2
@bfontaine: ใช่ แต่ฉันบอกว่าจะแยกความแตกต่างตัวcatchระบุจากตัวยึดรหัสที่มีอยู่ในความคิดเห็น ( // ...) ซึ่งเห็นได้ชัดว่าไม่ใช่ไวยากรณ์ C ++
Greg Hewgill

1
@GregHewgill: ใช่มันเป็นเพียงแค่การพิมพ์ nitpicking
bfontaine

1
@bfontaine: ยุติธรรมพอ :)
Greg Hewgill

44

เป็นไปไม่ได้ (ใน C ++) เพื่อตรวจจับข้อยกเว้นทั้งหมดในลักษณะพกพา นี่เป็นเพราะข้อยกเว้นบางอย่างไม่ใช่ข้อยกเว้นในบริบท C ++ ซึ่งรวมถึงสิ่งต่าง ๆ เช่นการหารด้วยศูนย์ข้อผิดพลาดและอื่น ๆ เป็นไปได้ที่จะแฮ็กและทำให้สามารถโยนข้อยกเว้นได้เมื่อเกิดข้อผิดพลาด แต่มันไม่ง่ายที่จะทำและแน่นอนว่าไม่ใช่เรื่องง่ายในการพกพา

ถ้าคุณต้องการจับข้อยกเว้น STL ทั้งหมดคุณสามารถทำได้

try { ... } catch( const std::exception &e) { ... }

ซึ่งจะช่วยให้คุณใช้e.what()ซึ่งจะส่งกลับconst char*ซึ่งสามารถบอกคุณเพิ่มเติมเกี่ยวกับข้อยกเว้นตัวเอง นี่คือโครงสร้างที่คล้ายกับโครงสร้าง Java ที่คุณถามถึงมากที่สุด

std::exceptionนี้จะไม่ช่วยให้คุณถ้ามีใครพอโง่โยนข้อยกเว้นที่ไม่ได้รับมรดกจาก


2
ทำไมนี่ไม่อยู่ในอันดับต้น ๆ ?
Ivan Sanz-Carasa

31

catch(...)ในระยะสั้นการใช้งาน อย่างไรก็ตามโปรดทราบว่าcatch(...)ควรใช้ควบคู่กับthrow;โดยทั่วไป:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

catch(...)วิธีนี้เป็นวิธีที่เหมาะสมในการใช้งาน


6
จะดีกว่าการใช้ RAII สำหรับการจัดการหน่วยความจำที่จัดการสถานการณ์ข้อยกเว้นนี้โดยอัตโนมัติ
paykoob

1
@paykoob วิธีจัดการกรณีที่คุณจัดการเพื่อสร้าง foo ใหม่ แต่มันล้มเหลวบนแถบ หรือเมื่อตัวสร้างแท่งพยายามเปิดไฟล์ แต่ล้มเหลวจึงเกิดการโยน จากนั้นคุณอาจท้ายด้วย fangel dangeling
Mellester

2
@Melelterk สแต็คจะไม่ได้รับการทำความสะอาดในกรณีนั้นซึ่งจะทำงานFooของ destructor หรือไม่ ฉันคิดว่านั่นเป็นจุดรวมของ RAII อย่างไรก็ตามหากคุณต้องการตัวชี้ไปที่Fooแทนที่จะสร้างFooบนสแต็กคุณจะต้องล้อมตัวชี้ในสิ่งอื่นที่ประกาศไว้บนสแต็ก
reirab

ใช่ auto foo = std :: make_unique <Foo> (); auto bar = std :: make_unique <Bar> (); // เป็นข้อยกเว้นที่ปลอดภัยและจะไม่รั่วไหลไม่ต้องมีการจับ (... )
paulm

คำตอบนี้สมควรได้รับการลงคะแนนถ้าเพียงสำหรับการสนทนาก็เริ่ม :)
Cristik

21

เป็นไปได้ที่จะทำสิ่งนี้โดยเขียน:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

แต่มีความเสี่ยงที่ไม่สามารถสังเกตเห็นได้ที่นี่: คุณไม่สามารถหาประเภทของข้อผิดพลาดที่ถูกโยนเข้าไปในtryบล็อกได้ดังนั้นให้ใช้ชนิดนี้catchเมื่อคุณแน่ใจว่าไม่ว่าชนิดของข้อยกเว้นคืออะไรโปรแกรมจะต้องคงอยู่ ตามวิธีที่กำหนดไว้ในcatchบล็อก


31
ฉันหวังว่าคุณจะได้รับตราสำหรับการตอบคำถามเกือบ 5 ปีหลังจากได้รับคำตอบที่ดีกว่า!

4
@DrEval ตามที่คุณต้องการ;) stackoverflow.com/help/badges/17/necromancer?userid=2580505
Andreas

18

คุณสามารถใช้ได้

catch(...)

แต่มันอันตรายมาก ในหนังสือของเขาDebugging Windows , John Robbins บอกเล่าเรื่องราวสงครามเกี่ยวกับข้อผิดพลาดที่น่ารังเกียจจริงๆซึ่งถูกหลอกลวงโดยคำสั่ง catch (... ) คุณทำได้ดีกว่ามากในการจับข้อยกเว้นเฉพาะ ตรวจสอบสิ่งที่คุณคิดว่าบล็อกลองของคุณอาจมีเหตุผลพอสมควร แต่ให้โค้ดโยนข้อยกเว้นให้สูงกว่านี้หากมีสิ่งที่ไม่คาดคิดเกิดขึ้นจริง


1
ฉันเพิ่งได้เห็นการใช้งานของสิ่งเหล่านี้และมีการบันทึกในขั้นตอนนั้น การไม่ทำอะไรเลยยกเว้นเป็นการขอปัญหาอย่างแน่นอน
jxramos

15

ให้ฉันพูดถึงสิ่งนี้ที่นี่: Java

try 
{
...
}
catch (Exception e)
{
...
}

อาจไม่ได้รับการยกเว้นทั้งหมด! อันที่จริงฉันเคยมีเรื่องแบบนี้เกิดขึ้นมาก่อนและมันก็เป็นสิ่งกระตุ้นอารมณ์ฉุนเฉียว ข้อยกเว้นมาจาก Throwable ดังนั้นอย่างแท้จริงที่จะจับทุกอย่างคุณไม่ต้องการที่จะจับข้อยกเว้น; คุณต้องการที่จะจับ Throwable

ฉันรู้ว่ามันฟังดูไร้ค่า แต่เมื่อคุณใช้เวลาหลายวันในการพยายามหาจุดที่ "ข้อยกเว้นที่ไม่ถูกตรวจจับ" มาจากรหัสที่ล้อมรอบไปด้วยลอง ... catch (Exception e) "บล็อกมาจาก คุณ.


2
แน่นอนคุณไม่ควรจับวัตถุข้อผิดพลาด - ถ้าคุณควรจะจับพวกมันพวกเขาจะเป็นข้อยกเว้น วัตถุข้อผิดพลาดเป็นสิ่งที่ร้ายแรงอย่างสมบูรณ์เช่นวิ่งออกมาจากพื้นที่อื่น ๆ กอง
SCDF

1
ทั้งข้อยกเว้นรันไทม์ซึ่งเป็นส่วนใหญ่ยกเว้น GoodProgrammerExpected !!!
OscarRyz

3
เรามีข้อผิดพลาดที่ร้ายแรงมากที่เกิดจากการจับ OutOfMemoryError เนื่องจากบล็อก (Throwable) ที่จับแทนที่จะปล่อยให้มันฆ่าสิ่งต่าง ๆ ...
Trejkaz

1
แน่นอนcatch(Exception)อาจจะไม่จับข้อยกเว้นทั้งหมดใน Java คุณได้รับมันผสมกับ C # Java ... = catch(Thowable), C # catch(Exception)= อย่าทำให้พวกเขาสับสน
เชฟฟาโรห์

2
@OscarRyz เสียงที่เหมือนCoderMalfunctionError(ซึ่งเป็นจริงจริง Java Errorsubclass ... แม้ว่ามันจะไม่ได้หมายความว่าสิ่งที่ดูเหมือน.)
reirab

9

ถ้าคุณต้องการที่จะตรวจจับข้อยกเว้นทั้งหมดเพื่อสร้าง minidump เช่น ...

บางคนทำงานบน Windows

ดูhttp://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus ในบทความเขาอธิบายว่าเขาพบวิธีการจับข้อยกเว้นทุกประเภทและเขาให้รหัสที่ทำงานได้

นี่คือรายการที่คุณสามารถจับได้:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

และการใช้งาน: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // ทำสิ่งนี้เพื่อหนึ่งเธรด ch.SetThreadExceptionHandlers (); // สำหรับแต่ละ thred


โดยค่าเริ่มต้นสิ่งนี้จะสร้าง minidump ในไดเรกทอรีปัจจุบัน (crashdump.dmp)


4

กลไกการยกเว้นแบบทั่วไปจะพิสูจน์ว่ามีประโยชน์มาก

น่าสงสัย คุณรู้แล้วว่ารหัสของคุณเสียเพราะขัดข้อง การกินข้อยกเว้นอาจปกปิดสิ่งนี้ได้ แต่นั่นอาจส่งผลให้เกิดข้อผิดพลาดที่ลึกซึ้งยิ่งขึ้น

สิ่งที่คุณต้องการจริงๆคือเครื่องมือดีบั๊ก ...


13
ฉันไม่เห็นด้วยมีหลายกรณีในแอปพลิเคชันแบบเรียลไทม์ที่ฉันต้องการรับข้อยกเว้นที่ไม่รู้จักเขียนอะไรลงในบันทึก / ติดตามข้อผิดพลาดทั่วไปของการกระทำแน่นอนแทนที่จะปล่อยให้แอปพลิเคชันหยุดทำงาน
f0ster

3
ฉันค่อนข้างสงสัยว่าคุณกำลังนึกถึงกรณีที่คุณสามารถทำตามแนวทางข้อผิดพลาดทั่วไปโดยไม่สนใจกรณีที่กองขยะถูกทิ้งหรือหน่วยความจำหมดและการจัดการข้อผิดพลาดทั่วไปก็ไม่ประสบความสำเร็จเช่นกัน ไม่มีอะไรผิดปกติกับการจับข้อผิดพลาดที่คุณสามารถกู้คืนได้ แต่ IMHO catch-all ควรมีอยู่อย่างโดดเดี่ยวเท่านั้น (สแต็กแยกหน่วยความจำที่จัดสรรล่วงหน้า) ตรรกะที่เขียนอย่างระมัดระวังที่เรียกว่าก่อนที่จะสิ้นสุดโปรแกรม หากคุณไม่ทราบว่าปัญหาคืออะไรคุณไม่สามารถมั่นใจได้ว่าจะสามารถแก้ไขได้
Shog9

1
นั่นคือติดตั้งตัวจัดการสัญญาณซึ่งคลายบันทึกบางอย่างที่คุณสร้างระหว่างรันไทม์เพื่อหาว่าโปรแกรมเกิดข้อผิดพลาดและหวังว่าทำไม
ชัดเจน

3
  1. คุณสามารถรัน JNI โดยใช้แอ็พพลิเคชัน Java จากหน้าต่างคอนโซล (เรียกใช้จากบรรทัดรับคำสั่ง java) เพื่อดูว่ามีรายงานใด ๆ ที่ตรวจพบก่อนที่ JVM จะล้มเหลวหรือไม่ เมื่อรันโดยตรงเป็นแอ็พพลิเคชันหน้าต่าง Java คุณอาจหายไปข้อความที่จะปรากฏขึ้นหากคุณรันจากหน้าต่างคอนโซลแทน

  2. ประการที่สองคุณสามารถเริ่มต้นการใช้งาน JNI DLL เพื่อแสดงให้เห็นว่าวิธีการใน DLL ของคุณกำลังถูกป้อนจาก JNI คุณกลับมาอย่างถูกต้องหรือไม่?

  3. ในกรณีที่ปัญหาเกิดขึ้นจากการใช้วิธีการ JNI-interface ที่ไม่ถูกต้องจากรหัส C ++ คุณได้ตรวจสอบแล้วว่าตัวอย่าง JNI แบบเรียบง่ายบางส่วนรวบรวมและทำงานกับการตั้งค่าของคุณหรือไม่ ฉันคิดโดยเฉพาะการใช้วิธีการ JNI - interface สำหรับการแปลงพารามิเตอร์เป็นรูปแบบ C ++ ดั้งเดิมและเปลี่ยนผลการทำงานเป็นประเภท Java มันจะมีประโยชน์ในการ stub เหล่านั้นเพื่อให้แน่ใจว่าการแปลงข้อมูลใช้งานได้และคุณจะไม่ยุ่งเหยิงในการโทรที่เหมือน COM ในอินเตอร์เฟส JNI

  4. มีสิ่งอื่น ๆ ให้ตรวจสอบ แต่เป็นการยากที่จะแนะนำใด ๆ โดยไม่ทราบเพิ่มเติมเกี่ยวกับวิธีการที่ Java ดั้งเดิมของคุณคืออะไรและการใช้ JNI ของพวกเขากำลังพยายามทำอะไร ไม่ชัดเจนว่าการจับข้อยกเว้นจากระดับรหัส C ++ เกี่ยวข้องกับปัญหาของคุณ (คุณสามารถใช้อินเตอร์เฟส JNI เพื่อสร้างข้อยกเว้นใหม่เป็น Java หนึ่ง แต่ไม่ชัดเจนจากสิ่งที่คุณระบุว่าสิ่งนี้จะช่วยได้)


2

สำหรับปัญหาที่แท้จริงเกี่ยวกับการไม่สามารถดีบักโปรแกรมที่ใช้ JNI อย่างถูกต้อง (หรือข้อผิดพลาดไม่ปรากฏขึ้นเมื่อเรียกใช้ภายใต้ดีบักเกอร์):

ในกรณีนี้มักจะช่วยในการเพิ่ม Java wrappers รอบการโทร JNI ของคุณ (เช่นวิธีดั้งเดิมทั้งหมดเป็นวิธีส่วนตัวและวิธีสาธารณะของคุณในชั้นเรียนเรียกพวกเขา) ที่ทำการตรวจสอบสติพื้นฐาน (ตรวจสอบว่า "วัตถุ" ทั้งหมดเป็นอิสระและ "วัตถุ" ไม่ได้ใช้หลังจากพ้น) หรือการซิงโครไนซ์ (เพียงซิงโครไนซ์วิธีทั้งหมดจาก DLL หนึ่งไปยังอินสแตนซ์วัตถุเดียว) ให้วิธีการ wrapper java บันทึกข้อผิดพลาดและโยนข้อยกเว้น

สิ่งนี้มักจะช่วยในการค้นหาข้อผิดพลาดจริง (ซึ่งน่าแปลกใจส่วนใหญ่ในโค้ด Java ที่ไม่เชื่อฟัง semantics ของฟังก์ชั่นที่เรียกว่าก่อให้เกิดคู่ที่น่ารังเกียจหรือคล้ายกัน) ได้ง่ายกว่าการพยายามดีบักโปรแกรม Java ดีบักเกอร์ดั้งเดิม ...

หากคุณทราบสาเหตุให้เก็บรหัสไว้ในวิธีการห่อหุ้มที่จะหลีกเลี่ยง วิธีการห่อหุ้มของคุณดีกว่าโยนข้อยกเว้นกว่ารหัส JNI ของคุณผิดพลาด VM ...


1

อย่างนี้ขึ้นอยู่กับสภาพแวดล้อมของคอมไพเลอร์ gcc ไม่พบสิ่งเหล่านี้ Visual Studio และ Borland สุดท้ายที่ฉันเคยใช้

ดังนั้นข้อสรุปเกี่ยวกับการล่มจึงขึ้นอยู่กับคุณภาพของสภาพแวดล้อมการพัฒนาของคุณ

ข้อมูลจำเพาะ C ++ บอกว่า catch (... ) ต้องจับข้อยกเว้นใด ๆ แต่ไม่ได้ในทุกกรณี

อย่างน้อยจากสิ่งที่ฉันพยายาม


1

ระวัง

try{
// ...
} catch (...) {
// ...
}

จับข้อยกเว้นระดับภาษาเท่านั้นยกเว้น / ข้อผิดพลาดระดับต่ำอื่น ๆ เช่นAccess ViolationและSegmentation Faultจะไม่ถูกจับ


สิ่งต่าง ๆ เช่นการแบ่งส่วนความผิดพลาดไม่ใช่ข้อยกเว้นจริง ๆ แล้วเป็นสัญญาณ ดังนั้นคุณไม่สามารถจับพวกมันเหมือนข้อยกเว้นทั่วไป แต่มีวิธีการแก้ปัญหาบางอย่างเช่นนี้
MAChitgarha
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.