หมายเหตุทางเทคนิคนี้อธิบายการใช้งานโครงสร้าง 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 สร้างวัตถุ และวิธีการในการโทร?
เช่นที่คุณเห็น รัฐโมดูลจะไปจากวัตถุวัตถุ ตามที่มีสร้าง คุณจำเป็นต้องมีโมดูสถานะการตั้งค่าอย่างเหมาะสม ถ้ามันไม่ได้ตั้งค่า 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)