C#调用DLL示例和注意事项

1. 生成dll文件

  • 利用visual studio 2017新建一个windows控制台应用空项目。
  • 添加接口定义和实现相关的.h和.cpp文件,示例如下:
# .h文件
#pragma once

#include 
#include 
#include 

#ifdef ImageHandleDLL_EXPORTS
#define ImageHandle_EXPORTS extern "C" __declspec(dllexport)
#else
#define ImageHandle_EXPORTS extern "C" __declspec(dllimport)
#endif

typedef enum
{
	OPERATION_SUCCESS = (0),
	INPUT_CHANNELS_ERR = (1),
	INPUT_EMPTY_ERR = (2),
	PROCESS_FAILE = (3),
} ErrCode;


ImageHandle_EXPORTS ErrCode Read_Image(char * filepath, unsigned char **output_image_data, int &image_h, int &image_w, int &input_image_channels);
ImageHandle_EXPORTS ErrCode Write_Image(unsigned char **intput_image_data, char * filepath, int image_h, int image_w, int input_image_channels);

ImageHandle_EXPORTS ErrCode Image_Gray(unsigned char **input_image_data, unsigned char **output_image_data, int image_h, int image_w, int input_image_channels = 3);

# .cpp文件

ErrCode Read_Image(char * filepath, unsigned char **output_image_data, int &image_h, int &image_w, int &input_image_channels)
{
	try {
		cv::Mat src = cv::imread(filepath);
		image_h = src.rows;
		image_w = src.cols;
		input_image_channels = src.channels();
		if (output_image_data[0] == nullptr) {
			output_image_data[0] = (unsigned char *)malloc(image_h*image_w *input_image_channels * sizeof(unsigned char));
		}
		memcpy(output_image_data[0], src.data, image_h * image_w * input_image_channels * sizeof(unsigned char));
		return OPERATION_SUCCESS;
	}
	catch (std::exception e) {
		if (output_image_data[0])
		{
			free(output_image_data[0]);
			output_image_data = nullptr;
		}
		std::cout << e.what() << std::endl;
		return PROCESS_FAILE;
	}
}

ErrCode Write_Image(unsigned char **input_image_data, char * filepath, int image_h, int image_w, int input_image_channels)
{
	try {
		
		if (input_image_channels == 4)
		{
			cv::Mat input_image(image_h, image_w, CV_8UC4, input_image_data[0]);
			cv::imwrite(filepath, input_image);
		}
		else if (input_image_channels == 3) {
			if (input_image_data) {
				cv::Mat input_image(image_h, image_w, CV_8UC3, input_image_data[0]);
				cv::imwrite(filepath, input_image);
			}
		}
		else if (input_image_channels == 1)
		{
			cv::Mat input_image(image_h, image_w, CV_8UC1, input_image_data[0]);
			cv::imwrite(filepath, input_image);
		}
		else
		{
			return INPUT_CHANNELS_ERR;
		}
		return OPERATION_SUCCESS;
	}
	catch (std::exception e) {
		std::cout << e.what() << std::endl;
		return PROCESS_FAILE;
	}
}
ErrCode Image_Gray(unsigned char **input_image_data, unsigned char **output_image_data, int image_h, int image_w, int input_image_channels)
{
	try {
		if (input_image_data[0] == nullptr)
		{
			return INPUT_EMPTY_ERR;
		}
		if (output_image_data[0] == nullptr)
		{
			output_image_data[0] = (unsigned char *)malloc(image_h*image_w * 1 * sizeof(unsigned char));
		}
		cv::Mat output_image;
		if (input_image_channels == 3) {
			cv::Mat input_image(image_h, image_w, CV_8UC3, input_image_data[0]);
			cv::cvtColor(input_image, output_image, cv::COLOR_BGR2GRAY);
		}
		else if (input_image_channels == 4) {
			cv::Mat input_image(image_h, image_w, CV_8UC4, input_image_data[0]);
			cv::cvtColor(input_image, output_image, cv::COLOR_BGRA2GRAY);
		}
		else if (input_image_channels == 1) {
			memcpy(output_image_data[0], output_image.data, image_h*image_w * 1 * sizeof(unsigned char));
			return OPERATION_SUCCESS;
		}
		else {
			if (output_image_data[0])
			{
				free(output_image_data[0]);
				output_image_data[0] = nullptr;
			}
			return INPUT_CHANNELS_ERR;
		}
		memcpy(output_image_data[0], output_image.data, image_h*image_w * 1 * sizeof(unsigned char));
		return OPERATION_SUCCESS;
	}
	catch (std::exception e) {
		if (output_image_data[0])
		{
			free(output_image_data[0]);
			output_image_data[0] = nullptr;
		}
		std::cout << e.what() << std::endl;
		return PROCESS_FAILE;
	}
}

