TN058: MFC โมดูสถานะการใช้งาน

หมายเหตุทางเทคนิคนี้อธิบายการใช้งานโครงสร้าง MFC "รัฐโมดูล" ความเข้าใจเกี่ยวกับสถานะการใช้งานโมดูลเป็นสิ่งสำคัญสำหรับใช้ MFC ใน Dll จาก DLL ที่ใช้ร่วมกัน (หรือ OLE เซิร์ฟเวอร์อยู่ระหว่างดำเนินการ)?

ก่อนที่จะอ่านหมายเหตุนี้ โปรดดูที่ "การจัดการสถานะข้อมูลของ MFC Modules" ใน การสร้างเอกสารใหม่ Windows และมุมมองในการแนะนำของ Visual c ++ Programmer ของ บทความนี้ประกอบด้วยข้อมูลการใช้งานที่สำคัญและรวมข้อมูลเกี่ยวกับเรื่องนี้?

ภาพรวม

มีอยู่สามชนิดข้อมูลสถานะ MFC: สถานะของโมดูล กระบวนการรัฐ และ สถานะกลุ่มหัวข้อ บางชนิดสถานะเหล่านี้สามารถรวม ตัวอย่างเช่น MFC ของหมายเลขอ้างอิงแผนที่ได้ทั้งภายในโมดูลและเธรดภายใน นี้ทำให้โมดูลต่าง ๆ ที่สอง มีแผนที่แตกต่างกันในแต่ละหัวข้อของตน?

สถานะของกระบวนการและสถานะกลุ่มหัวข้อได้เหมือนกัน รายการข้อมูลเหล่านี้เป็นสิ่งที่ได้ตามเกณฑ์รับตัวแปรส่วนกลาง แต่มีจำเป็นโดยเฉพาะกระบวนการที่กำหนด หรือเธรด สำหรับสนับสนุน Win32s ที่เหมาะสม หรือ สำหรับการสนับสนุนมัลติเธรดที่เหมาะสม ประเภทใดรายการข้อมูลที่กำหนดให้พอดีในขึ้นอยู่กับว่ารายการและความหมายของที่ต้องเกี่ยวข้องกับขอบเขตของกระบวนการและหัวข้อ?

โมดูสถานะไม่ซ้ำในที่นั้นสามารถประกอบด้วยสถานะส่วนรวมอย่างแท้จริงหรือรัฐที่เป็นกระบวนการภายในหรือภายในเธรด นอกจากนี้ ก็สามารถจะสลับอย่างรวดเร็ว?

สลับสถานะของโมดูล

แต่ละหัวข้อที่ประกอบด้วยตัวชี้ไปยังสถานะของโมดูล "ปัจจุบัน" หรือ "ทำงาน" (ไม่จู่ ๆ ตัวชี้เป็นส่วนหนึ่งของรัฐภายในเธรดของ MFC) ตัวชี้จะเปลี่ยนแปลงเมื่อเธรดปฏิบัติการผ่านขอบโมดูล เช่นโปรแกรมประยุกต์ที่เรียกเข้าตัว OLE ควบคุม หรือ DLL หรือการควบคุม OLE เรียกกลับลงในโปรแกรมประยุกต์?

สถานะปัจจุบันของโมดูลจะสลับ โดยการเรียกAfxSetModuleState ส่วนใหญ่ คุณจะไม่เคยจัดการกับ API แบบโดยตรง MFC ใหญ่ จะเรียกว่าสำหรับคุณ (ที่ WinMain, OLE-จุดAfxWndProcฯลฯ) สามารถทำได้ในคอมโพเนนต์ใด ๆ ที่คุณเขียน ด้วยการเชื่อมโยงคอนในแบบพิเศษWndProcและพิเศษWinMain (หรือDllMain) ที่รู้สถานะของโมดูลที่ควรเป็นปัจจุบัน คุณสามารถดูรหัสนี้ได้ โดยใช้เวลาดูที่ DLLMODULCPP หรือ APPMODULCPP ในไดเรกทอรี MFC\SRC?

มันไม่ค่อยพบว่า คุณต้องการตั้งค่าโมดูสถานะแล้ว ไม่ตั้งกลับ เวลาคุณต้องการ "ดัน" โมดูลของตนเองมากที่สุดรัฐเป็นปัจจุบันแล้ว หลังจากที่คุณเสร็จแล้ว "pop" เนื้อหาต้นฉบับกลับ ทำได้ โดยใช้แมโคAFX_MANAGE_STATEและชั้นเรียนพิเศษAFX_MAINTAIN_STATE?

