知识若不分享 实在没有意义 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?
// 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 版,要稍微高效一点:
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 )
代码
Dll!("user32.dll") user32;
user32.DllImport!("MessageBoxA", int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType))MessageBox;
或者:
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为大家提供一个学习交流各种知识的平台