1.导言
信不信由你,20世纪90年代仍有许多项目开始进行原始开发。这主要是因为不可能完全从旧的编程语言和环境中迁移。因此,为了管理这一点,一个好主意可能是开发和集成写在.NET框架。有几种跑步方式。NET代码,但在本文中,我们将重点介绍两种方法:
C /CLI ·
·非托管导出
下图显示了我们的示例的架构。在这篇文章中,我们将使用名为Intercom.Logger的单个库来标准化整个应用程序中的日志记录方法。该库使用流行的log4net库写日志消息。Intercom.Logger在任何托管代码中都可以成功使用,但我们将在C应用程序中使用它。
图1 -体系结构图
2.创建对讲解决方案
出于本文的目的,有必要创建一个名为Intercom的新解决方案,其中包含两个项目: Intercom.Logger和Intercom.Client。
对讲机记录器
Intercom.Logger
是负责附加日志文件的托管库。为此,将使用名为log4net的外部库。
1.打开Visual Studio并创建一个名为对讲机与对讲机记录器类库项目。
图2 -创建对讲解决方案和Intercom.Logger类库。
2。使用NuGet为Intercom.Logger安装log4net。
,图3– Log4Net参考。
3.在项目Intercom.Logger中创建以下类。
命名空间 对讲机。记录器 { 使用 log4net; 使用 log4net。<span style="background-image:none;background-repeat:repeat;background-attachment: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; ">附加器; 使用 log4net。核心; 使用 log4net。布局; 使用<span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-pos位置: 0% 0%;mso-突出显示: 白色; ">log4net。存储库。层次结构;
公共 静态 类 记录器 { <span style="font-size:9.5pt;"> 私有 静态 只读 ILog MyLog;
静态 记录器 () {
<span style = “背景-图像: 无; 背景D-repeat: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; “>var层次结构 = (层次结构)LogManager。GetRepository();
PatternLayoutpatternLayout =新建 PatternLayout(); patternLayout。转换模式=“% date [% thread] %-5级别 % 记录器-% message % newline”; patternLayout。ActivateOptions();
FileAppender附加器 =新建 FileAppender(); <span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-position:0% 0%;mso-highlight: 白色; ">appender。文件=@ “Logs.txt”; appender。布局= patternLayout; appender。ActivateOptions();
层次结构。根。<span style = “背景-图像: 无; 背景-重复: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: 白色;”>AddAppender(附加人);
层次结构。根。等级=等级。信息;
层次结构。已配置=<span style="color:# 0000ff;">true;
MyLog=LogManager。GetLogger(字符串。空); }
<span style = “背景-图像: 无; 背景-重复: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: 白色;”>公共 静态 无效 日志(字符串消息) { MyLog。信息(消息); } <span style="background-image:none;background-repeat: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; ">} }
|
图4–
记录器类。
对讲机客户端
Intercom.Client
是需要日志记录功能的本机Win32应用程序。
1。在上一节创建的解决方案中,添加一个名为Intercom.Client的新C Win32控制台应用程序项目。
图5 -对讲机.客户端
2。将Windows.h包含在stdafx.h头文件中。
// stdafx.h : include file对于标准系统包含文件, 请
// 或项目特定的包含文件,这些文件经常使用,但使用 // 不经常更改 //
# pragma 一次
<span style = “背景-图像: 无; 背景-重复: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: w海特; “># 包括 “targetver.h”
# 包括 <stdio.h> # 包括 <tchar.h>
<span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-pos位置: 0% 0%;mso-突出显示: 白色; ">// TODO: 在此处引用程序所需的其他标头 # 包括 <Windows.h>
|
图6– stdafx.h头文件内容。
3.创建互操作项目
创建对讲解决方案后,有两个独立的组件:
·Intercom.Client非托管本机Win32控制台应用程序
·对讲机。记录器管理。NET框架类库
目标是使用Intercom.Logger应用程序中的库公开的Intercom.Logger记录功能。Intercom.Client本身是一个非托管应用程序,因此它不能直接引用和调用Intercom.Logger有必要创建某种互操作/代理库。
的图7 -已实现的组件。
Intercom.Logger.UnmanagedExports
非托管导出是向非托管本机代码公开托管C # 代码的一种方法。主要思想是将已经编译的模块反编译为IL代码,更改模块的VTable和VTableFixup表并重新编译DLL。通过使用名为UnmanagedExports的NuGet包,可以对开发人员不可见地执行此操作。该包中包含具有dlilexport属性的库和自动更改模块表的MSBuild项目。
1。在对讲解决方案中,添加一个名为Intercom.Logger.UnmanagedExports的新C # 类库项目。
图8 -添加Intercom.Logger.UnmanagedExports项目。
2。对于新创建的项目,安装名为UnmanagedExports的NuGet包。
图9 -安装UnmanagedExports包。
3.添加参考Intercom.Logger。
图10 -添加Intercom.Logger参考。
4.创建LoggerUnmanagedInterop代理类。
,向非托管本机代码公开的方法必须使用dlliexport特性进行修饰。
命名空间 对讲机。记录器。UnmanagedExports { # 区域使用 使用 系统。运行时<span style="backgr声音-图像: 无; 背景-重复: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: 白色; ">。InteropServices; 使用 RGiesecke。DllExport; # endregion
公共 静态<span style="background-image:none;background-repeat:repeat;background-attachment: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; ">类 LoggerUnmanagedInterop { [DllExport(“LoggerUnmanagedInterop_Log”,调用约定=调用约定。StdCall)] <span style="background-image:none;background-重复: 重复; 背景-附件: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; ">公共 静态 无效 日志( [元帅(UnmanagedType。LPStr)]字符串消息) { <span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-position:0% 0%;mso-highlight:white;"> Logger.Log (消息); }
}
}
|
图11– LoggerUnmanagedInterop类。
5.添加以下生成后事件命令,将二进制文件复制到Intercom.Client的输出目录中。
复制 $(TargetDir)* $(SolutionDir)$ (配置) \ |
图12 -添加后生成事件。
6.在生成设置中,将所有配置的平台目标更改为x86。
7。编辑UnmanagedExports程序集公开的Interop.Client. cpp主过程和调用方法。
# 包括 “stdafx.h”
// 定义公开过程的类型。 typedef 无效(回拨*日志) (字符消息 []); <span style="background-image:none;background-repeat:repeat;background-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: 白色; "> 智力 _tmain() { // 加载Intercom.Logger.UnmanagedExports.dll HMODULEhUnmanagedExports = LoadLibraryW (文本(“Intercom.Logger.UnmanagedExports.dll”)); <span style="background-image:none;background-repeat:repeat;background-attachment:sc滚动; 背景-位置: 0% 0%;mso-高亮: 白色; "> 如果 (hUnmanagedExports = = NULL ){ printf_s( “未能加载Intercom.Logger.UnmanagedExports.dll”) ); 回报 -1; }
<span style = “background-image:none;background-repeat:repeat;back地面-附件: 滚动; 背景-位置: 0% 0%;mso-突出显示: 白色; “>// 加载ManagedLogger_Log过程。 日志日志 = (日志) GetProcAddress(hUnmanagedExports,“LoggerUnmanagedInterop_Log”); 如果(日志 = =NULL) { printf_s (“LoggerUnmanagedInterop_Log过程的地址未知”<span style="background-image:none;background-repeat:repeat;background-attachment: 滚动; 背景-位置: 0% 0%;mso-高亮: 白色; "> ); 回报 -1; }
的 /// 调用公开的过程。 日志 ( “ 消息记录”);
<span style = “mso-tab-count:1;”> 返回0; } |
图13 -使用暴露的托管过程在非托管C代码。
8.构建解决方案。
9。导航到根解决方案目录中的Release/Debug文件夹。该目录应包含Intercom. client.exe和由Intercom.Logger.UnmanagedExports库的build-event复制的文件。
,图14– Release/Debug目录。
执行Intercom.Client.exe后,应创建日志文件。
图15 -执行结果。
从现在开始,本机程序Intercom.Client通过调用Intercom.Logger.UnmanagedExports中导出的过程来使用Intercop.logging中的日志记录功能。
下图显示了如何使用UnmanagedExports实现架构。Intercom.Logger.UnmanagedExports支持动态DLL加载的任何语言都可以使用: C,C,Clarion,VBA等。
的图16 -已实现的组件。
对讲机.Logger.CLI
暴露的第二种方式。NET方法的本地代码是通过创建一个C /CLI库。C /CLI是一种语言规范,旨在取代C的托管扩展。可以说,用C /CLI编写的库是C和C # 的一种混合体。
1。添加一个名为 “ Intercom.Logger.CLI ” 的新项目
图17 -添加CLI项目。
2。添加参考Intercom.Logger。
图18 -添加Intercom.Logger参考。
图19 -添加Intercom.Logger参考。
3。将CliLog函数头添加到文件Intercom.Logger.CLI.h
# pragma 一次
使用 命名空间的 系统;
extern在 _declspec “C” (dLleexport) 无效 __stdcall CliLog( char message[]);
|
图20– Intercom.Logger.CLI.
头文件。
4.在文件Intercom.Logger.CLI.cpp中实现CliLog功能
# 包括 “stdafx.h”
# 包括 “Intercom.Logger.CLI.h”
无效 __stdcallCliLog (字符 <span style="color:#808080;"> 消息[]) { 对讲机:: 记录器::记录器日志 (gcnew 字符串(信息)); }
|
图21– CliLog函数实现。
5.使用名为exports. def的导出过程定义创建一个文件,内容如下:
库对讲.Logger.CLI.DLL
出口 CliLog @ 4
|
图22 -exports.Def文件内容。
该文件包含将导出的过程的定义。表达式 @ 4表示参数中采用的字节数。在这种情况下,参数消息 [] 是32位指针,所以它的大小等于4个字节。
确保文件属性如下所示:
图23 -exports.Def文件属性。
exports.Def文件也应包含在项目属性中,如下图所示:
,图24 -包括exports.Def在项目属性中。
6.将以下代码添加到Intercom.Client.cpp中:
// 加载对讲机.Logger.CLI.dll HMODULEhCli = LoadLibraryW (文本(“Intercom.Logger.CLI.dll”)); 如果(hCli = =NULL) { printf_s (“无法加载Intercom.Logger.CLI.dll”); <span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-位置: 0% 0%;mso-高亮: 白色; ">返回-1; }
// 加载日志过程。 日志logCli = (日志) GetProcAddress(hCli,“CliLog”); 如果(logCli = =NULL<span style="background-image:none;background-repeat:repeat;background-attachment:scroll;background-p位置: 0% 0%;mso-突出显示: 白色; "> ){ printf_s( “” 日志过程地址未知 “ ); 回报 -1; }
// 调用公开的CLI过程。
logCli( “消息从CLI”); |
7。重建并运行Intercom.Client项目。
CLI和客户端二进制文件将自动复制到同一目录。在这种情况下,不需要添加任何生成后事件。运行后,文件Logs.txt还应包含通过调用CliLog() export添加的消息。
下图显示了已经实现的功能。这一次, Intercom.Client的应用程序使用CLI库执行Intercom.Logger中的托管代码。
,图25 -体系结构图。
4.总结
本文的主要目的是展示如何调用managed.NET代码从非托管本机C应用程序。这些示例非常简单,但可以为扩展用C /Clarion/VBA或其他可能受益于DLL导出的语言编写的企业应用程序奠定良好的基础。Log() 只包含一个参数: char数组,但它可以传递其他类型和整个结构。更多信息可以在下面列出的网站中找到,您也可以找到本文中使用的来源的链接:
DLL导出查看器可用于浏览DLL导出:
http://www.nirsoft.net/utils/ dll_export_viewer.html
有关UnmanagedExports的详细信息:
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
https://www.nuget.org/packages/UnmanagedExports
要查看此项目的源代码,请下载所有文档的zip文件。
想要构建具有高性能控件的桌面、移动或web应用程序?下载终极免费试用现在,看看它能为你做什么!