Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2347122
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: WINDOWS

2008-07-23 18:41:49

一直以来, 在C#中使用 native DLL都只有一种方式, 那就是调用DLL中的导出函数.

后来, 发现了这样的需求, 在C中也需要回调C#中的函数, 主要原因可能有: 1. 应用系统中的某个功能C#已经提供在先, 不应该通过C再实现一遍, 这违反了最重要的软件工程原则之一: DRY(也是我个人高度赞同的原则). 2. 某些功能通过C#的类库实现更方便, 比如对XML的处理. 我的其它文章中讨论过关于C#向C中安装/注册回调函数及C中回调C#.

再后来, 在尝试用C#作一个测试pcre的前端UI时发现, 偶尔的偶尔, 还会有这样的需求: native DLL中不是导出了一个函数, 而是导出了一个变量的符号, 这个变量恰巧又是一个函数指针. 我先是没注意这个问题, 以为 pcre_free跟pcre_compile一样是导出函数, 后来在UI中一运行pcre_free就导出程序容易退出. 逐渐查到真正的原因是pcre_free乃一变量, 而非函数.

但它是PCRE API的一部分, 原型如下:
void (*pcre_free)(void *);

要使用它, 首先要有办法从DLL中得到这个变量的地址, 然后从其地址取得该地址存放的内容, 这个内容才是真正的函数指针:

[DllImport("kernel32.dll")]
private static extern int GetProcAddress(int hModule, string lpProcName);

[DllImport("kernel32.dll", EntryPoint = "LoadLibraryA")]
private static extern int LoadLibrary(string lpLibFileName);


C#代码中需要这两个导入函数, 目的是取得 pcre_free的地址(而非内容)


[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl) ]
delegate void pcre_free_FP(IntPtr compiled_pcre );

private static pcre_free_FP s_pcre_free_func = null;
///


/// Free the returned internal data from pcre_compile
///

/// the return value of pcre_compile

private static void pcre_free(IntPtr compied_pcre)
{
    if(s_pcre_free_func == null)
    {
        try
        {
            int h = LoadLibrary("pcre.dll");
            if( h == 0)
            {
                Trace.Assert(false, @"Fail to call LoadLibrary(""pcre.dll"")");
                return;
            }

            int addr = GetProcAddress(h, "pcre_free");
            if( addr == 0)
            {
                Trace.Assert(false, @"Fail to call GetProcAddress(h, ""pcre_free"")");
                return;
            }

            IntPtr addr_val = Marshal.ReadIntPtr( (IntPtr)addr);
            s_pcre_free_func = Marshal.GetDelegateForFunctionPointer(
                                 addr_val, typeof(pcre_free_FP)) as pcre_free_FP;
        }
        catch (Exception ex)
        {
            Trace.Assert(false, ex.ToString());
            return ;
        }
    }

    s_pcre_free_func.Invoke(compied_pcre);
}


我几乎从没想过这件事: GetProcAddress 也可以得一个变量的地址, 它其实只是得到一个导出符号的地址. 尽管函数名字以偏概全, 而且MSDN上也不提及这一点.


Marshal.ReadIntPtr( (IntPtr)addr); 很重要, 它的返回值才是真正的函数地址, 通过这个函数地址, 才能构造出来一个delegate类型的实例来作为调用pcre_free的代理.
阅读(1337) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~