DllMain और Loader Lock — DLL initialization में "कुछ मत करो" कहा जाने का असली कारण
· अद्यतन तिथि: · Go Komura · Windows, DLL, Windows development, C++, troubleshooting, multithreading, Win32 API
संशोधन इतिहास (पहला संस्करण, 22 Aug 2026 को प्रकाशित)
- पहला प्रकाशन
इस लेख का हवाला दें(DOI: 10.5281/zenodo.22176707)
यह लेख Zenodo पर संग्रहीत है। नीचे वह DOI है जो हमेशा नवीनतम संस्करण की ओर ले जाता है, और वह DOI भी जो आपके पढ़े जा रहे संस्करण से बँधा है।
Go Komura (2026). DllMain और Loader Lock — DLL initialization में "कुछ मत करो" कहा जाने का असली कारण. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22176707 https://comcomponent.com/hi/blog/dllmain-loader-lock/
- DOI (नवीनतम संस्करण)
- 10.5281/zenodo.22176707
- DOI (यह संस्करण)
- 10.5281/zenodo.22176708
“App startup पर hang होता है, पर केवल खास environment में।” “अपना DLL load करते समय LoadLibrary कभी लौटता ही नहीं।” “केवल service start के समय deadlock होता है।” — ऐसी जाँचों को काफी दूर तक ले जाएँ तो, अक्सर, आप उसी जगह पहुँचते हैं। DLL का initialization code — यानी DllMain।
Microsoft का documentation DllMain के बारे में unusually तीखे स्वर में चेताता है। LoadLibrary न बुलाएँ। दूसरे thread से synchronize न करें। User, Shell, या COM functions न बुलाएँ। Ideal DllMain खाली stub है — भाषा इतनी सख्त क्यों है? कारण एक internal mechanism में केंद्रित है, loader lock। Windows पर DLL, plug-in, और C++/CLI wrapper लिखने वाले developers के लिए, यह लेख primary sources से समझाता है कि loader lock कैसे काम करता है, वह structure जो deadlock को टिकाए रखती है, और सुरक्षित design तथा जाँच की प्रक्रिया।
1. निष्कर्ष पहले
DllMainloader lock पकड़े call होता है, वह shared lock जिसका per process ठीक एक होता है। इसलिएDllMainसे ऐसा काम call करना जो loader lock लेने की कोशिश करे (direct या indirect) deadlock की संभावना बनाता है, या अभी initialize न हुए DLL को छूने से crash।1LoadLibrary/FreeLibrarycall वर्जित है। यह circular load-order dependency बनाता है और ऐसे DLL के against initialization code चला सकता है जिसका अपना initialization अभी नहीं चला।2- दूसरे thread से synchronize करना भी वर्जित है। DLL notifications serialize हैं, इसलिए
DllMainके अंदर thread के start या exit का wait उस thread को खुद loader lock की wait में रोक देता है, और deadlock होता है।23 - जो सुरक्षित call कर सकते हैं वह practically केवल Kernel32.dll का subset है। और official documentation साफ़ कहता है कि “safe functions की पूरी list मौजूद नहीं”। User, Shell, और COM functions अन्य components load करते हैं और access violation पैदा करते हैं।2
- CRT से link DLL में, वही restrictions global के constructor और destructor पर लागू होते हैं। वे
DllMainके de facto हिस्से के रूप में चलते हैं।2 - सही design है “defer करना”। जो initialization compile time (static) हो सके वह करें; जो न हो वह पहली बार use होने तक defer करें। यही official best practice है।1
- C++/CLI mixed DLL खास तौर पर खतरनाक हैं। Loader lock के अधीन MSIL चलने से बचने के लिए,
DllMainऔर उसका call tree native compile होना चाहिए।4
2. DllMain कब और कैसे call होता है
DllMain वह entry point है जिसे OS loader तब call करता है जब DLL process या thread में enter करता या छोड़ता है। चार notifications हैं।
| Notification | Timing |
|---|---|
| DLL_PROCESS_ATTACH | जब DLL process में load हो |
| DLL_THREAD_ATTACH | जब process में नया thread start हो |
| DLL_THREAD_DETACH | जब thread normally exit करे |
| DLL_PROCESS_DETACH | जब DLL unload हो, या process exit करे |
दो facts आसानी से छूट जाते हैं। पहला, हर बार एक thread बनने पर, पहले से loaded हर DLL का DllMain DLL_THREAD_ATTACH से call होता है। यानी DllMain “मेरा DLL load होने पर एक बार चलने वाली चीज़” नहीं; यह code process की thread activity पर बार-बार call होता रहता है। यदि वह न चाहिए, तो DLL_PROCESS_ATTACH के अंदर DisableThreadLibraryCalls call कर रोक सकते हैं (static CRT से link DLL से न बुलाएँ)।5
दूसरा, CRT (C/C++ runtime) से link DLL में, global और static C++ object के constructor और destructor CRT के entry point से DllMain के हिस्से के रूप में चलते हैं।2 भले आप सोचें “हमारा DllMain खाली है, इसलिए हम सुरक्षित हैं”, detailed initialization वाला global object वही काम DllMain में चलाने जैसा है।
flowchart TB
accTitle: वो चार timings जब DllMain call होता है
accDescr: DLL load पर DLL_PROCESS_ATTACH चलता है; process में हर thread start और exit पर पहले से loaded हर DLL पर DLL_THREAD_ATTACH और DETACH चलते हैं; unload या process exit पर DLL_PROCESS_DETACH चलता है; और static object के constructor भी CRT से इसके अंदर चलते हैं
load["DLL load"] --> pa["DLL_PROCESS_ATTACH"]
pa --> ta["DLL_THREAD_ATTACH(हर thread start पर)"]
ta --> td["DLL_THREAD_DETACH(हर thread exit पर)"]
td --> pd["DLL_PROCESS_DETACH(unload या exit पर)"]
pa -.-> crt["static object का construction भी यहाँ चलता है"]
चित्र 1: DllMain केवल load time नहीं, हर thread start और exit पर भी call होता है, और static object का initialization भी उसके हिस्से के रूप में चलता है।
3. Loader lock — एक lock जो हर notification को serialize करता है
DllMain पर restrictions अकेले इतने कठोर क्यों हैं? जवाब loader की structure में है।
DLL load, unload, और विभिन्न notifications की श्रृंखला को consistent रखने के लिए, OS loader काम को per process एक loader lock से serialize करता है। और महत्वपूर्ण बिंदु यह है कि DllMain इस loader lock पकड़े call होता है।1 जब तक आप DllMain के अंदर हैं, उस process का हर अन्य DLL load, और हर thread-start notification, इस lock के छूटने का wait करता है।
उस structure से निषेधों के कारण एक के बाद एक निकलते हैं।
LoadLibraryन बुलाएँ क्योंकि वह loader-lock re-entry, या circular load-order dependency बनाता है। वह ऐसे DLL पर function call भी करा सकता है जिसका initialization अभी पूरा नहीं।2- दूसरे thread से synchronize करना खतरनाक है क्योंकि जिस thread का wait कर रहे हैं उसके ऐसे क्षण होते हैं जब उसे loader lock चाहिए (start और exit की notifications,
GetModuleHandle-family API की call, आदि)। आप loader lock पकड़े दूसरी ओर का wait करते हैं; दूसरी ओर loader lock का wait करती है — classic lock-order inversion।6 - User, Shell, और COM functions खतरनाक हैं क्योंकि वे internally अन्य system components load करते हैं। Initialization से पहले, या teardown के बाद, components छूते हैं और access violation मिलता है।2
sequenceDiagram
accTitle: DllMain के अंदर thread का wait deadlock क्यों करता है
accDescr: Loader lock पकड़े DllMain worker thread के निकलने का wait करता है, पर निकलने की कोशिश कर रहा worker DLL_THREAD_DETACH पाने के लिए loader lock के छूटने का wait करता है, इसलिए वे एक-दूसरे का wait कर deadlock करते हैं
participant L as Loader(lock पकड़े)
participant D as DllMain
participant W as Worker
L->>D: DLL_PROCESS_DETACH
D->>W: Exit माँगें और wait करें
W->>W: काम खत्म, फिर exit
Note over W: Exit notification को lock चाहिए
Note over D,W: DllMain lock पकड़े, W wait करता है
चित्र 2: “DllMain thread के निकलने का wait करे” structural deadlock है, क्योंकि thread exit को खुद loader lock चाहिए।
बिंदु यह है कि यह उस तरह की बात नहीं जो “भाग्य खराब हो तो होती है”; यह structure से तय है कि टिकेगी। Documentation कहता है कि loader lock को app द्वारा defined lock hierarchy के शीर्ष (पहले लिया जाने वाला) मानें। DllMain के अंदर आप पहले से वह top-level lock पकड़े हैं, इसलिए वहाँ से आगे किसी और चीज़ का wait करना खतरनाक है — याद रखने का useful तरीका।6
flowchart TB
accTitle: Loader lock और private lock के बीच lock-order inversion
accDescr: Loader lock पकड़े DllMain private lock लेने जाता है, जबकि वह private lock पकड़े worker GetModuleHandle आदि के लिए loader lock लेने जाता है, इसलिए acquisition order उलटा होता है और deadlock होता है
d["DllMain: loader lock पकड़े"] --> dg["Private lock G लेने जाता है"]
w["Worker: private lock G पकड़े"] --> wl["Loader lock लेने जाता है"]
dg -.-> dead["उल्टे acquisition order से deadlock"]
wl -.-> dead
wl -.-> api["GetModuleHandle आदि को internally चाहिए"]
चित्र 3: GetModuleHandle जैसी निर्दोष API भी internally loader lock माँगती है, इसलिए private lock से order inversion टिक सकता है।
साथ ही, DllMain के अंदर से CreateThread खुद recommended नहीं। बनाए thread को DLL_THREAD_ATTACH notification process करने के लिए loader lock चाहिए, इसलिए वह तब तक चलना शुरू नहीं कर सकता जब तक वर्तमान DllMain return करे और lock छोड़े। इसलिए DllMain के अंदर उस thread के start या खत्म होने का wait तत्काल deadlock है। Lifetime problem भी है — यदि DllMain return के बाद, अभी न चला thread पीछे रहते DLL unload हो, तो thread का start address पहले से free code की ओर इशारा करता है और crash होता है।3
4. दो landmines जिन पर C++ developers आसानी से चलते हैं
Landmine 1: Global object का dynamic initialization। अध्याय 2 में कहा, static object के constructor DllMain restrictions के अधीन चलते हैं। Config file पढ़ना, logging सुविधा खड़ी करना, COM initialization, thread start करना — जिस क्षण आप DLL में ऐसा global रखते हैं जिसका constructor वह काम करे, आप “DllMain में जो नहीं करना चाहिए” execute कर रहे होते हैं। Compile time तय constant initialization (जो constexpr बना सकें) सुरक्षित है; function call वाला initialization defer करना चाहिए।
flowchart TB
accTitle: Global object initialization के landmine बनने का path
accDescr: DLL load पर loader lock लिया जाता है, और global object के constructor CRT entry point से चलते हैं, इसलिए उन constructor के अंदर LoadLibrary, thread synchronization, और COM initialization DllMain निषेधों का execution हैं
load["DLL load(loader lock लिया)"] --> crt["CRT entry point"]
crt --> ctor["Global object का constructor"]
ctor --> ng1["LoadLibrary-equivalent काम"]
ctor --> ng2["Thread start कर खत्म होने का wait"]
ctor --> ng3["COM या User32 use"]
ng1 -.-> risk["ये सभी DllMain निषेधों के अधीन हैं"]
ng2 -.-> risk
ng3 -.-> risk
चित्र 4: भले “DllMain खाली है, इसलिए हम सुरक्षित हैं”, detailed initialization वाला global आते ही वही खतरा जीवित हो जाता है।
Landmine 2: C++/CLI (mixed assembly)। Native DLL को C++/CLI से wrap करने वाले configuration में (wrapper लेख में वर्णित रूप), loader lock के अधीन MSIL (managed code) चलने का खतरा है। MSIL चलाना CLR initialization या दूसरी assembly का load trigger कर सकता है। Compiler उन code पर warning C4747 देता है जहाँ DllMain सीधे MSIL चलाए, पर दूसरे module की function से indirect execution नहीं पकड़ सकता। DllMain और उससे बुलाई functions को #pragma unmanaged से native compile करें, या ऐसा configuration use करें जिसमें DllMain हो ही नहीं।4
flowchart TB
accTitle: Loader lock के अधीन MSIL execution पकड़ा जा सकता है या नहीं
accDescr: जहाँ DllMain सीधे MSIL चलाए वह code compiler warning C4747 से पकड़ सकता है, पर दूसरे module की function से indirect execution नहीं, इसलिए call tree की review और native compile पर ज़ोर से रोकना पड़ता है
d2["DllMain से call"] --> dir["सीधे MSIL चलाएँ"]
d2 --> ind["दूसरे module से चलाएँ"]
dir --> c47["Warning C4747 से पकड़ने योग्य"]
ind --> nc["Compiler नहीं पकड़ सकता"]
nc -.-> rv["Review और pragma unmanaged से रोकें"]
चित्र 5: C4747 केवल direct execution से बचाता है। Indirect path केवल review से पकड़े जाते हैं।
5. सही design — “defer करना” को default policy बनाएँ
Official best-practice recommendation स्पष्ट है।1
- जो initialization compile time (static) हो सके वह पूरा करें। पहले पूछें कि क्या dynamic initialization static से बदला जा सकता है।
- बाकी पहली बार use होने तक defer करें। जब तक पहली बार use DLL load पूरा होने के बाद बुलाई ordinary API से हो, initialization loader lock के बाहर चलता है और लगभग पूरी Windows API सुरक्षित use कर सकते हैं। First access पर exclusion के लिए
INIT_ONCE(one-time initialization) या C++ magic statics (function-local static) use कर सकते हैं। Defer करना रामबाण नहीं — यदि वह first access खुदDllMainया static initializer से हो, तो initializer फिर loader lock के अधीन चलता है और वही restrictions लौटते हैं। - केवल उन failures के लिए exception करें जिन्हें जल्दी पकड़ना हो। यह requirement हो सकती है कि टूटी config file से load खुद fail हो। तब भी, “कोशिश करें और तुरंत fail हों” के न्यूनतम तक रखें।
- DLL_PROCESS_ATTACH में
DisableThreadLibraryCallsपर विचार करें। यदि DLL thread notifications use न करे, तो notification लागत खुद हटा सकते हैं (static CRT या static TLS use को छोड़कर)।5 - Application Verifier से जाँचें।
DllMainके अंदर कई खतरनाक calls वे हैं जिन्हें Application Verifier run time पर पकड़ता है।1
flowchart TB
accTitle: DLL initialization के लिए design guidance
accDescr: पहले सोचें कि initialization compile-time static हो सकता है या नहीं; यदि नहीं, तो default पहली बार use होने तक defer करना है, और DllMain में केवल वह न्यूनतम छोड़ें जिसे load failure के रूप में जल्दी पकड़ना हो
q1{"Compile time तय हो सकता है?"} -->|"हाँ"| s["Static initialization बनाएँ"]
q1 -->|"नहीं"| q2{"Failure load time पकड़नी है?"}
q2 -->|"नहीं"| lazy["पहली बार use होने तक defer(default)"]
q2 -->|"हाँ"| min["DllMain में केवल न्यूनतम करें"]
lazy -.-> once["INIT_ONCE या function-local static से exclude करें"]
चित्र 6: Decision order है “क्या static हो सकता है → क्या defer किया जा सकता है”, और DllMain में जो छोड़ते हैं वह केवल वह न्यूनतम है जिसे जल्दी पकड़ना हो।
DisableThreadLibraryCalls apply करें या नहीं, निम्नलिखित शाखा से mechanically तय हो सकता है।
flowchart TB
accTitle: DisableThreadLibraryCalls call करें या नहीं
accDescr: Static CRT से link DLL से न बुलाएँ; यदि static TLS effective हो तो call खुद fail होती है इसलिए न बुलाएँ; यदि दोनों न हों और DLL thread notifications use न करे, तो DLL_PROCESS_ATTACH में return value जाँच कर बुलाएँ ताकि notification लागत कटे
q1{"Static CRT से link?"} -->|"हाँ"| no2["नहीं बुलाना चाहिए"]
q1 -->|"नहीं"| q2{"Static TLS use?"}
q2 -->|"हाँ"| eff["Call वैसे भी fail(FALSE)"]
q2 -->|"नहीं"| q3{"Thread notifications चाहिए?"}
q3 -->|"नहीं"| yes["ATTACH में बुलाएँ(return जाँचें)"]
q3 -->|"हाँ"| keep["न बुलाएँ; notifications सँभालें"]
चित्र 7: Static CRT, static TLS, और notifications चाहिए या नहीं — ये तीन शर्तें uniquely तय करती हैं कि बुलाना चाहिए या नहीं।
Unload पर thread रोकने के लिए official documentation ठोस protocol देता है। DLL_PROCESS_DETACH में worker thread के निकलने का “wait” करने के बजाय (FreeLibrary से unload पर), रूप है (1) event से exit signal दें, (2) thread पक्ष काम को consistent state तक समेटे, वापस signal दे, और infinite wait में प्रवेश करे, (3) DllMain पक्ष consistent state confirm कर फिर TerminateThread से thread समेटे।3 यह कठोर लगता है, पर “आप DllMain के अंदर thread के natural exit का wait नहीं कर सकते” की constraint के भीतर realistic जवाब के रूप में documented है।
sequenceDiagram
accTitle: Unload पर thread रोकने का protocol
accDescr: DllMain worker thread को event से निकलने का signal देता है; worker काम को consistent state तक समेटता है, वापस signal देता है, और infinite wait में जाता है; DllMain consistent state confirm कर फिर thread terminate करता है
participant D as DllMain(DETACH handle)
participant W as Worker thread
D->>W: Event से exit signal
W->>W: काम को consistent state तक समेटें
W->>D: Consistency complete signal दें और हमेशा wait
D->>W: TerminateThread से terminate करें
Note over D,W: Natural exit का wait नहीं, इसलिए deadlock नहीं
चित्र 8: “Natural exit का wait” के बजाय, “consistency signal का wait कर फिर काटें” loader lock से टकराव बचाता है।
पहले principles के रूप में, सबसे सुरक्षित design है unload हो सकने वाले DLL में thread का ownership न रखना, और thread ownership EXE पक्ष पर रखना।
Process exit पर DLL_PROCESS_DETACH उलटा है: कुछ न कर return करना ideal है। इस बिंदु तक हर अन्य thread पहले ही forcibly terminate हो चुका होता है, और dependent DLL या runtime की state पर भी भरोसा नहीं कर सकते। यहाँ detailed काम केवल deadlock और crash पैदा करता है। जो data persist होना चाहिए उसे app के अपने shutdown path में लिखें; इस notification पर depend न हों।3
6. जब इसमें फँसें तो जाँच कैसे करें
Loader-lock hang की पहचानने योग्य fingerprint होती है।
Hang dump में stack देखें। जमे क्षण का dump लें और हर thread का stack जाँचें। यदि आपको ntdll.dll loader functions (जिनके नाम Ldr से शुरू) के अंदर lock का wait कर रहा thread, और DllMain या static initializer (dynamic initializer) के अंदर किसी और चीज़ का wait कर रहा thread — यह जोड़ा मिले, तो लगभग निश्चित हैं। LoadLibrary call के बीच रुका thread दूसरा typical character है।
flowchart TB
accTitle: Loader-lock hang की fingerprint
accDescr: Hang dump में, यदि ntdll loader functions के अंदर lock का wait कर रहा thread और DllMain या static initializer के अंदर किसी और का wait कर रहा thread दोनों मिलें, तो लगभग निश्चित रूप से loader-lock deadlock मान सकते हैं
dump["Hang dump"] --> t1["Ldr-family function में lock का wait"]
dump --> t2["DllMain या static initializer में wait"]
t1 --> pair{"दोनों मौजूद?"}
t2 --> pair
pair -->|"हाँ"| conf["लगभग निश्चित loader-lock deadlock"]
pair -->|"नहीं"| other["दूसरे प्रकार के hang के रूप में जाँचें"]
चित्र 9: Loader-lock hang की पहचानने योग्य fingerprint है “Ldr में wait + DllMain के अंदर wait”।
“Timing-dependent” character पर शक करें। Loader-lock deadlock केवल उस क्षण टिकता है जब DLL load thread start या exit से मेल खाए। “Startup पर कभी-कभी”, “केवल खास machine पर”, और “केवल service के रूप में चलाने पर” जैसी reproduction स्थितियाँ इस तरह की problem के संकेत हैं।
Preventive जाँच चलाएँ। Application Verifier enable कर test चलाएँ, और DllMain के अंदर खतरनाक calls run time पर पकड़ सकते हैं।1 C++/CLI के लिए, warning C4747 ignore न करें; DllMain से पहुँचने योग्य functions की review में “indirect LoadLibrary call करने वाली function” (COM initialization, कुछ CRT सुविधाएँ, delay-loaded imports, आदि) का कोण checklist में जोड़ें, तो ship से पहले accidents पकड़ेंगे। Delay-loaded import की पहली call का internally LoadLibrary बन जाना आसानी से छूटने वाला बिंदु है।
7. सारांश
DllMainloader lock (per process एक, हर DLL notification serialize करने वाला lock) पकड़े call होता है। हर restriction उसी से निकलता है।- निषेधों का केंद्र है “
LoadLibrary/FreeLibraryन बुलाएँ”, “दूसरे thread से synchronize न करें”, और “Kernel32 के अलावा DLL पर depend करने वाली functions न बुलाएँ”। CRT से चलने वाले static object के constructor और destructor उन्हीं restrictions के अधीन हैं। - बुनियादी design policy defer करना है। जो static बना सकें वह static करें; बाकी पहली बार use होने तक defer करें।
DisableThreadLibraryCallsऔर Application Verifier use करें। - Unload पर thread रोकना official protocol मानता है (signal → consistency confirm → terminate)। Process exit पर DLL_PROCESS_DETACH ideally खाली है।
- C++/CLI में, loader lock के अधीन MSIL चलाना अपनी landmine है।
DllMaincall tree के native compile पर ज़ोर दें।
DllMain restrictions पहले देखने में निषेधों की अनुचित list लगते हैं। पर एक बार यह एक बिंदु पकड़ लें कि “यह top-level lock, loader lock, पकड़े call होता है”, हर निषेध उसी principle का restatement है। इसे principle के रूप में याद रखें, और जब documentation में न हो ऐसा किनारा मिले, तब भी सही सवाल पूछ सकेंगे: “क्या यह काम मैं lock पकड़े करने का अधिकार रखता हूँ?”
संबंधित लेख
- Windows DLL name resolution कैसे काम करता है — search order और SxS
- C# से native DLL call करना: C++/CLI wrapper बनाम P/Invoke
- व्यावहारिक multithreading best practices: C++ संस्करण — RAII और jthread से structure द्वारा accidents हटाना
- Spurious wakeup — Condition Variable “बिना notification के” क्यों जागती है और Windows पर सही wait
- WinDbg + SOS से crash dump पढ़ना — संग्रह के बाद analysis की practical guide
- COM STA/MTA की बुनियाद — threading model और hang से बचना
संबंधित परामर्श क्षेत्र
KomuraSoft LLC startup या DLL load time hang और deadlock की root-cause जाँच (dump analysis), DllMain और static initialization के आसपास design review, तथा C++/CLI wrapper और plug-in DLL को सुरक्षित initialization design की ओर सुधारना सँभालता है। “केवल खास environment में startup पर hang होता है” के कठिन-reproduction चरण पर भी consult कर सकते हैं।
संदर्भ लिंक
-
Microsoft Learn, Dynamic-Link Library Best Practices. DllMain के loader lock पकड़े call होने पर, जिससे call कर सकने वाली functions कठोर limited हैं; ideal DllMain के खाली stub होने और initialization यथासंभव defer किए जाने पर; compile-time static initialization की recommendation पर; जल्दी पकड़नी वाली failures के लिए केवल न्यूनतम करने पर; और Application Verifier से typical DllMain गलतियाँ पकड़ने पर। ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, DllMain entry point. Entry point में केवल simple initialization और termination करने पर; LoadLibrary / FreeLibrary न बुलाने के कारण (circular load order और initialization से पहले या termination के बाद DLL use); Kernel32.dll के पहले से loaded guaranteed होने पर, ताकि अन्य DLL load न करने की सीमा में call कर सकें; safe functions की पूरी list न होने पर; User, Shell, और COM functions के access violation पैदा करने पर; DLL notifications के serialize होने पर, जिससे अन्य thread या process से communication deadlock करता है; और CRT link होने पर static object के constructor और destructor पर वही restrictions लागू होने पर। ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, Dynamic-Link Library Best Practices - Best Practices for Synchronization. DllMain के अंदर thread के निकलने का wait करने पर deadlock करने वाली structure पर (thread exit की DLL_THREAD_DETACH notification को loader lock चाहिए); unload पर thread रोकने के protocol पर (event से signal, consistent state confirm, फिर terminate); process exit पर DLL_PROCESS_DETACH के अन्य threads पहले से forcibly terminate और address-space consistency की guarantee न होने पर, जिससे ideal handler खाली है; और DllMain में thread बनाने के अधूरे initialization के साथ notifications queue में छोड़ने और problem पैदा करने पर। ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Initialization of Mixed Assemblies. Loader lock के अधीन MSIL न चलाने पर; DllMain और उसके call tree को MSIL में न compile करने और #pragma unmanaged से निपटने पर; DllMain के सीधे MSIL चलाने की कोशिश पर warning C4747 दिए जाने, पर दूसरे module से indirect execution न पकड़े जाने पर; और static object के dynamic initializer के वही problem पैदा कर सकने पर। ↩ ↩2
-
Microsoft Learn, DisableThreadLibraryCalls function (libloaderapi.h). Thread create और destroy पर overhead घटाने के लिए DLL_THREAD_ATTACH / DLL_THREAD_DETACH notifications disable करने पर; static CRT से link DLL से न बुलाने पर; और static TLS (thread_local या __declspec(thread)) effective होने पर optimization न किए जाने पर। ↩ ↩2
-
Microsoft Learn, Dynamic-Link Library Best Practices - Deadlocks Caused by Lock Order Inversion. Lock hierarchy define कर हमेशा उसी order में acquire करने पर; loader के DllMain बुलाने से पहले loader lock लेने पर, जिससे loader lock lock hierarchy के शीर्ष पर बैठना चाहिए; GetModuleFileName जैसी indirect loader lock लेने वाली API और private lock के बीच acquisition order देखने पर; और lock-order inversion से deadlock के ठोस उदाहरण पर। ↩ ↩2
संबंधित लेख
निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।
Win32 Thread Pool API — CreateThreadpoolWork से thread बनाए बिना concurrency
क्या आपके native code में CreateThread calls बिखरी पड़ी हैं? यह लेख Vista में redesign की गई Win32 thread pool API — work, timer, wait और...
"Not Responding" वास्तव में क्या है — Windows ऐप के hang का फैसला कैसे करता है, और hang न करने वाले ऐप कैसे design करें
Windows का "Not Responding" वह mechanism है जिसमें OS तय करता है कि window ने 5 सेकंड message नहीं लिया और उसे ghost window से बदल देता ह...
Spurious wakeup — condition variable "बिना notification के" क्यों जागती है और Windows पर सही wait कैसे करें
Condition variable की wait notification आए बिना भी लौट सकती है (spurious wakeup)। यह लेख Windows implementation से समझाता है कि spec इसे ...
Named Pipes व्यवहार में — design से security तक, Windows की standard IPC
Named pipes — Windows की standard inter-process communication — की practical guide। यह लेख primary sources से byte और message mode का चुन...
Sleep से resume पर टूटने वाले apps — Windows power events कैसे काम करते हैं और उनसे बचने वाले business apps कैसे बनाएँ
Laptop खोला और business app के connections मर चुके थे — कारण वह design है जिसने sleep का हिसाब ही नहीं रखा। यह लेख WM_POWERBROADCAST noti...
संबंधित विषय
ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।
Windows के तकनीकी विषय
Windows विकास, बग जाँच और मौजूदा संपत्तियों के उपयोग का प्रवेश-द्वार।
इस विषय से जुड़ी सेवाएँ
यह लेख निम्नलिखित सेवाओं से सीधे जुड़ा है।
Windows ऐप विकास
व्यावसायिक ऐप, डिवाइस एकीकरण और संचार उपकरण, आवश्यकताओं से विकास तक।
अक्सर पूछे जाने वाले प्रश्न
इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।
- क्या DllMain में सच में कुछ भी नहीं कर सकते?
- "कुछ मत करो" exaggeration नहीं है; यह official design stance है, और Microsoft खुद कहता है कि ideal DllMain लगभग खाली stub है। Safe वो Kernel32.dll functions का subset है — DllMain चलने तक Kernel32 loaded होना guaranteed है — जो अन्य DLL load न करे। Critical section या mutex बनाना, और TLS use करना, उदाहरण हैं जो कर सकते हैं। इसके उल्टे, LoadLibrary/FreeLibrary, दूसरे thread से synchronize करना, और User32, Shell, COM वगैरह की function call वर्जित हैं क्योंकि वे deadlock और access violation पैदा करते हैं। जिस initialization पर शक हो वो DllMain में न करें; पहली बार use होने तक defer करें।
- क्या C++ global (static object) के constructor भी DllMain restrictions के अधीन हैं?
- हाँ। जब DLL CRT (C++ runtime) से link हो, तो global और static object के constructor और destructor CRT द्वारा दिए entry point से, DllMain के de facto हिस्से के रूप में चलते हैं। यानी constructor से LoadLibrary call करना, दूसरा thread start कर उसके खत्म होने का wait, COM initialization, आदि — सब वही खतरा रखते हैं जो DllMain में करने से होता है। Non-trivial initialization वाले global के लिए pointer रखें और first access पर बनाएँ, या function-local static use करें, ताकि काम DllMain के बाहर चले।
- क्या DisableThreadLibraryCalls call करूँ?
- Conditional, हाँ। यदि DLL को DLL_THREAD_ATTACH/DETACH notifications न चाहिए हों, तो DLL_PROCESS_ATTACH में DisableThreadLibraryCalls call करने से per-thread-create और per-thread-exit notifications रुकती हैं और बार-बार thread बनाने वाले process में overhead घटता है। दो exceptions हैं। Static CRT से link DLL से इसे न बुलाएँ (static CRT को thread notifications चाहिए)। और यदि thread_local या __declspec(thread) से static TLS effective हो, तो call खुद fail होकर FALSE लौटाती है, इसलिए return value जाँचने की आदत बनाएँ। Dynamically linked CRT use करने वाले typical DLL पर, यह confirm कर कि thread notifications पर कुछ depend नहीं, इसे use करें।
- C++/CLI (mixed managed) DLL startup पर क्यों hang होता है?
- Typical कारण है loader lock पकड़े रहते MSIL (managed code) चलाने की कोशिश। C++/CLI mixed assembly में, यदि DllMain, उससे बुलाई function, या global के dynamic initializer MSIL में compile हों, तो loader lock के अधीन CLR initialization या दूसरी assembly का load चाहिए पड़ सकता है, और वह deadlock कर सकता है। जब DllMain खुद सीधे MSIL चलाने की कोशिश करे तो compiler warning C4747 देता है, पर दूसरे module से indirect execution नहीं पकड़ सकता। Fix है DllMain और उसके call tree को #pragma unmanaged से native compile करना — या DllMain रखना ही नहीं।
- क्या DLL_PROCESS_DETACH में resources cleanup करूँ?
- जवाब "process exit" और "FreeLibrary से unload" के बीच बदलता है। Process exit पर DLL_PROCESS_DETACH में अन्य threads पहले ही terminate हो चुके होते हैं, और address space अभी भी consistent हो इसकी guarantee नहीं, इसलिए memory free करने जैसा cleanup वास्तव में खतरनाक है; official guidance है कि "ideal handler खाली है"। जो data persist होना चाहिए उसे app के अपने shutdown path में लिखें, और यहाँ basically कुछ न करें और return करें। FreeLibrary से unload पर process चलता रहता है, इसलिए पूरा cleanup चाहिए — threads रोकना, handles बंद करना, आदि। पर DllMain के अंदर thread के निकलने का wait deadlock करता है, इसलिए official protocol मानें: signal दें, consistent state तक wait करें, और काम DllMain के बाहर पूरा करें।