CCmdTargetมีคุณลักษณะพิเศษสำหรับสนับสนุนการสลับสถานะของโมดูล โดยเฉพาะอย่างยิ่งCCmdTargetเป็นชั้นรากใช้ OLE automation และ OLE COM จุด ชอบรายการจุดอื่น ๆ ตาก ระบบจุดเหล่านี้ต้องการตั้งค่าสถานะโมดูลที่ถูกต้อง วิธีการกำหนดCCmdTargetรู้จักสิ่งที่รัฐโมดูล "ถูกต้อง" ควร คำตอบคือว่า "จำ" โมดูสถานะ "ปัจจุบัน" คือเมื่อมันถูกสร้างขึ้น ซึ่งสามารถตั้งค่า โมดูสถานะปัจจุบันไปที่ "จำ" เรียกว่าค่าเมื่อเป็นรุ่นที่ใหม่กว่า ผล โมดูลรัฐที่เกี่ยวข้องกับวัตถุCCmdTargetกำหนด คือ รัฐโมดูลที่ปัจจุบันเมื่อวัตถุก่อสร้าง ลองมาทำเป็นตัวอย่างง่าย ๆ ของการโหลดที่เซิร์ฟเวอร์ INPROC สร้างวัตถุ และวิธีการในการโทร?

  1. DLL ที่โหลด โดยใช้ระบุว่า LoadLibrary OLE?

  2. การเรียกว่าRawDllMainเป็นครั้งแรก มันตั้งค่าสถานะโมดูลโมดูลคงทราบสถานะสำหรับ DLL ด้วยเหตุผลนี้RawDllMainคอนลิงค์กับ DLL?

  3. เรียกว่าการกำหนดสำหรับโรงงานคลาสเกี่ยวข้องกับวัตถุของเรา COleObjectFactoryได้รับมาจากCCmdTargetและผล มันจำสร้างอินในสถานะที่โมดูลจะถูกสแตนซ์ นี่คือสิ่งสำคัญ — เมื่อโรงงานคลาสคือขอให้สร้างวัตถุ มันรู้ตอนนี้รัฐโมดูลอะไรทำให้ปัจจุบัน?

  4. เรียกว่าDllGetClassObjectเพื่อขอรับโรงงานคลาส MFC รายโรงงานคลาสที่เกี่ยวข้องกับโมดูลนี้การค้นหา และส่งกลับค่าดังกล่าว?

  5. เรียกว่าCOleObjectFactory::XClassFactory2::CreateInstance ก่อนการสร้างวัตถุ และกลับมามัน ฟังก์ชันนี้ตั้งรัฐโมดูลโมดูสถานะที่ปัจจุบันอยู่ในขั้นตอนที่ 3 (หนึ่งในปัจจุบันเมื่อมีการสร้างอินสแตนซ์COleObjectFactory ) นี้เสร็จภายในของMETHOD_PROLOGUE?

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

  7. ไคลเอ็นต์เรียกฟังก์ชันบนวัตถุ OLE COM จะได้รับจากการเรียกCoCreateInstance เมื่อวัตถุถูกเรียก ใช้METHOD_PROLOGUEเพื่อสลับสถานะโมดูเหมือนไม่COleObjectFactory?

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

หมายเหตุว่า Dll, Dll "MFC Extension" โดยเฉพาะบางชนิดไม่สลับสถานะที่โมดูลในการRawDllMain (จริง พวกเขามักไม่แม้แต่จะเป็นRawDllMain) นี่เป็น เพราะพวกเขามีวัตถุประสงค์เพื่อ "เหมือน" เป็นจริงที่มีอยู่ในโปรแกรมประยุกต์ที่ใช้ในการทำงาน พวกเขามากส่วนหนึ่งของโปรแกรมประยุกต์ที่กำลังทำงานอยู่ และเป็นเจตนาของการปรับเปลี่ยนสถานะส่วนกลางของโปรแกรมประยุกต์?

OLE ควบคุมและ Dll อื่นได้แตกต่างกันมาก พวกเขาไม่ต้องการปรับเปลี่ยนสถานะของโปรแกรมประยุกต์ที่เรียก แอปพลิเคชันที่จะเรียกพวกเขาอาจแม้ไม่แอปพลิเคชัน MFC และดังนั้นอาจมีสถานะไม่มีการปรับเปลี่ยน นี่คือเหตุผลว่า โมดูลการสลับสถานะถูกอุปโลกน์?

