公司内部服务器端全部采用C++开发,自然而然暴露给客户的肯定是C++提供的API.这里不介绍为何通讯间不采用xml或和其他方式进行传输,因为这是个个别案例.
公司C++高手一大堆,我只是个C++新手,C#也就初级的样子(做过WEB开发以及WCF业务开发,客户端开发经验少).公司内部基本没人用过C#,客户那边又采用的是C#开发,强烈要求 写C#调用案例.固然,我也就赶着鸭子上架,尝试去做了.由于C++提供的API的源码有没有,只能通过部门老大了解,这也是大公司开发的弱势,你只能各种调底层api,学习东西很受限制..哎....
花了2天时间研究C++与C# 数据类型转换,以及相互调用,网上文章一大堆,但是实际用起来问题非常多.
提供几个可以参考的链接:
http://www.cnblogs.com/wdysunflower/archive/2010/09/01/1813947.html
http://www.cnblogs.com/yiki/archive/2008/10/29/1321848.html(个人电脑,发现收藏链接不在,回头再添上)
为了或许给以后的博友提供点帮助,少绕点圈子,特此写篇博客,大家不要喷我技术高低,其中不足之处大家可以帮我提出,一起改进.(我喜欢处理问题,对概念东西不喜欢过于追究.解决问题才是硬道理)
少文字,多代码.
案例如下:
(1)封装了 动态加载C++dll实现 \C#结构体定义 \ C++dll中api到C#转换
/********************************************************************************
********Athor : justliver
***** DataTime: 20130928
*******Description:文件包含委托记录类,ServerCliAPI类
**************** 以及相关委托数据类型(函数指针)
********************************************************************************
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;//调外部dll 命名空间
namespace demo_kpmskcxpapi
{
//委托类型列表,每一个委托对应一个C++ dll的api
public delegate int ServerCLI_Init(ref IntPtr hHandle);//&hHandl
public delegate int ServerCLI_Exit(IntPtr hHandle);
public delegate int ServerCLI_SetConnectOption(IntPtr hHandle, tagServerConnectOption stServerConnection);
public delegate int ServerCLI_ConnectServer(IntPtr hHandle, StringBuilder ServerName, StringBuilder String, StringBuilder Password);//[MarshalAs(LPStr)]
public delegate int ServerCLI_DisConnect(IntPtr hHandle);
public delegate int ServerCLI_DisConnectForce(IntPtr hHandle);
public delegate int ServerCLI_BeginWrite(IntPtr hHandle);
public delegate int ServerCLI_SetValue(IntPtr hHandle, StringBuilder KeyName, StringBuilder Vlu);
public delegate int ServerCLI_GetValue(IntPtr hHandle, StringBuilder KeyName, StringBuilder Vlu, int Len);
public delegate int ServerCLI_ACallProgramAndCommit(IntPtr hHandle, StringBuilder ProgramName, ref tagCallCtrl ptagCallCtrl);// ref tagCallCtrl -> tagCallCtrl *
public delegate int ServerCLI_ACallProgram(IntPtr hHandle, StringBuilder ProgramName, ref tagCallCtrl ptagCallCtrl);
public delegate int ServerCLI_GetReply(IntPtr hHandle, ref tagCallCtrl ptagCallCtrl);
public delegate int ServerCLI_RsGetColByName(IntPtr hHandle, StringBuilder KeyName, StringBuilder Vlu);//对应C++ StringBuilder -> char* 必须用StringBuilder,String无法接收数据(原因为String定长而StringBuilder非定长,大家自己查下资料,这个地方非常重要)
/// <summary>
/// 名称:ServerCliAPI类
///
/// 描述:采用动态调用方式,封装了用到的ServerCli.dll中的相关api方便调用
/// </summary>
public class ServerCliAPI
{
#region 调用Kernel32 API
//引入Win32API 用来来动态加载用户dll
[DllImport("Kernel32")]
public static extern IntPtr GetProcAddress(int handle, String funcname);
[DllImport("Kernel32")]
public static extern int LoadLibrary(String funcname);
[DllImport("Kernel32")]
public static extern int FreeLibrary(int handle);
#endregion
#region 转换 , 并定义与ServerCli.dll 对应的 API
public ServerCLI_Init Init;
public ServerCLI_Exit Exit;
public ServerCLI_SetConnectOption SetConn;
public ServerCLI_ConnectServer Connect;
public ServerCLI_DisConnect DisConnect;
public ServerCLI_DisConnectForce DisConnectForce;
public ServerCLI_BeginWrite BeginWrite;
public ServerCLI_SetValue SetValue;
public ServerCLI_ACallProgramAndCommit ACallProgramAndCommit;
public ServerCLI_GetReply GetReply;
public ServerCLI_GetErrorCode GetErrorCode;
public ServerCLI_GetErrorMsg GetErrorMsg;
public ServerCLI_RsOpen RsOpen;
public ServerCLI_SQLNumResultCols SQLNumResult;
public ServerCLI_SQLFetch SQLFetch;
public ServerCLI_RsGetColByName RsGetColByName;
#endregion
public ServerCliAPI()
{
try
{
hModule = LoadLibrary("ServerCli.dll");//加载ServerCli.dll
if (hModule <= 0)
{
return;
}
//初始化所有委托句柄,,即对应的ServerCli API
Init = (ServerCLI_Init)GetAddress(hModule, "ServerCLI_Init", typeof(ServerCLI_Init));
Exit = (ServerCLI_Exit)GetAddress(hModule, "ServerCLI_Exit", typeof(ServerCLI_Exit));
SetConn = (ServerCLI_SetConnectOption)GetAddress(hModule, "ServerCLI_SetConnectOption", typeof(ServerCLI_SetConnectOption));
Connect = (ServerCLI_ConnectServer)GetAddress(hModule, "ServerCLI_ConnectServer", typeof(ServerCLI_ConnectServer));
DisConnect = (ServerCLI_DisConnect)GetAddress(hModule, "ServerCLI_DisConnect", typeof(ServerCLI_DisConnect));
DisConnectForce = (ServerCLI_DisConnectForce)GetAddress(hModule, "ServerCLI_DisConnectForce", typeof(ServerCLI_DisConnectForce));
BeginWrite = (ServerCLI_BeginWrite)GetAddress(hModule, "ServerCLI_BeginWrite", typeof(ServerCLI_BeginWrite));
SetValue = (ServerCLI_SetValue)GetAddress(hModule, "ServerCLI_SetValue", typeof(ServerCLI_SetValue));
ACallProgramAndCommit = (ServerCLI_ACallProgramAndCommit)GetAddress(hModule, "ServerCLI_ACallProgramAndCommit", typeof(ServerCLI_ACallProgramAndCommit));
GetReply = (ServerCLI_GetReply)GetAddress(hModule, "ServerCLI_GetReply", typeof(ServerCLI_GetReply));
GetErrorCode = (ServerCLI_GetErrorCode)GetAddress(hModule, "ServerCLI_GetErrorCode", typeof(ServerCLI_GetErrorCode));
GetErrorMsg = (ServerCLI_GetErrorMsg)GetAddress(hModule, "ServerCLI_GetErrorMsg", typeof(ServerCLI_GetErrorMsg));
RsOpen = (ServerCLI_RsOpen)GetAddress(hModule, "ServerCLI_RsOpen", typeof(ServerCLI_RsOpen));
SQLNumResult = (ServerCLI_SQLNumResultCols)GetAddress(hModule, "ServerCLI_SQLNumResultCols", typeof(ServerCLI_SQLNumResultCols));
SQLFetch = (ServerCLI_SQLFetch)GetAddress(hModule, "ServerCLI_SQLFetch", typeof(ServerCLI_SQLFetch));
RsGetColByName = (ServerCLI_RsGetColByName)GetAddress(hModule, "ServerCLI_RsGetColByName", typeof(ServerCLI_RsGetColByName));
}
catch (System.Exception ex)
{
FreeLibrary(hModule);//释放
Console.WriteLine("转换并定义与ServerCli.dll 对应的 API 出现错误.Error:{0}", ex.Message.ToString());
}
}
~ServerCliAPI()
{
if (hModule > 0)
{
FreeLibrary(hModule);//释放
}
}
//获取api地址(函数指针转委托)
private static Delegate GetAddress(int dllModule, string functionname, Type t)
{
if (dllModule < 0 || string.IsNullOrEmpty(functionname) || t == null)
return null;
IntPtr addr = GetProcAddress(dllModule, functionname);
if (addr == IntPtr.Zero)
return null;
else
return Marshal.GetDelegateForFunctionPointer(addr, t);
}
//ServerCli.dll 对应地址
private static int hModule { get; set; }
}
/******************结构体定义**************************************/
[StructLayout(LayoutKind.Sequential, Size = 117, CharSet = CharSet.Ansi, Pack = 1)]//数据结构必须采用此种方式,保证C++结构体缓冲区大写固定(内存布局方式\缓冲区大小\字符集\对齐单位1个字节)
// 强烈建议LayoutKind.Sequential,不要用LayoutKind.Explicit
public unsafe struct tagCallCtrl
{
[MarshalAs(UnmanagedType.I4)]
public int nFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 27)]
public char[] szId; //用字符数组 对应C++为 char[27]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szMsgId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szCorrId;
[MarshalAs(UnmanagedType.I4)]
public int nExpiry;
[MarshalAs(UnmanagedType.I4)]
public int nPriority;
[MarshalAs(UnmanagedType.I8)]
public Int64 tTimeStamp; // 对应C++为 _int64
public IntPtr lpfnCallback; // void*
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]//数据结构必须采用此种方式,保证C++结构体缓冲区大写固定
public struct tagServerConnectOption
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]// 声明一个字符数组,大小为33 public char[] name;
public char[] szServerName;
public int nProtocal;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szAddress;
public int nPort;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szSendQName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szReceiveQName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
public char[] szReserved;
}
}
(2)具体调用:
ServerCliAPI = new ServerCliAPI();//初始化 调用ServerCliAPI
nRet = ServerCliAPI.Init(ref hHandle);
如果大家遇到相关问题可以找我探讨 QQ:一零六五八六六八一八