2.接口


  硕正的服务器专版和以往的纯客户端版本之间,无论是功能还是内部实现代码,都存在着千丝万缕的联系。对于开发者,要进行服务器端的开发,必须先熟悉常规的浏览器端的开发。例如在后端调用硕正接口的最常见的 C# 语句形式如:
 ...
 dll.func("build", ReportFilename);
 dll.func("SetSource", "reportdata/datacenter.do?pa1=23");
 dll.func("calc", "");
 dll.func("callfunc", "105\r\n type=htm;filename=" + TempFilename);
 ...
  如果您熟悉硕正客户端的开发,看到这段代码肯定不会感到陌生,其函数名、参数形式和浏览器 js 的书写方法几乎一致, 连最外层 "func" 都一样。
  没错,这是我们刻意这样做的,使您在客户端开发的知识技能还能对接得上。

  硕正服务器专版的包中,最外层的接口暴露文件是 winface.dll,该接口共有4个 API,其标准的C++形式是 :
HANDLE APIENTRY OpenReportService(LPCWSTR para);
void APIENTRY CloseService(HANDLE h);
int APIENTRY GetActiveServices();
LPCWSTR APIENTRY func(HANDLE h, LPCWSTR funcname, LPCWSTR para);
  简单地说,这个接口所要做的就是打开报表服务、调用报表服务、关闭报表服务这几个简单的事情.

  C#调用 winface.dll 接口,只能以非托管方式调用,我们用C#对该接口做了一个很规范的封装类,对32位和64位都适用,代码如下:
public class DllInvoke 
{
 //操作系统函数
 [DllImport("kernel32.dll")]
 private extern static IntPtr LoadLibrary(String path);
 [DllImport("kernel32.dll")]
 private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
 [DllImport("kernel32.dll")]
 private extern static bool FreeLibrary(IntPtr lib);
 //4个API的委托声明
 private delegate int DllGetActiveServices( );
 private delegate IntPtr DllOpenReportService(IntPtr CreatePara);
 private delegate void DllCloseService(IntPtr h);
 private delegate IntPtr Dllfunc(IntPtr h, IntPtr funcname, IntPtr para);
 
 //构建函数, 参数DLLPath:  为winface.dll的全文件名
 public DllInvoke(String DLLPath) {
  //加载dll
  m_hService = IntPtr.Zero;
  m_hLib = LoadLibrary(DLLPath);
  if(m_hLib == IntPtr.Zero) return;
 
  //定位入口函数地址
  m_getactiveservices = (DllGetActiveServices)GetInvoke("GetActiveServices", typeof(DllGetActiveServices));
  m_openservice = (DllOpenReportService)GetInvoke("OpenReportService", typeof(DllOpenReportService));
  m_closeservice = (DllCloseService)GetInvoke("CloseService", typeof(DllCloseService));
  m_func = (Dllfunc)GetInvoke("func", typeof(Dllfunc));
  if(m_getactiveservices==null || m_openservice==null || m_closeservice==null || m_func==null) {
   //定位失败
   FreeLibrary(m_hLib);  
   m_hLib = IntPtr.Zero;
  }
 }
 ~DllInvoke() {
  CloseService( );
 }
 
 //DLL句柄
 public IntPtr m_hLib;
 
 //函数:取得当前活动的服务数量
 public int GetActiveServices() {
  return m_getactiveservices();
 }
 //函数:打开服务
 public bool OpenReportService(string CreatePara) {
  if(m_hService != IntPtr.Zero) return true;	//已经打开着
  if(m_hLib == IntPtr.Zero) return false;
  IntPtr h1 = Marshal.StringToHGlobalUni(CreatePara);
  m_hService = m_openservice(h1);
  Marshal.FreeHGlobal(h1);
  return (m_hService == IntPtr.Zero) ? false : true;
 }
 //函数:关闭服务
 public void CloseService() {
  if(m_hService != IntPtr.Zero) {
   m_closeservice(m_hService);
   m_hService = IntPtr.Zero;
  }
 }
 //函数:调用服务中的方法
 public string func(string funcname, string para) {
  if(m_hService == IntPtr.Zero) return "";
  //参数 ==> Unicode串指针地址
  IntPtr h1 = Marshal.StringToHGlobalUni(funcname);
  IntPtr h2 = Marshal.StringToHGlobalUni(para);
  //调用
  IntPtr nRet = m_func(m_hService, h1, h2);
  //释放参数内存
  Marshal.FreeHGlobal(h1);
  Marshal.FreeHGlobal(h2);
  return Marshal.PtrToStringUni(nRet);
 }
 
 //private====================
 private Delegate GetInvoke(String APIName,Type t) {
  IntPtr api = GetProcAddress(m_hLib, APIName);
  return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
 }
 //入口函数地址
 private DllGetActiveServices m_getactiveservices;
 private DllOpenReportService m_openservice;
 private DllCloseService m_closeservice;
 private Dllfunc m_func;
 //服务句柄
 private IntPtr m_hService;
};
  浏览这个类,它在创建时就加载winface.dll了,如果加载失败,其成员变量 m_hLib 为零,报表服务也就不可用。

为什么我们要采用操作系统 LoadLibrary 函数、代理方式调用硕正的dll呢?原因是部署后 DLL 的位置是不固定的.



  为了方便讨论,我们在下面的文档中,都假定这个类的实例名为 "dll":
 ...
 DllInvoke dll = new DllInvoke(WinFacePathname);
 if(dll.m_hLib == IntPtr.Zero) {
  Response.Write("winface.dll加载失败");
  Response.End();
  return;
 }
 ...
  并都以该类的4个同名封装函数为准,不再深究 winface.dll 的原始API。