สำหรับฟังก์ชันที่ส่งออกจาก DLL เช่นที่เปิดใช้กล่องโต้ตอบใน DLL ของคุณ คุณจำเป็นต้องเพิ่มรหัสต่อไปนี้ไปยังจุดเริ่มต้นของฟังก์ชัน:

AFX_MANAGE_STATE (AfxGetStaticModuleState ())

นี้อัตราดอกเบี้ยโมดูสถานะปัจจุบันกับรัฐกลับจากAfxGetStaticModuleStateในตอนท้ายของขอบเขตปัจจุบัน?

ปัญหาเกี่ยวกับทรัพยากรใน Dll จะเกิดขึ้นหากไม่มีใช้แมโคAFX_MODULE_STATE โดยค่าเริ่มต้น MFC ใช้ตัวจัดการทรัพยากรของโปรแกรมประยุกต์หลักโหลดแบบทรัพยากร แม่แบบนี้จะถูกเก็บไว้ใน DLL จริง ๆ สาเหตุหลักคือ ว่า ของ MFC โมดูข้อมูลสถานะได้ถูกไม่ถูกสลับ โดยแมโคAFX_MODULE_STATE หมายเลขอ้างอิงทรัพยากรถูกกู้คืนจากสถานะของโมดูลของ MFC ไม่สลับสถานะโมดูลทำให้ตัวจัดการทรัพยากรที่ไม่ถูกต้องที่จะใช้?

AFX_MODULE_STATEไม่จำเป็นต้องย้ายในทุกฟังก์ชันใน DLL ตัวอย่างเช่น InitInstance สามารถถูกเรียก โดยรหัส MFC ในแอพลิเคชันโดยไม่AFX_MODULE_STATEเนื่องจาก MFC กะรัฐโมดูก่อนโดยอัตโนมัติ InitInstance แล้วสลับกลับหลังจาก InitInstance ส่งกลับได้ เหมือนเป็นจริงสำหรับตัวจัดการการแมปข้อความทั้งหมด Dll ปกติมีกระบวนงานหน้าต่างต้นแบบพิเศษที่จะสลับสถานะโมดูก่อนกำหนดเส้นทางข้อความ จริง?

ข้อมูลภายในกระบวนการ

กระบวนการข้อมูลภายในเครื่องต้องไม่ได้เช่นกังวลมันไม่ได้เป็นความยากลำบากของรูปแบบ Win32s DLL ใน Win32s ทั้งหมด Dll แบ่งปันข้อมูลส่วนกลาง แม้ว่าจะโหลดโปรแกรมประยุกต์หลายโปรแกรม อยู่เต็มเปาแตกต่างจาก "จริง" Win32 DLL แบบข้อมูล ซึ่งแต่ละ DLL ได้รับสำเนาของพื้นที่ข้อมูลในแต่ละกระบวนการที่แนบกับ DLL แยกต่างหาก เมื่อต้องการเพิ่มระดับความซับซ้อน ข้อมูลที่ปันส่วนในกองใน Win32s DLL เป็นจริงกระบวนการเฉพาะ (อย่างน้อยเป็นไกลเป็นไปความเป็นเจ้าของ) พิจารณาข้อมูลและรหัสต่อไปนี้:

คง CString strGlobal / / ที่ขอบเขตของแฟ้ม

__declspec(dllexport) โมฆะ SetGlobalString(LPCTSTR lpsz)
{
   strGlobal = lpsz
}

__declspec(dllexport)
GetGlobalString (LPCTSTR lpsz, int cb) ถือเป็นโมฆะ
{
   lstrcpyn (lpsz, strGlobal, cb);
}

พิจารณาว่าเกิดอะไรขึ้นถ้ารหัสข้างในอยู่ใน DLL และว่า จะโหลด DLL โดยเป็นกระบวนการที่สอง A และ B ( ในความเป็นจริง เป็นสองอินสแตนซ์ของแอพลิเคชันเดียวกัน) สาย SetGlobalString("Hello from A") ผล การจัดสรรหน่วยความจำสำหรับข้อมูลCStringในบริบทของกระบวนการ A. Keep ในจิตใจว่าCStringตัวเองเป็นส่วนกลาง และมองเห็นทั้ง A และ b ขณะนี้ เรียก B GetGlobalString(sz, sizeof(sz)) B จะสามารถดูข้อมูลที่ตั้งค่า A นี่คือเนื่องจาก Win32s ให้มีการป้องกันระหว่างกระบวนการทำเหมือนไม่ Win32 เพื่อให้เป็นปัญหาแรก ในหลายกรณี ไม่พึงปรารถนาให้มีแอปพลิเคชันหนึ่งที่ส่งผลกระทบต่อข้อมูลส่วนกลางที่จะถือว่าเป็นของโปรแกรมประยุกต์ที่แตกต่างกัน?

