相当于C(WinAPI)中的DllMain的C#
我有一个较旧的应用程序(约2005年),它接受的DLL插件。 该应用程序最初是为Win32 C插件devise的,但我有一个工作的C#dll模板。 我的问题:我需要做一些一次性的初始化,在Win32 C DLL将在DllMain完成:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { [one-time stuff here...] }
有没有一个C#相当于这个? 我没有C#模板中的“DllMain”。 我尝试了一个字面的C#解释,但没有去:该DLL的作品,但它不会触发DllMainfunction。
public static bool DllMain(int hModule, int reason, IntPtr lpReserved) { [one time stuff here...] }
给你的类一个静态构造函数,并在那里进行初始化。 它将在任何人第一次调用你的类的静态方法或属性或构造你的类的实例时运行。
我必须与旧版应用程序进行交互,可能与您的情况相同。 我发现了一个奇怪的方式来获得CLR程序集中的DllMainfunction。 幸运的是,这并不难。 它需要一个额外的DLL,但它不需要你部署一个额外的DLL,所以你仍然可以有“将DLL放在该目录中,应用程序将加载它”的范例。
首先,制作一个简单的常规C ++ DLL,如下所示:
dllmain.cpp:
#define WIN32_LEAN_AND_MEAN #include <windows.h> #include "resource.h" extern void LaunchDll( unsigned char *dll, size_t dllLength, char const *className, char const *methodName); static DWORD WINAPI launcher(void* h) { HRSRC res = ::FindResourceA(static_cast<HMODULE>(h), MAKEINTRESOURCEA(IDR_DLLENCLOSED), "DLL"); if (res) { HGLOBAL dat = ::LoadResource(static_cast<HMODULE>(h), res); if (dat) { unsigned char *dll = static_cast<unsigned char*>(::LockResource(dat)); if (dll) { size_t len = SizeofResource(static_cast<HMODULE>(h), res); LaunchDll(dll, len, "MyNamespace.MyClass", "DllMain"); } } } return 0; } extern "C" BOOL APIENTRY DllMain(HMODULE h, DWORD reasonForCall, void* resv) { if (reasonForCall == DLL_PROCESS_ATTACH) { CreateThread(0, 0, launcher, h, 0, 0); } return TRUE; }
注意线程创build。 这是为了保持Windows的快乐,因为调用DLL入口点内的托pipe代码是一个禁忌。
接下来,你必须创buildLaunchDll函数上面引用的代码。 这是在一个单独的文件,因为它将被编译为托pipe的C ++代码单元。 为此,首先创build.cpp文件(我称之为LaunchDll.cpp)。 然后右键单击您的项目中的configuration属性 – > C / C ++ – > General将通用语言运行时支持条目更改为公共语言运行时支持(/ clr) 。 你不能有exception,最小的重build,运行时检查,可能还有一些我忘记的东西,但编译器会告诉你。 当编译器抱怨时,追踪你从默认设置改变了多less设置,并只在LaunchDll.cpp文件上改变它们。
LaunchDll.cpp:
#using <mscorlib.dll> // Load a managed DLL from a byte array and call a static method in the DLL. // dll - the byte array containing the DLL // dllLength - the length of 'dll' // className - the name of the class with a static method to call. // methodName - the static method to call. Must expect no parameters. void LaunchDll( unsigned char *dll, size_t dllLength, char const *className, char const *methodName) { // convert passed in parameter to managed values cli::array<unsigned char>^ mdll = gcnew cli::array<unsigned char>(dllLength); System::Runtime::InteropServices::Marshal::Copy( (System::IntPtr)dll, mdll, 0, mdll->Length); System::String^ cn = System::Runtime::InteropServices::Marshal::PtrToStringAnsi( (System::IntPtr)(char*)className); System::String^ mn = System::Runtime::InteropServices::Marshal::PtrToStringAnsi( (System::IntPtr)(char*)methodName); // used the converted parameters to load the DLL, find, and call the method. System::Reflection::Assembly^ a = System::Reflection::Assembly::Load(mdll); a->GetType(cn)->GetMethod(mn)->Invoke(nullptr, nullptr); }
现在是非常棘手的部分。 您可能注意到在dllmain.cpp中加载的资源:launcher()。 这样做是检索第二个DLL作为资源插入在这里创build的DLL。 为此,请通过右键单击 – > 添加 – > 新build项目 – > Visual C ++ – > 资源 – > 资源文件(.rc)来创build资源文件 。 那么,你需要确保有一条线,如:
RESOURCE.RC:
IDR_DLLENCLOSED DLL "C:\\Path\\to\\Inner.dll"
在文件中。 (Tricky,是吧?)
剩下的唯一要做的就是创buildInner.dll程序集。 但是,你已经拥有了! 这就是你试图用你的遗留应用程序首先推出的东西。 只要确保包含一个带有公共无效DllMain()方法的MyNamespace.MyClass类(当然,您可以随意调用这些函数,这些只是硬编码到上面的dllmain.cpp:launcher()中的值。
因此,总而言之,上面的代码将现有的托pipeDLL插入到非托pipeDLL的资源中,该托pipeDLL在附加到进程后将从资源加载托pipe的DLL并调用其中的方法。
作为练习留给阅读者是更好的错误检查,加载不同的DLL的debugging和发布等模式,调用DllMainreplace与传递给真正的DllMain相同的参数(这个例子只对DLL_PROCESS_ATTACH),硬编码其他外部DLL中的内部DLL的方法作为通过方法。
从C#中也不容易,你可以有一个模块初始化器
模块可能包含称为模块初始化程序的特殊方法来初始化模块本身。 所有的模块可能都有一个模块初始化器。 这个方法应该是静态的,是模块的一个成员,不带参数,不返回任何值,用rtspecialname和specialname标记,并命名为.cctor。 对模块初始化程序中允许的代码没有限制。 允许模块初始化器运行并调用托pipe代码和非托pipe代码。
即使C#不直接支持模块初始化,我们可以使用reflection和静态构造函数来实现它。 为此,我们可以定义一个自定义属性,并使用它来查找需要在模块加载时初始化的类:
public class InitOnLoadAttribute : Attribute {} private void InitAssembly(Assembly assembly) { foreach (var type in GetLoadOnInitTypes(assembly)){ var prop = type.GetProperty("loaded", BindingFlags.Static | BindingFlags.NonPublic); //note that this only exists by convention if(prop != null){ prop.GetValue(null, null); //causes the static ctor to be called if it hasn't already } } } static IEnumerable<Type> GetLoadOnInitTypes(Assembly assembly) { foreach (Type type in assembly.GetTypes()) { if (type.GetCustomAttributes(typeof(InitOnLoadAttribute), true).Length > 0){ yield return type; } } } public MyMainClass() { //init newly loaded assemblies AppDomain.CurrentDomain.AssemblyLoad += (s, o) => InitAssembly(o.LoadedAssembly); //and all the ones we currently have loaded foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()){ InitAssembly(assembly); } }
在我们需要立即初始化的类上,我们将这些代码添加到它们的静态构造函数中(即使多次访问属性getter也会运行一次),并添加我们添加的自定义属性来公开这个function。
[InitOnLoad] class foo { private static bool loaded { get { return true; } } static foo() { int i = 42; } }