2. C#调用dll文件

  • 利用visual studio 2017新建一个windows控制台应用空项目。

  • 修改.cs文件如下:

#.cs文件 

using System;

using System.Runtime.InteropServices;

namespace app_project
{
    class Program
    {
        [DllImport("ImageHandle.dll")]
        public static extern int Read_Image(string filepath,ref UIntPtr output_image_data, ref int image_h, ref int image_w, ref int input_image_channels);
        [DllImport("ImageHandle.dll")]
        public static extern int Write_Image(ref UIntPtr intput_image_data, string filepath, int image_h, int image_w, int input_image_channels);
        [DllImport("ImageHandle.dll")]
        public static extern int Image_Gray(ref UIntPtr input_image_data,ref UIntPtr output_image_data, int image_h, int image_w, int input_image_channels = 3);


        static void Main(string[] args)
        {
            Console.WriteLine("测试");

            string path = "test_align.jpg";
            UIntPtr input_image_data = new UIntPtr();
            int image_height = 0;
            int image_width = 0;
            int image_channels = 0;
            Read_Image(path,ref input_image_data,ref image_height,ref image_width,ref image_channels);

            UIntPtr output_image_data = new UIntPtr();

            Image_Gray(ref input_image_data, ref output_image_data, image_height, image_width, image_channels);
            string path_1 = "test_align_gray.jpg";
            Write_Image(ref output_image_data, path_1, image_height, image_width, 1);

            Console.WriteLine("OK");
            Console.Read();
        }
    }
}

  • 选择平台并生成exe,其中平台需要和生成dll文件时的平台一致,并将dll文件所以依赖的其他dll文件拷贝到exe所在的目录下。

3 说明

3.1 [DllImport("*.dll")]说明

每添加一个接口,均要有一条[DllImport("*.dll")] , 示例如下:

[DllImport("ImageHandle.dll")]
public static extern IntPtr GetLibraryVersion();

[DllImport("ImageHandle.dll")]
public static extern int Read_Image(string filepath, ref UIntPtr output_image_data, ref int image_h, ref int image_w, ref int input_image_channels);

3.2 形参类型转换

  • 引用类型
    c++接口中形参类型为int &, 而对应在c#中形参类型为ref int,其中ref参数表示变量作为参数传给方法时,同时希望在方法执行完成后,对参数所做的修改能够反映到变量上;示例如下:
    C++ 方式:
Add( int &h, int &w, int &sum);

C# 方式:

Add(ref int h, ref int w, ref int sum);
  • char *
    c++接口中形参类型为char *, 而在c#中形参类型为string,示例如下:
Read_Image(char * filepath);

C# 方式:

Read_Image(string filepath);
  • 数组
    c++接口中形参类型为unsigned char **, 而在c#中形参类型为ref IntPtr,示例如下:
Read_Image(unsigned char **intput_image_data);

C# 方式:

Read_Image(ref UIntPtr intput_image_data);

上述c++接口中,传递数组不能采用unsigned char *,其原因为:UIntPtr类型被称之为“平台特定的类型”,资源的大小取决于使用的硬件和操作系统,但其大小总是足以包含系统的指针,类似含有窗口句柄参数(HANDLE)。

你可能感兴趣的:(C++,C#)