C# 需要调用C++东西,但是有不想做成COM,就只好先导出类中的函数处理。
抛砖了!
测试IDE版本为 visual studio 2008
C++部分:
1. 首先建一个C++WIN32 工程,配置属性里面--> 常规里的配置类型,改为动态库(.dll)
建立个SimpleClass类
头文件和CPP文件如下:SimpleCClass.h
#ifndef _SIMPLE_C_CLASS_H_
#define _SIMPLE_C_CLASS_H_
#include "iostream"
#include <string>
using namespace std;
class SIMPLE_CLASS_EXPORTS SimpleCClass
{
public:
SimpleCClass(void);
~SimpleCClass(void);
public:
int Add(int x,int y);
void SetName(wstring sName);
void Show();
private:
wstring m_sName;
int m_iResult;
};
#endif
SimpleCClass.cpp
#include "StdAfx.h"
#include "SimpleCClass.h"
SimpleCClass::SimpleCClass(void)
{
m_iResult = -1;
m_sName = L"";
}
SimpleCClass::~SimpleCClass(void)
{
}
int SimpleCClass::Add( int x,int y )
{
m_iResult = x + y;
return m_iResult;
}
void SimpleCClass::SetName( wstring sName )
{
m_sName = sName;
}
void SimpleCClass::Show()
{
cout<<"两数相交的结果为"<<m_iResult <<"\t"<<endl;
//cout<<"输入名字为"<< <<"\t"<<endl;
}
2. 定义了导出类中的那些函数
建立两个文件,ExportSimple.h 和 ExportSimple.cpp
#ifndef _EXPORT_SIMPLE_H_
#define _EXPORT_SIMPLE_H_
#include "SimpleCClass.h"
#include "windows.h"
SimpleCClass* g_pSimple = NULL;
// 创建对象
extern "C" SIMPLE_CLASS_EXPORTS void CreateSimple();
extern "C" SIMPLE_CLASS_EXPORTS int Add(int x, int y);
extern "C" SIMPLE_CLASS_EXPORTS void SetName(LPCTSTR sName);
extern "C" SIMPLE_CLASS_EXPORTS void Release();
#endif
CPP 文件
#include "StdAfx.h"
#include "ExportSimple.h"
extern "C" SIMPLE_CLASS_EXPORTS void CreateSimple()
{
g_pSimple = new SimpleCClass();
}
extern "C" SIMPLE_CLASS_EXPORTS int Add( int x, int y )
{
return g_pSimple->Add(x,y);
}
extern "C" SIMPLE_CLASS_EXPORTS void SetName( LPCTSTR sName )
{
g_pSimple->SetName(sName);
}
extern "C" SIMPLE_CLASS_EXPORTS void Release()
{
if (NULL != g_pSimple)
{
delete g_pSimple;
g_pSimple = NULL;
}
}
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#ifdef SIMPLE_CLASS_EXPORTS
#define SIMPLE_CLASS_EXPORTS __declspec(dllexport)
#else
#define SIMPLE_CLASS_EXPORTS __declspec(dllimport)
#endif
// TODO: 在此处引用程序需要的其他头文件
4.注意:要在属性页中的C++下的预处理器的预处理器定义中加上:SIMPLE_CLASS_EXPORTS
这样C++ 部分基本搞定。编译生产DLL,
我电脑生成路径为:D:\TestCode\DynamicImportDLL\Debug\SimpleOperate.dll
C#部分:
建立个WIN32工程用来调用测试刚才生产的DLL。工程名称在TestExportSimple。
建了一个导入DLL的类,文件名称为ImportSimpleDLL.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// 添加引用
using Microsoft.Win32;
using System.Runtime.InteropServices;
//
namespace TestExportSimple
{
#region 导入VRBTLoftingBuilder 动态库
public static class ImportSimleDLL
{
const string DLLPath = @"D:\TestCode\DynamicImportDLL\Debug\SimpleOperate.dll";
[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None)]
public static extern void CreateSimple();
[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None)]
public static extern int Add(int x,int z);
[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None)]
public static extern void SetName([MarshalAs(UnmanagedType.LPTStr)]string sName);
[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None)]
public static extern void Release();
}
#endregion
#region 设置环境变量,此代码网上COPY的!
/// <summary>
/// 设置环境变量
/// </summary>
class SysEnvironment
{
/// <summary>
/// 获取系统环境变量
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static string GetSysEnvironmentByName(string name)
{
string result = string.Empty;
try
{
result = OpenSysEnvironment().GetValue(name).ToString();//读取
}
catch (Exception)
{
return string.Empty;
}
return result;
}
/// <summary>
/// 打开系统环境变量注册表
/// </summary>
/// <returns>RegistryKey</returns>
private static RegistryKey OpenSysEnvironment()
{
RegistryKey regLocalMachine = Registry.LocalMachine;
RegistryKey regSYSTEM = regLocalMachine.OpenSubKey("SYSTEM", true); //打开HKEY_LOCAL_MACHINE下的SYSTEM
RegistryKey regControlSet001 = regSYSTEM.OpenSubKey("ControlSet001", true); //打开ControlSet001
RegistryKey regControl = regControlSet001.OpenSubKey("Control", true); //打开Control
RegistryKey regManager = regControl.OpenSubKey("Session Manager", true);
RegistryKey regEnvironment = regManager.OpenSubKey("Environment", true);
return regEnvironment;
}
/// <summary>
/// 设置系统环境变量
/// </summary>
/// <param name="name">变量名</param>
/// <param name="strValue">值</param>
public static void SetSysEnvironment(string name, string strValue)
{
OpenSysEnvironment().SetValue(name, strValue);
}
/// <summary>
/// 检测系统环境变量是否存在
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool CheckSysEnvironmentExist(string name)
{
if (!string.IsNullOrEmpty(GetSysEnvironmentByName(name)))
return true;
else
return false;
}
/// <summary>
/// 添加到PATH环境变量(会检测路径是否存在,存在就不重复)
/// </summary>
/// <param name="strPath"></param>
public static void SetPathAfter(string strHome)
{
string pathlist;
pathlist = GetSysEnvironmentByName("PATH");
//检测是否以;结尾
if (pathlist.Substring(pathlist.Length - 1, 1) != ";")
{
SetSysEnvironment("PATH", pathlist + ";");
pathlist = GetSysEnvironmentByName("PATH");
}
string[] list = pathlist.Split(';');
bool isPathExist = false;
foreach (string item in list)
{
if (item == strHome)
isPathExist = true;
}
if (!isPathExist)
{
SetSysEnvironment("PATH", pathlist + strHome + ";");
}
}
public static void SetPath(string strHome)
{
string pathlist;
pathlist = GetSysEnvironmentByName("PATH");
string[] list = pathlist.Split(';');
bool isPathExist = false;
foreach (string item in list)
{
if (item == strHome)
isPathExist = true;
}
if (!isPathExist)
{
SetSysEnvironment("PATH", pathlist + strHome + ";");
}
}
}
#endregion
}
在Program.cs 中直接调用,因为写的ImportSimleDLL 类是个静态类。
故此:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace TestExportSimple
{
class Program
{
static void Main(string[] args)
{
ImportSimleDLL.CreateSimple();
int iResult = ImportSimleDLL.Add(1,3);
ImportSimleDLL.Release();
}
}
}
至此,编译通过没有问题。
在visual studio 2010 中,如果C#导出DLL中 [DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None)],若没有CallingConvention = CallingConvention.Cdecl 这个参数,就会报错,内容为:
原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。
可以看到,C#导入类中的DLLPath 路径是固定,不能修改。这个因为是DLLImport里面不能为变量,动态生成的。
所以如果想加入为动态加载处理,我目前的处理方式是把对于DLL 文件夹加入到环境变量中。故此上面代码中包含了对加入环境变量的处理。
代码为网上COPY的。多多见谅!
用法: 可以在 初始化对象函数中处理:
C#代码:
private void InitializeObject()
{
string DLLPath = System.Windows.Forms.Application.StartupPath;
DLLPath += @"\Plugins\MagicBox.Plugin.PipeImport\";
SysEnvironment.SetPathAfter(DLLPath);
}
其他调用过程不变。
若有好的方法,能简单处理和动态加载,请不吝赐教!!