• 非托管C++通过C++/CLI包装调用C# DLL


    项目中要给其它客户程序提供DLL做为接口,该项目是在.Net4.0平台下开发。终所周知.Net的各个版本之间存在着兼容性的问题,但是为了使用高版本运行平台的新特性,又不得不兼顾其它低版本平台客户程序的调用。为了解决这个问题尝试通过一个C++/CLI DLL对高版本的.Net DLL的接口加了一层包装,对外暴露C风格的接口给客户程序调用。

    可支持的客户语言平台:

    • VB 6.0
    • VC++
    • .Net 1.0/.Net 1.1
    • .Net 2.0
    • .Net 3.5

     创建C# .Net4.0的类库

    • 创建一个C#项目:Csharp

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Csharp
    {
        public class CsharpClass
        {
            public int DoTesting(int x, int y, string testing, out string error)
            {
                error = testing + " -> testing is ok.";
                return x + y;
            }
        }
    }

    创建C++/CLI包装类库

    • 创建项目C++/CLI项目:CsharpWrap

    • 添加对Csharp的引用

    • CsharpWrap.h

    // CsharpWrap.h
    
    #pragma once
    
    #include <windows.h>  
    #include <string> 
    
    using namespace System;
    using namespace Csharp;
    using namespace std;
    using namespace Runtime::InteropServices;
    • CsharpWrap.cpp

    // This is the main DLL file.
    
    #include "stdafx.h"
    
    #include "CsharpWrap.h"
    
    extern "C" _declspec(dllexport) int DoTesting(int x, int y, char* testing, char* error)
    {
        try
        {
            CsharpClass ^generator = gcnew CsharpClass();
            String^ strTesting = gcnew String(testing);
            String^ strError;
    
            int sum = generator->DoTesting(x, y,strTesting, strError);
    
            if(strError != nullptr)
            {
                char* cError = 
                    (char*)(Marshal::StringToHGlobalAnsi(strError)).ToPointer();
                memcpy(error,cError,strlen(cError) + 1);
            }
            else
            {
                error = nullptr;
            }
    
            return sum;
        }
        catch(exception e)
        {
            memcpy(error,e.what(),strlen(e.what()) + 1);
            return -1;
        }
    }
    • 项目输出和使用

    1. Csharp.dll
    2. CsharpWrap.dll

    如果要调用CsharpWrap.dll必须保证Csharp.dll也被调用程序可见(即应该放在进程EXE文件同一目录下)

    调用Demo代码

    • C++

    HINSTANCE hInst= LoadLibrary(_T("CsharpWrap.dll"));
    if(hInst)
    {
        pfunc DoTesting = (pfunc)GetProcAddress(hInst,"DoTesting");
    
        if(DoTesting)
        {
            char error[100]= {NULL};
            int sum = DoTesting(1, 2, "Hello", error);
            //show testing results
            char strSum[8];
            _itoa_s(sum,strSum,16);
            ::MessageBoxA(NULL, error, strSum, MB_OK);
        }
        else
        {
            ::MessageBoxA(NULL, "Get function fail.", "Fail", MB_OK);
        }
        //free library
        FreeLibrary(hInst);
        hInst = nullptr;
    }
    else
    {
        ::MessageBoxA(NULL, "Load dll fail.", "Fail", MB_OK);
    }
    • C#低版本.Net

    导出函数

    [DllImport("CsharpWrap.dll")]
    static extern int DoTesting(int x, int y, string testing, StringBuilder error);

    调用

    StringBuilder sb = new StringBuilder(200);
    int sum  = DoTesting(1, 2, "Hellow", sb);
    MessageBox.Show(sb.ToString(), sum.ToString());
    • VB6.0

    导出函数

    Public Declare Function DoTesting Lib "CsharpWrap.dll" (ByVal x As Integer, ByVal y As Integer, ByVal testing As String, ByVal error As String) As Integer

    调用

    Dim error As String
    Dim testing As String
    Dim x As Integer
    Dim y As Integer
    Dim sum As Integer
    
    testing = "Hello"
    error = String(60000, vbNullChar)
    sum = DoTesting(x, y, testing, error)

    常见问题及注意事项

    • 平台工具集的问题

    如果使用的是VS2010以上的版本编译出来的C++/CLI DLL可能会遇到缺少依赖等运行错误。

    如果要支持XP系统工具集尽量用_xp结尾的

    平台工具集的依赖DLL,把下面的或其它相应版本的依赖DLL放到目标机器的C:WINDOWSSYSTEM32下

        1. msvcp100.dll
        2. msvcr100.dll
        3. msvcp110.dll
        4. msvcr110.dll
    • 字符集的问题,因为.Net默认用的字符集是Unicode,但是客户程序有可能是其它字符集,这样也可能会造成字符串在程序间的兼容问题。

    可选择Unicode/Multi-Byte,根据不项目需求选择相应的字符集

    代码内对不同字符集进行转换

    从char* to 宽字符

    wchar_t *GetWC(const char *c)
    {
        const size_t cSize = strlen(c)+1;
        wchar_t* wc = new wchar_t[cSize];
        MultiByteToWideChar(CP_ACP,0,(const char *)c,int(cSize),wc,int(cSize));    
        return wc;
    }

    String^ to Char*

    char* cError = (char*)(Marshal::StringToHGlobalAnsi(strError)).ToPointer();
    • 调用的C++/CLI DLL的时候传入参数的问题

    C#调用的时候String参数对应的类型应该是StringBuilder,要注意StringBuilder的容量,默认是256个字符,如果返回的比较多的东西要注意初始化相应大小的容量。

    • DLL多层嵌套的问题

    如果用LoadLibrary加载DLL失败,可以尝试用LoadLibraryEx,同时保证所依赖的C#DLL放到进程EXE同级目录。

    LoadLibraryEx("DLL绝对路径", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

    转载请注明出处:http://www.cnblogs.com/keitsi/p/5554110.html

  • 相关阅读:
    关于ubantu在VMWARE里安装时出现'SMBus Host Controller not enabled'
    临时备忘录
    第七届 山东ACM热身赛 Dwarf Tower
    数学问题题目连接
    蓝桥杯2016决赛 凑平方数
    博弈整理(取物)
    格子刷油漆(DP)
    蓝桥杯第六届总决赛B组
    蓝桥杯第五届决赛B组
    蓝桥杯第四届总决赛
  • 原文地址:https://www.cnblogs.com/keitsi/p/5554110.html
Copyright © 2020-2023  润新知