yidabu 2007-4-27 19:03
C#-like 的 DLL 封装
C#-like 的 DLL 封装知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070427点击下面网址查看原文:http://www.d-programming-language-china.org by: oldrev from: http://oldrev.javaeye.com/blog/71604 一个类似 C# 的 DllImport 实现,用于“半”动态加载 DLL。用起来比我以前写的 DLLWrapper 要麻烦一些,但是 DLLWrapper 由于使用一个 Tuple 来存储函数声明,会造成超长的标识符导致编译错误,这个 DllImport 避免了这个问题。 这个实现有一个缺陷是每次调用API函数的时候都会执行一次 GetProcAddress,效率比较低.... 谁能告诉我怎么避免该死的 CTFE? [Copy to clipboard] [ - ]CODE: // DllImport - A C#-like DLL Wrapper // written by oldrev (wstring#gmail.com) // License: BSD import std.stdio; import std.typetuple; import std.utf; import std.c.windows.windows; import std.traits; import std.string; import singleton; extern(Windows) { HMODULE LoadLibraryW(LPCWSTR libPath); } private static class ModuleManager { private static HMODULE [char[]] m_modules; private this() { } static public ~this() { foreach(h; m_modules) { FreeLibrary(h); } } private static HMODULE registerModule(char[] name) { char[] lname = tolower(name); HMODULE h = LoadLibraryW(toUTF16z(lname)); if(h is null) throw new Exception("Failed to load DLL: " ~ name); m_modules[lname] = h; return h; } public static HMODULE getHandle(char[] name) { return m_modules[name]; } public static ProcType getSymbol(ProcType)(char[] moduleName, char[] procName) { HMODULE handle = null; if(moduleName in m_modules) handle = m_modules[moduleName]; else handle = registerModule(moduleName); assert(handle !is null); return cast(ProcType)GetProcAddress(handle, toStringz(procName)); } } struct DllImport(char[] ModuleName, char[] ProcName, FT) { extern(Windows) alias ReturnType!(FT) function(ParameterTypeTuple!(FT)) FunctionType; // 非要这样重新绑定 extern(Windows),是不是编译器的 bug? // extern(Windows) alias FT FunctionType; // 这样就不行 //怎么避免 CTFE? //FIXME: //FunctionType m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName); public ReturnType!(FunctionType) opCall(ParameterTypeTuple!(FunctionType) args) { FunctionType m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName); return m_funcPtr(args); } } void main() { DllImport!("user32.dll", "MessageBoxA", int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) MessageBox; MessageBox(null, "Text", "Title", MB_OK); } 0.0.0.0.0.2 版,要稍微高效一点: [Copy to clipboard] [ - ]CODE: import std.typetuple; import std.c.windows.windows; import std.traits; import std.string; import std.utf; extern(Windows) { HMODULE LoadLibraryW(LPCWSTR libPath); } private static class ModuleManager { private static HMODULE [char[]] m_modules; private this() { } static public ~this() { foreach(h; m_modules) { FreeLibrary(h); } } private static HMODULE registerModule(char[] name) { char[] lname = tolower(name); HMODULE h = LoadLibraryW(toUTF16z(lname)); if(h is null) throw new Exception("Failed to load DLL: " ~ name); m_modules[lname] = h; return h; } public static HMODULE getHandle(char[] name) { return m_modules[name]; } public static ProcType getSymbol(ProcType)(char[] moduleName, char[] procName) { HMODULE handle = null; if(moduleName in m_modules) handle = m_modules[moduleName]; else handle = registerModule(moduleName); assert(handle !is null); ProcType proc = cast(ProcType)GetProcAddress(handle, toStringz(procName)); if(proc is null) throw new Exception("Cannot to get the address of " ~ procName); return proc; } } struct DllImport(char[] ModuleName, char[] ProcName, FT) { extern(Windows) alias ReturnType!(FT) function(ParameterTypeTuple!(FT)) FunctionType; alias DllImport!(ModuleName, ProcName, FT) SelfType; //FIXME: avoid the CTFE? private FunctionType m_funcPtr = null; public ReturnType!(FunctionType) opCall(ParameterTypeTuple!(FunctionType) args) { if(m_funcPtr is null) m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName); return m_funcPtr(args); } } void main() { DllImport!("user32.dll", "MessageBoxA", int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) MessageBox; MessageBox(null, "Text", "Title", MB_OK); } qiezi 2007-04-17 10:31 我觉得上一版使用更方便呢,效率应该也会高一些吧。 oldrev 2007-04-17 11:31 问题是 DMD 限制标识符为 4k 个字符,上一版只要稍微多定义几个函数就出错了 qiezi 2007-04-17 11:59( 本文出处: http://www.d-programming-language-china.org ) 代码 [Copy to clipboard] [ - ]CODE: Dll!("user32.dll") user32; user32.DllImport!("MessageBoxA", int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType))MessageBox; 或者: [Copy to clipboard] [ - ]CODE: DllImport!(user32, "MessageBoxA", int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType))MessageBox; 改成这种呢?我不大喜欢HMODULE [char[]] m_modules这种用字符串作key的,相对路径和绝对路径都无法统一处理。 另外DLL里面没有指定必须是stdcall调用吧?如果是cdecl怎么办?还得加一个调用约定参数吧? oldrev 2007-04-17 13:32 有没有办法可以反射出一个struct或class的方法的名字 qiezi 2007-04-17 13:45 class有classinfo,它包括名字,struct我就不清楚了,应该也有。不过这些好像是运行时的,编译时可以用模板的alias参数去取,前面我提到过几次了,pyd里面用了这种方法取的参数名字。( 本文出处: http://www.d-programming-language-china.org ) oldrev 2007-04-17 14:37 我查了 ClassInfo 类,没有方法名字,只有类的名字 qiezi 2007-04-17 15:06 方法名字只能用其它方式得到,比如stringof DavidL 2007-04-20 13:26 可以直接用bindings里的win32/winuser.d来调用这些dll吧( lastupdate:20070427 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:yidabu提倡在交流中学习,在分享中提高收集感兴趣的知识,写下心得,通过网络与别人一起分享理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台