![]() |
...
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" 都一样。HANDLE APIENTRY OpenReportService(LPCTSTR para); void APIENTRY CloseService(HANDLE h); int APIENTRY GetActiveServices(); LPCTSTR APIENTRY func(HANDLE h, LPCTSTR funcname, LPCTSTR para);简单地说,这个接口所要做的就是打开报表服务、调用报表服务、关闭报表服务这几个简单的事情.
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( );
if(m_hLib != IntPtr.Zero) FreeLibrary(m_hLib);
}
//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的位置是不固定的.
...
DllInvoke dll = new DllInvoke(WinFacePathname);
if(dll.m_hLib == IntPtr.Zero) {
Response.Write("winface.dll加载失败");
Response.End();
return;
}
...
并都以该类的4个同名封装函数为准,不再深究 winface.dll 的原始API。| 函数 int GetActiveServices( ) | |
| 用途 | 取得当前进程中活动的服务数量 |
| 返回值 | 整数, 大于等于零 |
| 函数 bool OpenReportService(string CreatePara) | |
| 用途 | 打开报表服务 |
| 参数 | ";"分隔的 名-值对 串, 例如: LogSize=1000; LogLevel=2; TempDir=c:\\website\\temp;BaseDir=http://localhost/supcan 可用属性及其解释说明如下: TempDir - 临时目录的全名, 非常重要。因为硕正报表服务在运行过程中会产生一些临时缓存文件,而Web服务器对目录的写权限有很严格的控制,为此您必须提供一个匿名访问者都能写的物理目录; BaseDir - 为后继的函数中可能出现的相对URL提供统一的参考路径。在浏览器端的开发中,"相对URL" 通常是相对于页面的,而在后端开发中,这个相对URL的参考点就必须指定,因为报表服务根本不知道当前aspx的实际URL; LogLevel - 日志级别, 为0/1/2, 默认是0, 0表示不需要日志,1表示需要简单的日志, 2需要更详细的日志. 日志文件名为 @Log.txt, 会自动产生于临时目录中; LogSize - 日志文件最大尺寸, 单位为kb. 防止日志文件无限膨胀. |
| 返回值 | true/false, 失败的原因通常为DLL文件位置不正确 |
| 函数 string func(string funcname, string para) | |
| 用途 | 调用报表服务 |
| 参数 | 和硕正套件常规的用法完全一致,请参考硕正常规的开发文档 |
| 返回值 | 串, 也和套件的常规的用法一致 |
| 函数 void CloseService( ) | |
| 用途 | 关闭报表服务 |
| 返回值 | 无 |
~DllInvoke() {
CloseService( );
if(m_hLib != IntPtr.Zero) FreeLibrary(m_hLib);
}
但我们发现,这个 CloseService( )不一定会被执行到,可能和非托管有关,最终结果就是应用程序池资源耗尽,导致http请求响应异常,产生大量 httpStatusCode 为 503 的错误,甚至IIS重启都困难. 切记!