DLL’ler ve Visual C++ çalışma zamanı kitaplığı davranışı
Visual Studio kullanarak dinamik bağlantı kitaplığı (DLL) oluşturduğunuzda, bağlayıcı varsayılan olarak Visual C++ çalışma zamanı kitaplığını (VCRuntime) içerir. VCRuntime, C/C++ yürütülebilir dosyasını başlatmak ve sonlandırmak için gereken kodu içerir. Bir DLL'ye bağlanıldığında, VCRuntime kodu bir işleme veya iş parçacığına eklemek veya bu iş parçacığından ayırmak için DLL'ye Windows işletim sistemi iletilerini işleyen adlı _DllMainCRTStartup
bir iç DLL giriş noktası işlevi sağlar. İşlev, _DllMainCRTStartup
yığın arabelleği güvenlik kurulumu, C çalışma zamanı kitaplığı (CRT) başlatma ve sonlandırma gibi temel görevleri gerçekleştirir ve statik ve genel nesneler için oluşturuculara ve yıkıcılara çağrı yapar. _DllMainCRTStartup
ayrıca Kendi başlatma ve sonlandırma işlemlerini gerçekleştirmek için WinRT, MFC ve ATL gibi diğer kitaplıklar için kanca işlevlerini çağırır. Bu başlatma olmadan, CRT ve diğer kitaplıkların yanı sıra statik değişkenleriniz başlatılmamış durumda bırakılır. DLL'nizin statik olarak bağlı bir CRT veya dinamik olarak bağlı bir CRT DLL kullanması farketmeksizin aynı VCRuntime iç başlatma ve sonlandırma yordamları çağrılır.
Varsayılan DLL giriş noktası _DllMainCRTStartup
Windows'da, tüm DLL'ler hem başlatma hem de sonlandırma için çağrılan, genellikle adlı DllMain
isteğe bağlı bir giriş noktası işlevi içerebilir. Bu size gerektiğinde ek kaynaklar ayırma veya serbest bırakma fırsatı verir. Windows giriş noktası işlevini dört durumda çağırır: işlem ekleme, işlem ayırma, iş parçacığı ekleme ve iş parçacığı ayırma. Dll bir işlem adres alanına yüklendiğinde, onu kullanan bir uygulama yüklendiğinde veya uygulama çalışma zamanında DLL'yi istediğinde, işletim sistemi DLL verilerinin ayrı bir kopyasını oluşturur. Buna işlem ekleme adı verilir. DLL'nin yüklendiği işlem yeni bir iş parçacığı oluşturduğunda iş parçacığı ekleme gerçekleşir. İş parçacığı ayırma , iş parçacığı sonlandırıldığında gerçekleşir ve işlem ayırma , DLL'nin artık gerekli olmadığı ve bir uygulama tarafından serbest bırakıldığı durumlardır. İşletim sistemi, bu olayların her biri için DLL giriş noktasına ayrı bir çağrı yapar ve her olay türü için bir neden bağımsız değişkeni geçirir. Örneğin, işletim sistemi sinyal işlemi eklemeye neden bağımsız değişkeni olarak gönderirDLL_PROCESS_ATTACH
.
VCRuntime kitaplığı, varsayılan başlatma ve sonlandırma işlemlerini işlemek için adlı _DllMainCRTStartup
bir giriş noktası işlevi sağlar. İşlem ekleme sırasında _DllMainCRTStartup
işlev arabellek güvenlik denetimlerini ayarlar, CRT ve diğer kitaplıkları başlatır, çalışma zamanı türü bilgilerini başlatır, statik ve yerel olmayan veriler için oluşturucuları başlatır ve çağırır, iş parçacığı yerel depolamayı başlatır, her ekleme için bir iç statik sayacı artırır ve sonra kullanıcı veya kitaplık tarafından sağlanan DllMain
bir öğesini çağırır. İşlem ayırma işleminde işlev bu adımları tersten ilerler. çağrısında DllMain
bulunur, iç sayacı yok eder, yok edicileri çağırır, CRT sonlandırma işlevlerini ve kayıtlı atexit
işlevleri çağırır ve diğer sonlandırma kitaplıklarına bildirir. Ek sayacı sıfıra gittiğinde, işlev Windows'a DLL'nin kaldırılabildiğini göstermek için geri döner FALSE
. İşlev, _DllMainCRTStartup
iş parçacığı ekleme ve iş parçacığı ayırma sırasında da çağrılır. Böyle durumlarda, VCRuntime kodu kendi başına ek başlatma veya sonlandırma gerçekleştirmez ve yalnızca iletiyi iletmek için çağırır DllMain
. İşlem ekleme, sinyal hatasından döndürülüyorsa DllMain
FALSE
, _DllMainCRTStartup
yeniden çağrılar DllMain
DLL_PROCESS_DETACH
ve neden bağımsız değişkeni olarak geçerse sonlandırma işleminin geri kalanından geçer.
Visual Studio'da DLL'ler oluşturulurken, VCRuntime tarafından sağlanan varsayılan giriş noktası _DllMainCRTStartup
otomatik olarak bağlanır. /ENTRY (Giriş noktası simgesi) bağlayıcı seçeneğini kullanarak DLL'niz için bir giriş noktası işlevi belirtmeniz gerekmez.
Not
/ENTRY: bağlayıcı seçeneğini kullanarak DLL için başka bir giriş noktası işlevi belirtmek mümkün olsa da, giriş noktası işlevinizin aynı sırada olan _DllMainCRTStartup
her şeyi yinelemesi gerekeceğinden bunu önermeyiz. VCRuntime, davranışını yinelemenize olanak sağlayan işlevler sağlar. Örneğin, /GS (Arabellek güvenlik denetimi) arabellek denetimi seçeneğini desteklemek için işlem ekleme sırasında __security_init_cookie hemen çağırabilirsiniz. DLL başlatma veya sonlandırma işlevlerinin _CRT_INIT
geri kalanını gerçekleştirmek için giriş noktası işleviyle aynı parametreleri geçirerek işlevini çağırabilirsiniz.
DLL başlatma
DLL'niz, DLL'niz yüklendiğinde yürütülmesi gereken başlatma koduna sahip olabilir. Kendi DLL başlatma ve sonlandırma işlevlerinizi gerçekleştirebilmeniz için, _DllMainCRTStartup
sağlayabileceğiniz adlı DllMain
bir işlevi çağırır. Dll giriş noktası için gerekli imzaya DllMain
sahip olmanız gerekir. Varsayılan giriş noktası işlevi _DllMainCRTStartup
, Windows tarafından geçirilen parametreleri kullanarak çağırır DllMain
. Varsayılan olarak, bir işlev sağlamazsanız, Visual Studio sizin için bir DllMain
işlev sağlar ve her zaman çağıracak bir şey olması _DllMainCRTStartup
için bunu bağlar. Bu, DLL'nizi başlatmanız gerekmiyorsa DLL'nizi oluştururken yapmanız gereken özel bir şey olmadığı anlamına gelir.
Bu, için DllMain
kullanılan imzadır:
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved); // reserved
Bazı kitaplıklar işlevi sizin için sarmalar DllMain
. Örneğin, normal bir MFC DLL'sinde, DLL'nizin gerektirdiği başlatma ve sonlandırma işlemlerini gerçekleştirmek için nesnenin InitInstance
ve ExitInstance
üye işlevlerini uygulayınCWinApp
. Daha fazla ayrıntı için Normal MFC DLL'lerini başlatma bölümüne bakın.
Uyarı
DLL giriş noktasında güvenle yapabileceklerinizde önemli sınırlar vardır. içinde aranması güvenli olmayan belirli Windows API'leri DllMain
hakkında daha fazla bilgi için bkz . Genel En İyi Yöntemler. En basit başlatma dışında herhangi bir şeye ihtiyacınız varsa bunu DLL için bir başlatma işlevinde yapın. Uygulamaların başlatıldıktan sonra DllMain
ve DLL'deki diğer işlevleri çağırmadan önce başlatma işlevini çağırmasını gerektirebilirsiniz.
Normal (MFC olmayan) DLL'leri başlatma
VCRuntime tarafından sağlanan _DllMainCRTStartup
giriş noktasını kullanan normal (MFC olmayan) DLL'lerde kendi başlatmanızı gerçekleştirmek için, DLL kaynak kodunuz adlı DllMain
bir işlev içermelidir. Aşağıdaki kod, tanımının DllMain
nasıl görünebileceğini gösteren temel bir iskelet sunar:
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved) // reserved
{
// Perform actions based on the reason for calling.
switch (reason)
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
Not
Eski Windows SDK belgelerinde, DLL giriş noktası işlevinin gerçek adının bağlayıcı komut satırında /ENTRY seçeneğiyle belirtilmesi gerektiği belirtilir. Visual Studio ile, giriş noktası işlevinizin DllMain
adı ise /ENTRY seçeneğini kullanmanız gerekmez. Aslında, /ENTRY seçeneğini kullanır ve giriş noktası işlevinizi dışında DllMain
bir adla adlandırırsanız, giriş noktası işleviniz aynı başlatma çağrılarını _DllMainCRTStartup
yapmadığı sürece CRT düzgün başlatılmaz.
Normal MFC DLL'lerini başlatma
Normal MFC DLL'lerinin bir CWinApp
nesnesi olduğundan, başlatma ve sonlandırma görevlerini bir MFC uygulamasıyla aynı konumda gerçekleştirmeleri gerekir: DLL'nin CWinApp
türetilmiş sınıfının ve ExitInstance
üye işlevlerindeInitInstance
. MFC ve için tarafından _DllMainCRTStartup
çağrılan bir DllMain
işlev sağladığındanDLL_PROCESS_ATTACH
, kendi DllMain
işlevinizi yazmamalısınız.DLL_PROCESS_DETACH
MFC tarafından sağlanan DllMain
işlev, DLL'niz yüklendiğinde ve InitInstance
DLL kaldırilmeden önce çağırdığında çağırır ExitInstance
.
Normal bir MFC DLL işlevinde TlsAlloc ve TlsGetValue çağrısı yaparak birden çok iş parçacığını InitInstance
izleyebilir. Bu işlevler DLL'nin iş parçacığına özgü verileri izlemesine olanak sağlar.
MFC'ye dinamik olarak bağlanan normal MFC DLL'nizde, sırasıyla herhangi bir MFC OLE, MFC Veritabanı (veya DAO) veya MFC Yuva desteği kullanıyorsanız, hata ayıklama MFC uzantısı DLL'leri MFCOsürümüD.dll, MFCDsürümüD.dll ve MFCNsürümD.dll (sürüm numarasıdır) otomatik olarak bağlanır. Normal MFC DLL'nizde CWinApp::InitInstance
kullandığınız bu DLL'lerin her biri için aşağıdaki önceden tanımlanmış başlatma işlevlerinden birini çağırmanız gerekir.
MFC desteğinin türü | Çağrılacak başlatma işlevi |
---|---|
MFC OLE (MFCOsürümD.dll) | AfxOleInitModule |
MFC Veritabanı (MFCDsürümüD.dll) | AfxDbInitModule |
MFC Yuvaları (MFCNsürümüD.dll) | AfxNetInitModule |
MFC uzantısı DLL'lerini başlatma
MFC uzantısı DLL'lerinin türetilmiş bir CWinApp
nesnesi olmadığından (normal MFC DLL'lerinde olduğu gibi), başlatma ve sonlandırma kodunuzu MFC DLL Sihirbazı'nın DllMain
oluşturduğu işleve eklemeniz gerekir.
Sihirbaz MFC uzantısı DLL'leri için aşağıdaki kodu sağlar. Kodda, PROJNAME
projenizin adı için bir yer tutucudur.
#include "pch.h" // For Visual Studio 2017 and earlier, use "stdafx.h"
#include <afxdllx.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static AFX_EXTENSION_MODULE PROJNAMEDLL;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("PROJNAME.DLL Initializing!\n");
// MFC extension DLL one-time initialization
AfxInitExtensionModule(PROJNAMEDLL,
hInstance);
// Insert this DLL into the resource chain
new CDynLinkLibrary(Dll3DLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("PROJNAME.DLL Terminating!\n");
}
return 1; // ok
}
Başlatma sırasında yeni CDynLinkLibrary
bir nesne oluşturmak, MFC uzantısı DLL'sinin nesneleri veya kaynakları istemci uygulamasına dışarı aktarmasına CRuntimeClass
olanak tanır.
Bir veya daha fazla normal MFC DLL'sinden MFC uzantı DLL'nizi kullanacaksanız, nesne oluşturan bir CDynLinkLibrary
başlatma işlevini dışarı aktarmanız gerekir. Bu işlev, MFC uzantısı DLL'sini kullanan normal MFC DLL'lerinin her birinden çağrılmalıdır. Bu başlatma işlevini çağırmak için uygun bir yer, MFC uzantı DLL'sinin InitInstance
dışarı aktarılan sınıflarından veya işlevlerinden herhangi birini kullanmadan önce normal MFC DLL'sinin CWinApp
türetilmiş nesnesinin üye işlevindedir.
MFC DLL Sihirbazı'nın DllMain
oluşturduğu içinde, modülün çalışma zamanı sınıflarını (CRuntimeClass
yapıları) ve nesne oluşturulduğunda kullanılacak CDynLinkLibrary
nesne fabrikalarını (COleObjectFactory
nesneleri) yakalama çağrısıAfxInitExtensionModule
. değerinin dönüş değerini AfxInitExtensionModule
denetlemeniz gerekir; değerinden AfxInitExtensionModule
sıfır değer döndürülürse işlevinizden DllMain
sıfır döndürür.
MFC uzantı DLL'niz açıkça bir yürütülebilir dosyaya bağlanacaksa (dll'ye bağlanmak için yürütülebilir çağrılar AfxLoadLibrary
anlamına gelir), üzerinde DLL_PROCESS_DETACH
öğesine AfxTermExtensionModule
bir çağrı eklemeniz gerekir. Bu işlev, her işlem MFC uzantı DLL'sinden ayrıldığında MFC uzantısı DLL'sini temizlemesine olanak tanır (işlem çıktığında veya bir AfxFreeLibrary
çağrı sonucunda DLL kaldırıldığında gerçekleşir). MFC uzantı DLL'niz uygulamaya örtük olarak bağlanacaksa çağrısı AfxTermExtensionModule
gerekli değildir.
MFC uzantısı DLL'lerine açıkça bağlanan uygulamaların DLL'yi serbest bırakırken çağrısı AfxTermExtensionModule
yapması gerekir. Ayrıca, uygulama birden çok iş parçacığı kullanıyorsa ve AfxFreeLibrary
(Win32 işlevleri LoadLibrary
yerine ve FreeLibrary
) kullanmalıdırAfxLoadLibrary
. ve AfxLoadLibrary
AfxFreeLibrary
kullanılması, MFC uzantısı DLL yüklendiğinde ve kaldırıldığında yürütülen başlatma ve kapatma kodunun genel MFC durumunu bozmamasını sağlar.
MFCx0.dll çağrılınca tamamen başlatıldığından DllMain
, içinde bellek ayırabilir ve MFC işlevlerini DllMain
çağırabilirsiniz (MFC'nin 16 bit sürümünden farklı olarak).
Uzantı DLL'leri işlevindeki ve DLL_THREAD_DETACH
durumlarını işleyerek DLL_THREAD_ATTACH
çoklu iş parçacığı kullanımıyla DllMain
ilgilenebilir. İş parçacıkları DLL'ye DllMain
eklendiğinde ve dll'den ayrıldığında bu durumlar geçirilir. Dll eklenirken TlsAlloc çağrısı, DLL'nin DLL'ye bağlı her iş parçacığı için iş parçacığı yerel depolama (TLS) dizinlerini korumasını sağlar.
Afxdllx.h üst bilgi dosyasının MFC uzantısı DLL'lerinde kullanılan yapılar için ve CDynLinkLibrary
tanımı gibi özel tanımlar AFX_EXTENSION_MODULE
içerdiğini unutmayın. Bu üst bilgi dosyasını MFC uzantı DLL'nize eklemelisiniz.
Not
pch.h dosyasındaki _AFX_NO_XXX
(Visual Studio 2017 ve önceki sürümlerde stdafx.h) makroları tanımlamanız veya tanımlamamanız önemlidir. Bu makrolar yalnızca belirli bir hedef platformun bu özelliği destekleyip desteklemediğini denetlemek amacıyla bulunur. Bu makroları denetlemek için programınızı yazabilirsiniz (örneğin, #ifndef _AFX_NO_OLE_SUPPORT
), ancak programınız bu makroları hiçbir zaman tanımlamamalı veya tanımlarını kaldırmamalıdır.
Çoklu iş parçacıklarını işleyen bir örnek başlatma işlevi, Windows SDK'sında Dinamik Bağlantı Kitaplığında İş Parçacığı Yerel Depolama Kullanma bölümüne dahildir. Örnekte adlı LibMain
bir giriş noktası işlevi bulunduğunu, ancak MFC ve C çalışma zamanı kitaplıklarıyla çalışması için bu işlevi DllMain
adlandırmanız gerektiğini unutmayın.
Ayrıca bkz.
Visual Studio'da C/C++ DLL'leri oluşturma
DllMain giriş noktası
Dinamik Bağlantı Kitaplığı En İyi Yöntemleri