发新话题
打印

[转载D语言文档] 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?

// 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为大家提供一个学习交流各种知识的平台

TOP

发新话题