C#调用C++代码(CSharp Platform Invoke)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32782059/article/details/76258233

在.Net开发的过程中,有时候我们需要从C#中调用C++开发的代码,原因之一就是集成第三方的程序库(C++)写的,另外一个原因就是为了速度,将一些功能在C或C++里面实现。C#调用C++的功能有两种方法,一是用C++/CLI进行封装,其二就是使用.Net的Platform Invoke。本文只讨论第二种情况。C++代码的DLL也分好几种情况,

1、DLL使用C++编写,没有Export函数

2、DLL使用C++编写,有Export函数

3、DLL有C导出函数

期中第3种情况最简单了,直接使用DllImport导入函数就可以,第1、2种情况必须转换为第3种情况来进行处理。


1、准备C++的DLL,没有Export。

HelloWorld.h文件

#pragma once

class HelloWorld
{
public:
HelloWorld();
~HelloWorld();


public:
char* say(const char* name);


private:


};


HelloWorld.cpp文件


#include "stdafx.h"
#include "HelloWorld.h"
#include <string.h>

HelloWorld::HelloWorld()
{
}


HelloWorld::~HelloWorld()
{
}


const char * HelloWorld::say(const char * name)
{
const char* field = "你好,";


char* hello = new char[  strlen(field) + strlen(name) + 1];
char* header = hello;


while (*field != ('\0'))
{
*hello++ = *field++;
}


while (*name != ('\0'))
{
*hello++ = *name++;
}


*hello = ('\0');


return header;
}

然后编译为DLL文件,通过Dependency Walker查看,没有任何的导出函数。


导出C++类。

HelloWorldProxy.h

#pragma once


#include "HelloWorld.h"


#ifdef TESTHELLOWORLD_EXPORTS
#define API_EXPORT __declspec(dllexport)
#else
#define API_EXPORT __declspec(dllimport)
#endif


class API_EXPORT HelloWorldProxy
{
public:
HelloWorldProxy();
~HelloWorldProxy();


public:
char* say(const char* name);


private:
HelloWorld* m_helloWorld;
};


HelloWorldProxy.cpp



#include "stdafx.h"
#include "HelloWorldProxy.h"




HelloWorldProxy::HelloWorldProxy()
{
m_helloWorld = new HelloWorld();
}


HelloWorldProxy::~HelloWorldProxy()
{
delete m_helloWorld;
m_helloWorld = NULL;
}


char * HelloWorldProxy::say(const char * name)
{
return m_helloWorld->say(name);
}

然后编译为DLL文件,通过Dependency Walker查看,C++风格的导出函数。



2、C#访问C++函数的声明,通过DllImport来导入函数

    public static class HelloWorld
    {
        // [return:MarshalAs(UnmanagedType.LPStr)]
        // [MarshalAs(UnmanagedType.LPStr)]
        
        private const string LibraryName = "testHelloWorld";


        [DllImport(LibraryName, EntryPoint = "??0HelloWorldProxy@@QEAA@XZ")]
        public static extern IntPtr Create();


        [DllImport(LibraryName, EntryPoint = "?say@HelloWorldProxy@@QEAAPEBDPEBD@Z")]
        [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string SayCpp([MarshalAs(UnmanagedType.LPStr)] string name);
    }

备注:这里导入的是C++的函数,

    class Program
    {
        static void Main(string[] args)
        {
            // 创建C++对象,这个没有问题
            IntPtr ptr = HelloWorld.Create();


            // 调用C++对象里面的方法,这个是有问题的,因为系统不知道你调用的那个对象。
            string hello = HelloWorld.SayCpp("jacky");
            Console.WriteLine(hello);
        }
    }


3、转换为C风格的函数

文件ApiMain.h

#pragma once


#include "HelloWorldProxy.h"


#ifdef  __cplusplus
extern "C" {
#endif //  __cplusplus


API_EXPORT char* say(const char *name);
API_EXPORT void say2(char* buffer, int size, const char* name);
API_EXPORT int Add(int a, int b);

#ifdef __cplusplus
}
#endif // __cplusplus


文件ApiMain.cpp



#include "stdafx.h"
#include "ApiMain.h"
void WriteLog(const char* content) {
FILE *file = fopen("testHelloWorld.txt", "a");
fputs(content, file);
fputc('\n', file);
fclose(file);
}


char* say(const char* name) {
HelloWorldProxy *hello = new HelloWorldProxy();
const char *result = hello->say(name);


delete hello;
hello = NULL;


return result;
}


void say2(char* buffer, int size, const char* name) {

WriteLog("say2 begin");

HelloWorldProxy *hello = new HelloWorldProxy();
char *result = hello->say(name);


strcpy_s(buffer, size, result);


WriteLog("say2 end");
}


int Add(int a, int b) {
return a + b;
}

重新编译成testHelloWorld.dll文件,通过dependency walker看到的如下:



4、使用C风格的导出函数

        // say函数,直接转换为string或者StringBuilder不可用。
        [DllImport(LibraryName, EntryPoint = "say", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern StringBuilder SayC(string name);


        // say函数,返回值转换为IntPtr,可以正常调用,然需要在代码中将指针转换为字符串。
        [DllImport(LibraryName, EntryPoint = "say", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr SayC2(string name);


        // say2是say函数的变形,原来say的返回值通过参数传递。
        [DllImport(LibraryName, EntryPoint = "say2", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern void say2([MarshalAs(UnmanagedType.LPStr)]StringBuilder buffer, int size, string name);


        // 纯C方法。
        [DllImport(LibraryName, EntryPoint = "Add", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Add(int a, int b);


5、在C#中使用导入的函数。

            StringBuilder sb = new StringBuilder(250);
            HelloWorld.say2(sb, sb.Capacity, "jacky, ok");
            Console.WriteLine("sb = " + sb.ToString());


            sb = new StringBuilder(250);
            HelloWorld.say2(sb, sb.Capacity, "祖国");
            Console.WriteLine("sb = " + sb.ToString());


            int sum = HelloWorld.Add(20, 10);
            Console.WriteLine("sum = " + sum.ToString());


            IntPtr ptr = HelloWorld.SayC2("纽约交易所。");
            if(ptr == IntPtr.Zero)
            {
                Console.WriteLine("错误的数据。");
            }
            else
            {
                string aa = Marshal.PtrToStringAnsi(ptr);
                Console.WriteLine("IntPtr里面保存的字符串:" + aa);
            }
            
            // 下面的调用会报错,不能正常使用。
            sb = HelloWorld.SayC("jacky");
            Console.WriteLine(sb.ToString());



猜你喜欢

转载自blog.csdn.net/qq_32782059/article/details/76258233
今日推荐