zoukankan      html  css  js  c++  java
  • .Net平台调用の初识

    前言

    工作过程中难免遇到混合编程,现在我们要谈的是C#和c++语言的编程。C#要调用C++的库目前可选主要有两种方式:Com技术和平台调用(P/Invoke)。现在我们要谈的是P/Invoke技术。

    一、平台调用

     使用平台调用的技术可以在托管代码中调用动态链接库(Dll)中实现的非托管函数,如Win32 Dll和C/C++ 创建的dll。看到这里,有些朋友们应该会有疑问——在怎样的场合我们可以使用

    平台调用技术来调用动态链接库中的非托管函数呢?

    .Net封装了一些系统的API供我们调用,一般我的操作我们是访问系统的,但是并不全面,我们也需要访问系统的本身的API。

    有些程序,比如截图、底层处理等,使用C/C++有很大的优势,尤其是和其他公司的产品对接的时候。

    托管代码的效率不如非托管代码,为了提高效率,此时也可以考虑托管代码中调用C库函数。

     平台调用步骤

    (1).  获得非托管函数的信息,即dll的名称,需要调用的非托管函数名等信息

    (2). 在托管代码中对非托管函数进行声明,并且附加平台调用所需要属性

    (3). 在托管代码中直接调用第二步中声明的托管函数

    平台调用的调用过程

    (1)  查找包含该函数的DLL(本地函数声明的时候需要dllimport标识)

    (2) 将找到的DLL加载到内存中。

    (3) 查找函数在内存中的地址并把其参数推入堆栈,来封送所需的数据。CLR只会在第一次调用函数时,才会去查找和加载DLL,并查找函数在内存中的地址。当函数被调用过一次之后,CLR会将函数的地址缓存起来,CLR这种机制可以提高平台调用的效率。在应用程序域被卸载之前,找到的DLL都一直存在于内存中。

    (4) 执行非托管函数。

    二、C/C++库的构建

    注意:C#是无法直接调用c++的类的,我们可以写一个c++的类,再通过C/C++封装成C函数的方式,对外暴露。为了简单,以下只使用C函数。

    1、我们选择新建项目(C++,动态链接库)

     2、不要选择空项目,因为可能有其他依赖项

     3、删除自带的示例代码,我们添加自己的接口(附加一个回调方法)

    #ifdef ADDDLL_EXPORTS
    #define ADDDLL_API __declspec(dllexport)
    #else
    #define ADDDLL_API __declspec(dllimport)
    #endif
    
    typedef void(__stdcall * RecvDataCallback) (unsigned char* pData, int iDataSize, int iMsgType, long lContext);
    
    long mCallBackFunction=NULL;
    
    EXTERN_C ADDDLL_API int add(int a,int b);
    
    EXTERN_C ADDDLL_API bool MLogin(long lInstance,ULONG pServerIP,int iConferenceNo,long pCallBackFunction,long lContext);
    
    EXTERN_C ADDDLL_API bool MLogout(long lInstance);
    
    EXTERN_C ADDDLL_API bool MSendData(long lInstance,unsigned char* pData,int iDataSize,int iMsgType);
    
    void logPrintf(const char* str);

    cpp文件中实现

    // AddDll.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include "stdafx.h"
    #include "AddDll.h"
    
    
    int add(int a,int b)
    {
        return a + b;
    }
    
    ADDDLL_API bool MLogin(long lInstance, ULONG pServerIP, int iConferenceNo, long pCallBackFunction, long lContext)
    {
        std::string strLog="log from login:
    ";
        strLog += "lInstance:" + std::to_string(lInstance);
        strLog += " pServerIP:"+ std::to_string(pServerIP);
        strLog += " iConferenceNo:" + std::to_string(pServerIP);
        strLog += " pCallBackFunction:" + std::to_string(pCallBackFunction);
        strLog += " lContext:" + std::to_string(lContext);
        strLog += "
    " ;
        logPrintf(strLog.c_str());
    
        mCallBackFunction = pCallBackFunction;
    
        return true;
    }
    
    ADDDLL_API bool MLogout(long lInstance)
    {
        std::string strLog = "log from logout:
    ";
        strLog += "lInstance:" + std::to_string(lInstance);
        strLog += "
    ";
        logPrintf(strLog.c_str());
        return true;
    }
    
    ADDDLL_API bool MSendData(long lInstance, unsigned char * pData, int iDataSize, int iMsgType)
    {
        std::string strLog = "log from sendData:
    ";
        strLog += "lInstance:" + std::to_string(lInstance);
        strLog += " pData:" + std::string((const char *)pData);
        strLog += " iDataSize:" + std::to_string(iDataSize);
        strLog += " iMsgType:" + std::to_string(iMsgType);
        strLog += "
    ";
        logPrintf(strLog.c_str());
    
        ( *(RecvDataCallback) mCallBackFunction)((unsigned char*) pData, strlen((const char *)pData), iMsgType, 0);
    
        return true;
    }
    
    void logPrintf(const char * str)
    {
        FILE* fp = fopen("D:\log.txt", "a+");
        if (fp)
        {
            fprintf(fp, str);
            fclose(fp);
        }
    }
    View Code

    生成即可

    4、需要注意的是,不要勾选空项目,因为可能有依赖项,如果是给C++调用,那么此处可以选择空项目

    一定不要忘记导出标识

    extern "C" __declspec(dllexport)
    参见:点我

    三、C#端调用
      /// <summary>
        /// 注意:需要类中声明,方法中new,避免被回收
        /// </summary>
        /// <param name="pData"></param>
        /// <param name="iDataSize"></param>
        /// <param name="iMsgType"></param>
        /// <param name="lContext"></param>
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate  void RecvDataCallback(IntPtr pData,int iDataSize, int iSendMsgType, int iCallBackMsgType, long lContext);
    
        public static class CRelay
        {
            public static Int32 lInstance = 0;
    
            [DllImport("RelayDllR.DLL", EntryPoint = "CreateInstance", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern Int32 CreateInstance();
    
            [DllImport("RelayDllR.DLL", EntryPoint = "DestroyInstance", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern void DestroyInstance(Int32 lInstance);
    
            [DllImport("RelayDllR.DLL", EntryPoint = "Login", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern bool Login(Int32 lInstance, UInt32 pServerIP, Int32 iConferenceNo,/*RecvDataCallback*/Int32 pCallBackFunction, Int32 lContext);
    
            [DllImport("RelayDllR.DLL", EntryPoint = "Logout",SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern bool Logout(Int32 lInstance);
    
            [DllImport("RelayDllR.DLL", EntryPoint = "SendData", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern bool SendData(Int32 lInstance, [MarshalAs(UnmanagedType.LPArray)] byte[] pData, Int32 iDataSize, Int32 iMsgType);
    
    

    注意:回调的声明不能少好    [UnmanagedFunctionPointer(CallingConvention.Cdecl)],否则可能只能调用有限次数后被平台释放,调用处应该在类中声明,方法中初始化(避免被GC回收)。

     
  • 相关阅读:
    eclipse如何把多个项目放在一个文件夹下【eclipse中对项目进行分类管理】-图示详解
    hibernate中cache二级缓存问题
    Java处理正则验证手机号-详解
    Java处理手机号中间4位替换成*
    WPF:依赖属性的应用
    WPF:自定义路由事件的实现
    WPF:类型转换器的实现
    MFC中CString转化为char*
    Android4.4 往短信收件箱中插入自定义短信(伪造短信)
    OpenCV imread读取图片,imshow展示图片,出现cv:Exception at memory location异常
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/11599477.html
Copyright © 2011-2022 走看看