แต่รอ — มีปัญหาเพิ่มเติม สมมติว่า ขณะนี้การออกจาก ออกเมื่อ A จาก หน่วยความจำที่ใช้งานโดย ' strGlobal ' สตริงที่มีอยู่สำหรับระบบ — นั่นคือ หน่วยความจำทั้งหมดที่จัดสรร โดย A กระบวนอยู่รอดโดยอัตโนมัติ โดยระบบปฏิบัติการ จะรอด เพราะ destructor CStringที่ถูกเรียกว่า มันยังไม่ถูกเรียกยัง มันอยู่รอดเพียงเนื่องจากแอพลิเคชันซึ่งปันส่วนนั้นได้ออกจากฉาก ตอนนี้ถ้าเรียกว่า B GetGlobalString(sz, sizeof(sz)) มันอาจไม่ได้รับข้อมูลที่ถูกต้องได้ โปรแกรมประยุกต์อื่นอาจใช้หน่วยความจำสำหรับสิ่งอื่น?

อย่างชัดเจนมีปัญหาต่อไปนี้ MFC 3.x ใช้เทคนิคเรียกว่าเก็บข้อมูลภายในเธรด (TLS) MFC 3.x จะปันส่วนดัชนีแบบ TLS ที่ภายใต้ Win32s จริง ๆ ทำหน้าที่เป็นดัชนีเก็บข้อมูลภายในการประมวลผล แม้ว่าจะไม่เรียกว่าที่ และจากนั้น จะอ้างอิงข้อมูลทั้งหมดโดยใช้ดัชนีที่ TLS นี้คล้ายคลึงกับดัชนี TLS ซึ่งถูกใช้เพื่อจัดเก็บข้อมูลภายในหัวข้อบน Win32 (ดูด้านล่างสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนั้น) ซึ่งเกิดจาก DLL MFC ทุกอย่างน้อยสอง TLS ดัชนีต่อกระบวนการการใช้งาน เมื่อคุณมีบัญชีสำหรับการโหลดจำนวนมาก OLE ควบคุม Dll (OCXs), คุณรวดเร็วเพียงพอดัชนี TLS (มีเฉพาะ 64) นอกจากนี้ MFC ได้เมื่อต้องการวางข้อมูลทั้งหมดนี้ที่เดียว ในโครงสร้างเดียว มันไม่สามารถขยายได้มาก และไม่เหมาะที่เกี่ยวข้องกับการใช้ดัชนี TLS?

MFC 4.x ที่อยู่นี้กับชุดแม่แบบของคลาสคุณสามารถ "ตัด" รอบข้อมูลที่ควรเป็นกระบวนการภายใน ตัวอย่าง ปัญหากล่าวถึงข้างต้นไม่สามารถแก้ไขได้ โดยการเขียน:

struct CMyGlobalData: CNoTrackObject สาธารณะ
{
   CString strGlobal
};
CProcessLocalltCMyGlobalData > globalData

__declspec(dllexport) โมฆะ SetGlobalString(LPCTSTR lpsz)
{
   globalData - > strGlobal = lpsz
}

__declspec(dllexport)
GetGlobalString (LPCTSTR lpsz, int cb) ถือเป็นโมฆะ
{
   lstrcpyn (lpsz, globalData - > strGlobal, cb);
}

MFC ใช้นี้ในสองขั้นตอน ก่อน ไม่มีเลเยอร์ที่ด้านบนของ Win32 Tls * APIs (TlsAlloc, TlsSetValue, TlsGetValueฯลฯ) ซึ่งใช้ดัชนี TLS เพียงสองต่อกระบวน ไม่ว่ากี่ Dll ที่คุณมี ที่สอง CProcessLocal มาแม่แบบเพื่อการเข้าถึงข้อมูลนี้ มันแทนตัวดำเนินการ-gt ซึ่งเป็นสิ่งช่วยให้ไวยากรณ์ง่ายที่คุณเห็นด้านบน วัตถุทั้งหมดที่ถูกห่อด้วย CProcessLocal ต้องได้มาจาก CNoTrackObject . CNoTrackObject มีการจัดสรรในระดับที่ต่ำกว่า (LocalAlloc/LocalFree) และ destructor แบบเสมือนที่ MFC สามารถโดยอัตโนมัติทำลายวัตถุภายในกระบวนการเมื่อมีการยกเลิกกระบวนการ วัตถุเช่นสามารถมี destructor แบบกำหนดเองถ้าจำเป็นต้องมีการล้างข้อมูลเพิ่มเติม ตัวอย่างข้างต้นไม่ต้องใช้หนึ่ง เนื่องจากคอมไพเลอร์จะสร้าง destructor เริ่มต้นที่ทำลายCStringวัตถุฝังตัว(&G)?

