2012-12-09 सारांश:विनाश
- एक सामान्य मिश्रित मोड आवेदन वैश्विक देशी सी ++ finalizers के रूप में चलाने विनाशकर्ता में। उस व्यवहार या संबंधित टाइमआउट को बदलना संभव नहीं है।
- एक मिश्रित-मोड असेंबली डीएलएल डीएलएल लोड/अनलोड के दौरान सी ++ कन्स्ट्रक्टर/विनाशकों को चलाता है - बिल्कुल मूल डीएलएल के रूप में।
- COM12 इंटरफ़ेस का उपयोग कर देशी निष्पादन योग्य में सीएलआर को होस्ट करना deconstructors दोनों को देशी डीएलएल (व्यवहार की इच्छा है) में व्यवहार करने और अंतिमकर्ताओं (एक अतिरिक्त बोनस) के लिए टाइमआउट सेट करने की अनुमति देता है।
- जहां तक मेरा बता सकते हैं से कम से कम विजुअल स्टूडियो 2008, 2010 और 2012 (केवल .NET 4 के साथ परीक्षण)
वास्तविक CLR होस्टिंग निष्पादन मैं उपयोग करने की योजना बहुत के समान है पर लागू होता है कुछ मामूली परिवर्तन के अलावा इस सवाल में उल्लिखित एक:
- कुछ मूल्य को
OPR_FinalizerRun
रूपरेखा (60 सेकंड वर्तमान में है, लेकिन विषय बदलने के लिए) के रूप में हंस Passant ने सुझाव दिया। - एटीएल कॉम स्मार्ट पॉइंटर वर्गों का उपयोग करना (ये विजुअल स्टूडियो के एक्सप्रेस संस्करणों में उपलब्ध नहीं हैं, इसलिए मैंने उन्हें इस पोस्ट से छोड़ा)।
CLRCreateInstance
mscoree.dll
गतिशील रूप से (कोई संगत सीएलआर स्थापित होने पर बेहतर त्रुटि संदेशों को अनुमति देने के लिए)।- मेजबान से कमांड लाइन को असेंबली डीएलएल में नामित
Main
फ़ंक्शन पर पास करना।
उन सभी के लिए धन्यवाद जिन्होंने प्रश्न और/या टिप्पणी पढ़ने के लिए समय निकाला।
2012-12-02 पोस्ट के नीचे अपडेट करें।
मैं विजुअल स्टूडियो 2012 का उपयोग .NET 4 के साथ मिश्रित मोड सी ++/सीएलआई एप्लिकेशन पर काम कर रहा हूं और यह पता चला है कि कुछ मूल वैश्विक वस्तुओं के विनाशकों को बुलाया नहीं जा रहा था। इस मुद्दे की जांच करना यह पता चला है कि वे प्रबंधित वस्तुओं की तरह व्यवहार करते हैं जैसा कि this post में बताया गया है।
मैं इस व्यवहार से काफी आश्चर्यचकित था (मैं इसे प्रबंधित वस्तुओं के लिए समझता हूं) और इसे कहीं भी दस्तावेज नहीं मिला, न तो C++/CLI standard और न ही destructors and finalizers के विवरण में।
Hans Passant द्वारा टिप्पणी में दिए गए सुझाव के बाद, मैंने कार्यक्रमों को एक असेंबली डीएलएल के रूप में संकलित किया और इसे एक छोटे से मूल निष्पादन योग्य में होस्ट किया और यह मुझे वांछित व्यवहार देता है (विनाशकों को एक ही धागे में खत्म करने और चलाने के लिए पर्याप्त समय दिया जाता है जैसा कि उनका निर्माण किया गया था)!
मेरे सवालों का:
- मैं एक अकेले खड़े निष्पादन में समान व्यवहार प्राप्त कर सकते हैं?
- यदि (1) संभव नहीं है तो निष्पादन योग्य के लिए प्रक्रिया समय समाप्ति नीति (यानी मूल रूप से
ICLRPolicyManager->SetTimeout(OPR_ProcessExit, INFINITE)
पर कॉल करना) को कॉन्फ़िगर करना संभव है?यह एक स्वीकार्य कामकाज होगा। - यह दस्तावेज कहां है/मैं इस विषय पर खुद को और कैसे शिक्षित कर सकता हूं? मैं उस व्यवहार पर भरोसा नहीं करता जो बदलने के लिए उत्तरदायी है।
संकलन नीचे फ़ाइलों के रूप में निम्नानुसार पुन: पेश करने के लिए:
cl /EHa /MDd CLRHost.cpp
cl /EHa /MDd /c Native.cpp
cl /EHa /MDd /c /clr CLR.cpp
link /out:CLR.exe Native.obj CLR.obj
link /out:CLR.dll /DLL Native.obj CLR.obj
अवांछित व्यवहार:
C:\Temp\clrhost>clr.exe
[1210] Global::Global()
[d10] Global::~Global()
C:\Temp\clrhost>
चल रहा है की मेजबानी:
C:\Temp\clrhost>CLRHost.exe clr.dll
[1298] Global::Global()
2a returned.
[1298] Global::~Global()
[1298] Global::~Global() - Done!
C:\Temp\clrhost>
प्रयुक्त फाइलें:
// CLR.cpp
public ref class T {
static int M(System::String^ arg) { return 42; }
};
int main() {}
// Native.cpp
#include <windows.h>
#include <iostream>
#include <iomanip>
using namespace std;
struct Global {
Global() {
wcout << L"[" << hex << GetCurrentThreadId() << L"] Global::Global()" << endl;
}
~Global() {
wcout << L"[" << hex << GetCurrentThreadId() << L"] Global::~Global()" << endl;
Sleep(3000);
wcout << L"[" << hex << GetCurrentThreadId() << L"] Global::~Global() - Done!" << endl;
}
} g;
// CLRHost.cpp
#include <windows.h>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
#include <iostream>
#include <iomanip>
using namespace std;
int wmain(int argc, const wchar_t* argv[])
{
HRESULT hr = S_OK;
ICLRMetaHost* pMetaHost = 0;
ICLRRuntimeInfo* pRuntimeInfo = 0;
ICLRRuntimeHost* pRuntimeHost = 0;
wchar_t version[MAX_PATH];
DWORD versionSize = _countof(version);
if (argc < 2) {
wcout << L"Usage: " << argv[0] << L" <assembly.dll>" << endl;
return 0;
}
if (FAILED(hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)))) {
goto out;
}
if (FAILED(hr = pMetaHost->GetVersionFromFile(argv[1], version, &versionSize))) {
goto out;
}
if (FAILED(hr = pMetaHost->GetRuntime(version, IID_PPV_ARGS(&pRuntimeInfo)))) {
goto out;
}
if (FAILED(hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pRuntimeHost)))) {
goto out;
}
if (FAILED(hr = pRuntimeHost->Start())) {
goto out;
}
DWORD dwRetVal = E_NOTIMPL;
if (FAILED(hr = pRuntimeHost->ExecuteInDefaultAppDomain(argv[1], L"T", L"M", L"", &dwRetVal))) {
wcerr << hex << hr << endl;
goto out;
}
wcout << dwRetVal << " returned." << endl;
if (FAILED(hr = pRuntimeHost->Stop())) {
goto out;
}
out:
if (pRuntimeHost) pRuntimeHost->Release();
if (pRuntimeInfo) pRuntimeInfo->Release();
if (pMetaHost) pMetaHost->Release();
return hr;
}
2012-12-02:
जहां तक मेरा व्यवहार बता सकते हैं इस प्रकार हो रहा है:
- एक मिश्रित-मोड EXE फ़ाइल में, वैश्विक विनाशकर्ता दौरान finalizers के रूप में चलाए जा रहे हैं डोमेनUnload चाहे वे मूल कोड या सीएलआर कोड पर रखा गया हो। विजुअल स्टूडियो 2008, 2010 और 2012 में यह मामला है।
- प्रबंधित मूल पद्धति के चलते DLL_PROCESS_DETACH के दौरान वैश्विक मूल वस्तुओं के लिए मूल अनुप्रयोग विनाशकों द्वारा होस्ट किए गए मिश्रित मोड डीएलएल में चलाया जाता है और अन्य सभी सफाई हुई है। वे कन्स्ट्रक्टर के समान धागे में भागते हैं और उनके साथ जुड़े समय (वांछित व्यवहार) नहीं होता है। जैसा कि वैश्विक प्रबंधित वस्तुओं के समय विनाशक (
/clr
के साथ संकलित फ़ाइलों में रखे गैर-रेफ कक्षाएं)ICLRPolicyManager->SetTimeout(OPR_ProcessExit, <timeout>)
का उपयोग करके नियंत्रित किया जा सकता है।
एक अनुमान hazarding, मुझे लगता है कि कारण वैश्विक देशी कंस्ट्रक्टर्स/विनाशकर्ता "सामान्य रूप से" में कार्य DLL परिदृश्य में (के रूप में मैं उम्मीद करेंगे व्यवहार कर के रूप में परिभाषित) देशी कार्यों पर LoadLibrary
और GetProcAddress
उपयोग करने की अनुमति है। इस प्रकार मैं उम्मीद करता हूं कि भविष्य में यह भविष्य में बदलने के लिए अपेक्षाकृत सुरक्षित नहीं है, लेकिन आधिकारिक स्रोतों/दस्तावेज़ीकरण से किसी तरह की पुष्टि/अस्वीकार करने की सराहना करता है।
अद्यतन 2:
विजुअल स्टूडियो 2012 में (एक्सप्रेस और प्रीमियम संस्करण के साथ परीक्षण किया है, मैं दुर्भाग्य से इस मशीन पर पहले के संस्करणों के लिए पहुँच नहीं है)। इसे कमांड लाइन (उपरोक्त उल्लिखित भवन) पर उसी तरह काम करना चाहिए, लेकिन यहां आईडीई के भीतर से पुन: उत्पन्न करने का तरीका बताया गया है।
बिल्डिंग CLRHost.exe:
- फ़ाइल -> नया प्रोजेक्ट
- विजुअल C++ -> Win32 -> Win32 कंसोल अनुप्रयोग (नाम परियोजना "CLRHost")
- आवेदन सेटिंग्स -> अतिरिक्त विकल्प -> खाली परियोजना
- प्रेस "समाप्त करें"
- समाधान एक्सप्लोरर में स्रोत फ़ाइलों पर राइट क्लिक करें। जोड़ें -> नया आइटम -> दृश्य सी ++ -> सी ++ फ़ाइल। इसे CLRHost.cpp नाम दें और पोस्ट से CLRHost.cpp की सामग्री पेस्ट करें।
- परियोजना -> गुण। कॉन्फ़िगरेशन गुण -> सी/सी ++ -> कोड जनरेशन -> "सी ++ अपवादों को सक्षम करें" में "एसईएच अपवाद (/ ईएचए)" और "मूल रनटाइम चेक" के साथ "डिफ़ॉल्ट"
- बिल्ड करें।
बिल्डिंग CLR.DLL:
- फ़ाइल -> नया प्रोजेक्ट
- विजुअल C++ -> CLR -> कक्षा लाइब्रेरी (नाम परियोजना "CLR")
- सभी स्वत: जनरेट की फ़ाइलें हटाएँ
- परियोजना -> गुण। कॉन्फ़िगरेशन गुण -> सी/सी ++ -> प्रीकंपिल्ड हेडर -> प्रीकंपिल्ड हेडर। "प्रीकंपील्ड हेडर का उपयोग न करें" में बदलें।
- समाधान एक्सप्लोरर में स्रोत फ़ाइलों पर राइट क्लिक करें। जोड़ें -> नया आइटम -> दृश्य सी ++ -> सी ++ फ़ाइल। इसे CLR.cpp नाम दें और पोस्ट से CLR.cpp की सामग्री पेस्ट करें।
- Native.cpp नामक एक नई सी ++ फ़ाइल जोड़ें और पोस्ट से कोड पेस्ट करें।
- समाधान एक्सप्लोरर में "Native.cpp" पर राइट क्लिक करें और गुण चुनें। बदलें सी/सी ++ -> सामान्य -> सामान्य भाषा रनटाइम समर्थन "कोई सामान्य भाषा रनटाइम समर्थन"
- प्रोजेक्ट -> गुण -> डिबगिंग। CLRhost.exe को इंगित करने के लिए "कमांड" को इंगित करने के लिए "कमांड तर्क" को "$ (लक्ष्यपैथ)" पर उद्धरण, "डिबगर प्रकार" को "मिश्रित"
- बनाएं और डीबग करें।
> clr.dll!Global::~Global() Line 11 C++ clr.dll!`dynamic atexit destructor for 'g''() + 0xd bytes C++ clr.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 416 C clr.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 522 + 0x11 bytes C clr.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 472 + 0x11 bytes C [email protected]() + 0x136 bytes [email protected]() + 0xad bytes [email protected]() + 0x14 bytes [email protected]() + 0x141 bytes [email protected]() + 0x74 bytes kernel32.dll!74e37a0d() mscoreei.dll!RuntimeDesc::ShutdownAllActiveRuntimes() + 0x10e bytes [email protected]() + 0x27 bytes [email protected]() + 0x94 bytes msvcr110d.dll!___crtCorExitProcess() + 0x3a bytes msvcr110d.dll!___crtExitProcess() + 0xc bytes msvcr110d.dll!__unlockexit() + 0x27b bytes msvcr110d.dll!_exit() + 0x10 bytes CLRHost.exe!__tmainCRTStartup() Line 549 C CLRHost.exe!wmainCRTStartup() Line 377 C [email protected]@12() + 0x12 bytes [email protected]() + 0x27 bytes [email protected]() + 0x1b bytes
(एक स्टैंडअलोन निष्पादन योग्य मैं एक स्टैक ट्रेस है कि बहुत हंस Passant द्वारा मनाया के समान है मिल के रूप में चल रहा है, हालांकि:
ग्लोबल के नाशक में एक ब्रेकपाइंट रखने निम्नलिखित स्टैक ट्रेस देता है यह सीआरटी की कामयाब संस्करण) का उपयोग नहीं कर रहा है:
> clrexe.exe!Global::~Global() Line 10 C++
clrexe.exe!`dynamic atexit destructor for 'g''() + 0xd bytes C++
msvcr110d.dll!__unlockexit() + 0x1d3 bytes
msvcr110d.dll!__cexit() + 0xe bytes
[Managed to Native Transition]
clrexe.exe!<CrtImplementationDetails>::LanguageSupport::_UninitializeDefaultDomain(void* cookie) Line 577 C++
clrexe.exe!<CrtImplementationDetails>::LanguageSupport::UninitializeDefaultDomain() Line 594 + 0x8 bytes C++
clrexe.exe!<CrtImplementationDetails>::LanguageSupport::DomainUnload(System::Object^ source, System::EventArgs^ arguments) Line 628 C++
clrexe.exe!<CrtImplementationDetails>::ModuleUninitializer::SingletonDomainUnload(System::Object^ source, System::EventArgs^ arguments) Line 273 + 0x6e bytes C++
[email protected]@12() + 0x12 bytes
[email protected]() + 0x27 bytes
[email protected]() + 0x1b bytes
आप CLRHost.exe के साथ काफी अटक गए हैं, सीएलआर लोड करने और इसे फिर से कॉन्फ़िगर करने की अनुमति देने के लिए आपके प्रोग्राम का एंट्रीपॉइंट अप्रबंधित होना चाहिए। यदि आप एक निष्पादन योग्य है तो यह आपके EXE में एम्बेडेड संसाधनों से असेंबली लोड करने के लिए IHostAssemblyStore हैक कर सकता है। एक साधारण सेटअप के साथ प्रतिस्पर्धी बनाने के लिए मुश्किल है।exe –
@ हंसपैसेंट: एक एकल EXE मेरी मुख्य चिंता नहीं है (वास्तविक आवेदन जो मेरा प्रश्न पहले से ही कई डीएलएल पर निर्भर करता है), मुझे इसके मूल भाग में रूचि है जैसे कि यह मानक मूल में होगा आवेदन (और मेजबान EXE नहीं होने से डीबगिंग इत्यादि आसान हो जाएगा)। अधिकांशतः मैं यह जानना चाहता हूं कि मैं कहां पढ़ सकता हूं कि यह लोड/होस्ट किए जाने के आधार पर अलग-अलग व्यवहार क्यों करता है। मुझे एक ऐसे दृष्टिकोण का उपयोग करने में असहज महसूस होता है जिसे मैं नहीं जानता (क्या सीआरटी/संकलक के अपडेट हो सकता है अचानक होस्टेड परिदृश्य EXE की तरह व्यवहार करता है?) – user786653
जो मुझे नहीं मिल रहा है वह है कि स्टैंडअलोन EXE में विनाशक रन ProcessExit के दौरान एक फाइनलाइज़र के रूप में, लेकिन होस्टेड परिदृश्य में विनाशक डीएलएल अनलोड के दौरान चलता है (ExecuteInDefaultAppDomain रिटर्न और क्लॉहोस्ट वैश्विक विनाशक के बिना जारी रहता है) यानी यह एक अंतिमकर्ता (जहां तक मैं कह सकता हूं) के रूप में नहीं चल रहा है। – user786653