解决方案 »

  1.   

    http://blog.sina.com.cn/s/blog_79ba23780101hedb.html
      

  2.   

    理论上说PInvoke也可以访问成员函数,因为成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。但是因为C#操作C++对象比较麻烦,这样不推荐,你可以用C++/CLI(也就是C++.NET)作为代理去访问原生C++类,再包装出来给C#用。或者用原生C++编写dll,将成员函数转换为一般的函数。
      

  3.   

    托管C++就是C++,因此调用unmanaged C++类库没有问题。然后在暴露managed接口,这样C#就可以调用了。用C做中间层一样的道理,C调用C++库,在暴露纯C的接口,给platform用。
      

  4.   

    Native C++
    class CNative
    {
    public:
      int NativeFunction(int a) {}
    }
    C++/CLI
    public ref class CNativeWrapper
    {
    private:
      CNative *m_pNative;
    public:
      CNativeWrapper()  { m_pNative = new CNative; }
      int ManagedMethod(int a) { return m_pNative->NativeFunction(a); }
      ~CNativeWrapper() { delete m_pNative; }
    protected:
      !CNativeWrapper() { delete m_pNative; }
    }
    C#
    class Program
    {
      int Main()
      {
        var nativeWrapper = new CNativeWrapper();
        nativeWrapper.ManagedMethod();
        return 0;
      }
    }
      

  5.   

    感谢楼上各位的帮助!现在我去试试用托管c++封装,
    另外2楼说:“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。”我也去试试!
      

  6.   

    刚才去试了下2楼的想法,2楼所说“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上”,这事儿办不到....声明一个指向c++的某个类的指针,这怎么可能在C#中做到呢?C++中的struct倒可以在C#中重写个struct,再作为参数声明。C++中的class不可能在C#中重写啊........另外,刚才我建了个C++DLL工程,导出了一个全局函数。,在C#中用PInvoke调用,怎么会提示:“未将引用设置到对象的实例”?我这个C++全局函数不需要实例化任何对象啊,怎么会出这样的错呢?网上某些人说C++导出函数必须声明为extern "c",我认为这是错误的,extern c只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。所以各位使用过pinvoke的,C++导出函数并没有必要写extern c,对吧?那么我这个异常究竟是为何引起的呢......
      

  7.   

    “未将引用设置到对象的实例”?问题找到了.....我dllimport时DLL名字写错了.......!
    另外关于2楼,我发现只要我的成员函数定义中,没有操作数据成员的话(例如给某成员赋值),就可以在C#中被成功调用。也就是C++中那个隐含的this参数,自动被C#去掉了....C#这么智能啊。当然,如果此类成员函数中有对成员变量赋值的操作,那么自然会出错,因为C#中根本没有实例化过此C++类的实例。那么看来对于这种类成员函数,只能用托管C++封装了,PInvoke无能为力。剩下的问题就是:
    ①:2楼所说“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上”,这事儿办不到....声明一个指向c++的某个类的指针,这怎么可能在C#中做到呢?C++中的struct倒可以在C#中重写个struct,再作为参数声明。C++中的class不可能在C#中重写啊........
    ②:网上某些人说C++导出函数必须声明为extern "c",我认为这是错误的,extern c只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。  对吧?
      

  8.   

    给你一个例程。
    class CFORCS_API CCForCS
    {
    public:
    CCForCS(void);
    CCForCS(int nValue);
    ~CCForCS(void);
    public:
    void Print(LPCWSTR pszMessage);
    int GetValue(void);
    protected:
    virtual int VFunctionBefore(LPCWSTR pszMessage, int *pnValue);
    virtual void VFunctionAfter(LPCWSTR pszMessage, int nValue);
    private:
    int _nValue;
    };
      

  9.   

    class CVTable : IDisposable
    {
    IntPtr _vtable;
    public CVTable(params Delegate[] methods)
    {
    _vtable = Marshal.AllocHGlobal(methods.Length * Marshal.SizeOf(typeof(IntPtr)));
    for (int i = 0; i < methods.Length; ++i)
    {
    Marshal.WriteIntPtr(_vtable, i * Marshal.SizeOf(typeof(IntPtr)), Marshal.GetFunctionPointerForDelegate(methods[i]));
    }
    }
    public void Dispose()
    {
    Dispose(true);
    GC.SuppressFinalize(this);
    } private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
    if (!this.disposed)
    {
    if (_vtable != IntPtr.Zero)
    {
    Marshal.FreeHGlobal(_vtable);
    _vtable = IntPtr.Zero;
    }
    disposed = true;
    }
    } ~CVTable()
    {
    Dispose(false);
    } public IntPtr CreateVTable(IntPtr @this)
    {
    IntPtr old = Marshal.ReadIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)));
    Marshal.WriteIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)), _vtable);
    return old;
    }
    public void ReleaseVTable(IntPtr @this, IntPtr vtable)
    {
    Marshal.WriteIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)), vtable);
    }
    }
    partial class CSForC : IDisposable
    {
    public void Dispose()
    {
    Dispose(true);
    GC.SuppressFinalize(this);
    } private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
    if (!this.disposed)
    {
    _Dispose();
    disposed = true;
    }
    } ~CSForC()
    {
    Dispose(false);
    }
    } [StructLayout(LayoutKind.Sequential)]
    public struct _CForCS
    {
    public IntPtr _vtable;
    public int _nValue;
    } partial class CSForC
    {
    private IntPtr _basecpp;
    private const string dllname = "..\\..\\..\\Debug\\CForCS.dll";
    [DllImport(dllname, EntryPoint = "??0CCForCS@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]
    private static extern void _Constructor([In] IntPtr @this);
    [DllImport(dllname, EntryPoint = "??0CCForCS@@QAE@H@Z", CallingConvention = CallingConvention.ThisCall)]
    private static extern void _Constructor([In] IntPtr @this, [In, MarshalAs(UnmanagedType.I4)] int value);
    [DllImport(dllname, EntryPoint = "??1CCForCS@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]
    private static extern void _Destructor([In] IntPtr @this); [DllImport(dllname, EntryPoint = "?Print@CCForCS@@QAEXPB_W@Z", CallingConvention = CallingConvention.ThisCall)]
    private static extern void _Print([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message);
    [DllImport(dllname, EntryPoint = "?GetValue@CCForCS@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)]
    [return: MarshalAs(UnmanagedType.I4)]
    private static extern int _GetValue([In] IntPtr @this); public CSForC()
    {
    _basecpp = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));
    _Constructor(_basecpp);
    CreateVtable();
    } public CSForC(int value)
    {
    _basecpp = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));
    _Constructor(_basecpp, value);
    CreateVtable();
    } private void _Dispose()
    {
    if (_basecpp != IntPtr.Zero)
    {
    ReleaseVtable();
    _Destructor(_basecpp);
    Marshal.FreeCoTaskMem(_basecpp);
    _basecpp = IntPtr.Zero;
    }
    } public void Print(string message)
    {
    _Print(_basecpp, message);
    }
    public int GetValue()
    {
    return _GetValue(_basecpp);
    } IntPtr _basecppvtable;
    CVTable vtable;
    private void CreateVtable()
    {
    vtable = new CVTable(new _VFunctionBeforeHandler(VFunctionBefore), new _VFunctionAfterHandler(VFunctionAfter));
    _basecppvtable = vtable.CreateVTable(_basecpp);
    }
    private void ReleaseVtable()
    {
    if (_basecppvtable != IntPtr.Zero)
    {
    vtable.ReleaseVTable(_basecpp, _basecppvtable);
    ((IDisposable)vtable).Dispose();
    }
    } [DllImport(dllname, EntryPoint = "?VFunctionBefore@CCForCS@@MAEHPB_WPAH@Z", CallingConvention = CallingConvention.ThisCall)]
    [return: MarshalAs(UnmanagedType.I4)]
    private static extern int _VFunctionBefore([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message, [Out, MarshalAs(UnmanagedType.I4)] out int value);
    [DllImport(dllname, EntryPoint = "?VFunctionAfter@CCForCS@@MAEXPB_WH@Z", CallingConvention = CallingConvention.ThisCall)]
    private static extern void _VFunctionAfter([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message, [In, MarshalAs(UnmanagedType.I4)]int value);
    private delegate int _VFunctionBeforeHandler([In, MarshalAs(UnmanagedType.LPWStr)]string message, [Out, MarshalAs(UnmanagedType.I4)]out int value);
    private delegate void _VFunctionAfterHandler([In, MarshalAs(UnmanagedType.LPWStr)]string message, [In, MarshalAs(UnmanagedType.I4)]int value);
    protected virtual int VFunctionBefore(string message, out int value)
    {
    int n = _VFunctionBefore(_basecpp, message, out value);
    value = 100;
    return n;
    }
    protected virtual void VFunctionAfter(string message, int value)
    {
    System.Diagnostics.Debug.WriteLine(string.Format("{0}, {1}.", message, value));
    }
    }
    class Program
    {
    static void Main(string[] args)
    {
    var v = new CSForC(123456789);
    v.Print("CSMessage ");
    System.Diagnostics.Debug.WriteLine(string.Format("GetValue() = {0}", v.GetValue())); ((IDisposable)v).Dispose();
    }
    }
      

  10.   

    如果没有虚函数,那么把相关的vtable 去掉。