มีข้อดีที่น่าสนใจอื่น ๆ การทำเช่นนี้ ไม่เพียงแต่ มีทั้งหมด CProcessLocal วัตถุโดยอัตโนมัติ ทำลายพวกเขาไม่ได้สร้างจนกว่าพวกเขาจะต้อง CProcessLocal::operator-gt; จะสร้างอินสแตนซ์วัตถุเชื่อมโยงในครั้งแรกที่เรียกว่า และไม่มี sooner ในตัวอย่างข้างต้น ซึ่งหมายความว่า ที่ ' strGlobal ' สตริจะไม่สามารถถูกสร้างขึ้นจนเป็นครั้งแรกที่SetGlobalStringหรือGetGlobalStringเรียกว่า ในบางกรณี นี้สามารถช่วยลดเวลาเริ่มต้นของ DLL(&G)?

ข้อมูลภายในเธรด

คล้ายการประมวลผลข้อมูลท้องถิ่น หัวข้อข้อมูลในท้องถิ่นจะใช้เมื่อข้อมูลต้องอยู่ภายในหัวข้อที่กำหนด กล่าวคือ คุณต้องอินสแตนซ์แยกกันของข้อมูลสำหรับแต่ละหัวข้อที่เข้าถึงข้อมูลนั้น นี้หลายครั้งสามารถใช้ lieu ของกลไกการซิงโครไนส์ที่กว้างขวางขึ้น ถ้าข้อมูลไม่จำเป็นต้องใช้ร่วมกันจากหลาย ๆ กระทู้ กลไกเช่นได้ราคาแพง และไม่จำเป็น สมมติว่า เรามีวัตถุCString (เหมือนตัวอย่างข้างต้น) เราสามารถทำให้หัวข้อเฉพาะ โดยการตัดคำด้วยการ CThreadLocal แม่:

struct CMyThreadData: CNoTrackObject สาธารณะ
{
   CString strThread
};
CThreadLocalltCMyThreadData > threadData

โมฆะ MakeRandomString()
{
   / / เป็นชนิดของการ์ดสลับ (ไม่เป็นยอดเยี่ยม)
   CString & str = threadData - > strThread
   strEmpty()
   ในขณะที่ (strGetLength() ! = 52)
   {
      TCHAR ch = rand() % 52 + 1
      ถ้า (strFind(ch) < 0)
         str += ch / / ไม่พบ เพิ่ม
   }
}

ถ้า MakeRandomString ถูกเรียกจากสองหัวข้อแตกต่างกัน แต่ละจะ "สลับ" สายอักขระในวิธีการต่าง ๆ โดยไม่รบกวนอีกด้วย เนื่องจากไม่มีตัวจริง strThread อินสแตนซ์ต่อแทนของอินสแตนซ์ส่วนกลางเพียงหนึ่งเธรด?

หมายเหตุวิธีใช้การอ้างอิงในการจับภาพCStringอยู่ครั้งเดียวแทนที่เป็นหนึ่งครั้งสำหรับแต่ละการวนรอบการคำนวณซ้ำ รหัสการวนรอบอาจมีการเขียนด้วย threadData-gt;strThread ทุก ' str ' ใช้ แต่จะช้ามากในการเรียกใช้รหัสได้ มันดีแคการอ้างอิงไปยังข้อมูลเมื่อมีการอ้างอิงเช่นที่เกิดขึ้นในเกิดการวนซ้ำ(&G)?

การ CThreadLocal คลาแม่ใช้กลไกเดียวกันที่ CProcessLocal ไม่และเทคนิคการใช้งานเดียวกัน?

หมายเหตุด้านเทคนิคตามหมายเลข|nbsp หมายเหตุด้านเทคนิคตามประเภท(&N)

Index