.Net组件编程练习

一、引言

1.实验目的

构建一个基于C#的组件,该组件具有以下功能:
1)给一个链接地址,下载这个地址所指向的网页;
2)对下载的网页进行去重、中文分词等,并给出这个文档的“主题词”和各词的重要性评价。(可自己写或者基于其他库来分词,主题词的定义可参考语言处理模型)

分别用VC 和 C#调用这个库,提供GUI界面。

2.环境配置

  • 操作系统:Windows 10 X64
  • 编程工具:Visual Studio 2017

二、c#封装动态链接库DLL并调用

1.准备阶段

(1)使用Visual Studio 2017,新建c#项目:文件->新建->项目

在这里插入图片描述

(2)点击左侧的Visual C#,选择Windows窗体应用(.NET Framework),命名为JiebaSpider,选择自己的文件位置,框架选择.NET Framework 4.5,然后选择确定

在这里插入图片描述

(3)创建好项目之后,设计GUI界面,点击视图->工具箱

在这里插入图片描述

(4)按照下图将控件的位置先拉好(直接从工具箱拖拉拽控件即可)

在这里插入图片描述

(5)右键控件选择属性,在右侧属性栏外观下修改Text如下图

在这里插入图片描述
在这里插入图片描述

2.编译c#类并利用Jieba分词工具进行分词

(1)导入jieba.NET包

点击工具->NuGet包管理器->管理解决方案的NuGet程序包

在这里插入图片描述

点击浏览,输入jieba,选择jieba.NET包,在右侧选中项目,点击安装

在这里插入图片描述

点击确定

在这里插入图片描述

(2)编辑Form1.cs类

具体代码如下:Form1.cs
using JiebaNet.Analyser;
using JiebaNet.Segmenter;
using System;
using System.Net;
using System.Text;
using System.Windows.Forms;

namespace JiebaSpider
{
    public partial class Form1 : Form
    {
        public Form1()
        {

            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string url = textBox1.Text;
            Uri httpURL = new Uri(url);

            ///HttpWebRequest类继承于WebRequest,并没有自己的构造函数,需通过WebRequest的Creat方法 建立,并进行强制的类型转换   
            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(httpURL);
            //httpReq.Headers.Add("cityen", "tj");

            ///通过HttpWebRequest的GetResponse()方法建立HttpWebResponse,强制类型转换   
            HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();

            ///GetResponseStream()方法获取HTTP响应的数据流,并尝试取得URL中所指定的网页内容   
            ///若成功取得网页的内容,则以System.IO.Stream形式返回,若失败则产生ProtoclViolationException错 误。
            System.IO.Stream respStream = httpResp.GetResponseStream();

            ///返回的内容是Stream形式的,所以可以利用StreamReader类获取GetResponseStream的内容
            System.IO.StreamReader respStreamReader = new System.IO.StreamReader(respStream, Encoding.UTF8);
            //从流的当前位置读取到结尾
            string strBuff = respStreamReader.ReadToEnd();

            respStreamReader.Close();
            respStream.Close();

            //使用Jieba组件进行分词操作
            var segmenter = new JiebaSegmenter();
            var segments = segmenter.Cut(strBuff);
            textBox2.Text = string.Join("/ ", segments);
            var extractor = new TfidfExtractor();

            //获取文档的关键词和权重,这里是提取了前十个关键名词或动词
            var keywords = extractor.ExtractTagsWithWeight(textBox2.Text, 10, Constants.NounAndVerbPos);
            foreach (var keyword in keywords)
            {
                textBox3.Text += keyword.Word + "(" + String.Format("{0:F}", keyword.Weight) + ")" + " ";
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
        }
    }
}

可以使用Alt+Enter快捷键导入引用的包

在这里插入图片描述

(3)把jieba.NET包里的Resources文件复制进JiebaSpider/bin/Debug文件夹下(不然运行会报错)

在这里插入图片描述
在这里插入图片描述

(4)运行结果,测试课程网站,进行分词并给出主题词和权重

在这里插入图片描述

3.生成动态链接库DLL

(1)点击项目->JiebaSpider属性

在这里插入图片描述

(2)选中左侧的应用程序,将输出类型改为类库,保存

在这里插入图片描述

(3)点击生成->重新生成解决方案

在这里插入图片描述

(4)可以再JiebaSpider->bin->Debug文件夹下看到生成的动态链接库JiebaSpider.dll

在这里插入图片描述

4.在c#中调用JiebaSpider.dll库

(1)创建一个新项目,选择Windows窗体应用(.NET Framework),命名为Test,框架版本为。NET Framework4.5,点击确定

在这里插入图片描述

(2)引用JiebaSpider.dll库

在解决方案里右键引用,选择添加引用
在这里插入图片描述
点击浏览,找到JiebaSpider.dll文件,点击添加
在这里插入图片描述
在这里插入图片描述

(3)修改Form1的名字(避免和要调用的dll库里的Form1重名)

右键Form1视图,选择属性
在这里插入图片描述
在右侧属性栏中修改Name和Text为Form1_test
在这里插入图片描述

(4)创建一个Button组件,用来调用JiebaSpider的界面

在这里插入图片描述

(5)编写Form1.cs

在这里插入图片描述
具体代码如下:Form1.cs

using JiebaSpider;
using System;
using System.Windows.Forms;

namespace Test
{
    public partial class Form1_test : Form
    {
        public Form1_test()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Form1 form = new Form1();
            this.Hide();
            form.Show();//显示在JiebaSpider项目中设计的那个Form1窗体
        }
    }
}

(6)把JiebaSpider/bin/Debug文件夹下的Resources文件复制进Test/bin/Debug文件夹

这里是因为要调用jieba.NET里的方法需要用到Resources里的文件
在这里插入图片描述

(7)运行程序,点击开始测试按钮,调用成功

在这里插入图片描述

三、VC++调用c#动态链接库DLL

c++调用c#的动态链接库和c#调用操作类似,这里就不重复一遍了,接下来主要是从c++调用c#生成的COM组件角度分析

1. 用C#编写dll

(1)文件->新建->项目

在这里插入图片描述
选择类库(.NET Framework),命名为JiebaSpiderClass,框架为.NET Framework4.5,点击确定
在这里插入图片描述

(2)创建接口类

在解决方案里,右键项目->添加->新建项
在这里插入图片描述
选择接口,命名为IJieba.cs,点击添加
在这里插入图片描述

(3)编写代码

具体代码如下:

IJieba.cs:
using System.Runtime.InteropServices;

namespace JiebaSpiderClass
{
    //可替换成自己的GUID
    [Guid("7CA6063F-EFD1-4EE0-81CD-3C5E64A8894E")]
    public interface IJieba
    {
        [DispId(1)]
        void Split(string URL, out string strBuff);
        [DispId(2)]
        void Getkey(string strBuff, out string Text2);
    }
}

Jieba.cs:
using JiebaNet.Analyser;
using JiebaNet.Segmenter;
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;

namespace JiebaSpiderClass
{
    //可替换成自己的GUID
    [Guid("9164BC25-F8F8-4192-935C-9B230241E63F")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Jieba : IJieba
    {

        public void Split(string URL, out string strBuff)
        {
            string url = URL;
            Uri httpURL = new Uri(url);

            ///HttpWebRequest类继承于WebRequest,并没有自己的构造函数,需通过WebRequest的Creat方法 建立,并进行强制的类型转换   
            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(httpURL);
            //httpReq.Headers.Add("cityen", "tj");

            ///通过HttpWebRequest的GetResponse()方法建立HttpWebResponse,强制类型转换   
            HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();

            ///GetResponseStream()方法获取HTTP响应的数据流,并尝试取得URL中所指定的网页内容   
            ///若成功取得网页的内容,则以System.IO.Stream形式返回,若失败则产生ProtoclViolationException错 误。
            System.IO.Stream respStream = httpResp.GetResponseStream();

            ///返回的内容是Stream形式的,所以可以利用StreamReader类获取GetResponseStream的内容
            System.IO.StreamReader respStreamReader = new System.IO.StreamReader(respStream, Encoding.UTF8);
            //从流的当前位置读取到结尾
            strBuff = respStreamReader.ReadToEnd();

            //使用Jieba组件进行分词操作
            //var segmenter = new JiebaSegmenter();
            //var segments = segmenter.Cut(strBuff);
            //Text1 = string.Join("/ ", segments);

            respStreamReader.Close();
            respStream.Close();
            return;
        }

        public  void Getkey(string strBuff,out string Text2)
        {
            //使用Jieba组件进行分词操作
            var segmenter = new JiebaSegmenter();
            var segments = segmenter.Cut(strBuff);
            string Text1 = string.Join("/ ", segments);
            Text2 = "";
            var extractor = new TfidfExtractor();
            //获取文档的关键词和权重,这里是提取了前十个关键名词或动词
            var keywords = extractor.ExtractTagsWithWeight(Text1, 10, Constants.NounAndVerbPos);
            foreach (var keyword in keywords)
            {
                Text2 += keyword.Word + "(" + String.Format("{0:F}", keyword.Weight) + ")" + " ";
            }
            return;
        }
    }
}

(4)创建GUID,替换上述文件里的GUID,两个文件不一样

点击工具->创建GUID

在这里插入图片描述
选择第5个,点击复制,替换文件里的GUID
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)设置“使程序集COM可见”和“为COM互操作注册”

点击项目->JiebaSpiderClass属性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置好后保存文件

(6)以管理员身份重新打开这个项目,生成动态链接库

由于注册表权限的问题,这里需要用到管理员权限

点击生成->生成解决方案
在这里插入图片描述
生成成功
在这里插入图片描述
在JiebaSpiderClass/bin/Debug文件夹下可以看到生成的JiebaSpiderClass.dll和JiebaSpiderClass.tlb文件
在这里插入图片描述

2. 将dll封装成COM组件

(1)新建一个项目,选择c++的动态链接库,命名为JiebaCom,点击确定

在这里插入图片描述

(2)把之前生成的JiebaSpiderClass.dll和JiebaSpiderClass.tlb文件复制进JiebaCom/JiebaCom文件夹下

在这里插入图片描述

(3)编辑JiebaCom.cs

具体代码如下:JiebaCom.cs

// JiebaCom.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"

using namespace JiebaSpiderClass;

#define DLLEXPORT extern "C" __declspec(dllexport)


DLLEXPORT void Split(char* URL, char* str)
{
	CoInitialize(NULL);
	JiebaSpiderClass::IJiebaPtr JiebaPtr(__uuidof(Jieba));//获取Jieba所关联的GUID
	BSTR temp;
	JiebaPtr->Split(_bstr_t(URL), &temp);
	strcpy(str, _com_util::ConvertBSTRToString(temp));
	JiebaPtr->Release();
	CoUninitialize();
}

DLLEXPORT void Getkey(char* str, char* Text)
{
	CoInitialize(NULL);
	JiebaSpiderClass::IJiebaPtr JiebaPtr(__uuidof(Jieba));//获取Calc所关联的GUID
	BSTR temp;
	JiebaPtr->Getkey(_bstr_t(str), &temp);
	strcpy(Text, _com_util::ConvertBSTRToString(temp));
	JiebaPtr->Release();
	CoUninitialize();
}

(4)修改头文件stdafx.h

添加一句#import “JiebaSpiderClass.tlb”

具体代码如下:

// stdafx.h: 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 项目特定的包含文件
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>



// 在此处引用程序需要的其他标头
#import "JiebaSpiderClass.tlb"

(5)配置预处理器,避免4996错误提示

点击项目->JiebaCom属性,打开属性页,点击C/C++目录下的预处理器,编辑预处理器定义,添加_CRT_SECURE_NO_DEPRECATE,点击确定
在这里插入图片描述

(6)设置项目字符集为“使用多字节字符集”

这里是为了避免一些格式不兼容的错误

点击常规->字符集,选择使用多字节字符集,点击应用,再点击确定
在这里插入图片描述

(7)生成解决方案

在这里插入图片描述
生成的JiebaCom.dll文件在Jieba/Debug文件夹下
在这里插入图片描述

3. c++调用c#封装成的COM组件

(1)创建一个新项目

选择Visual C++目录下的MFC/ATL(如果vs没有需要安装),选择MFC应用程序,命名为JiebaTest,点击确定
在这里插入图片描述
在MFC应用程序界面选择“基于对话框”的应用程序类型,选择完成
在这里插入图片描述

(2)编辑GUI界面

在解决方案资源管理器里打开资源文件目录,双击JiebaTest.rc文件
在这里插入图片描述
打开Dialog目录,双击IDD_JIEBATEST_DIALOG
在这里插入图片描述
创建好GUI组件,和上文的类似
在这里插入图片描述
这里设置分词结果的Edit Controller的格式,为可滑动的文本框

多行Multiple -> Ture

返回换行Want Return -> True

垂直滚动Vertical Scroll -> Ture
在这里插入图片描述

(3)编辑JiebaTestDlg.cpp文件

这里是需要声明方法:

typedef void(Split)(char URL, char* str);
typedef void(Getkey)(char str, char* text);
在这里插入图片描述
在视图界面双击“分词”按钮,编辑按钮的点击事件
在这里插入图片描述

这一块的具体代码如下:

void CJiebaTestDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	char A[255];
	char B[100000];
	char C[100000];

	CString str_A;
	CString str_B;
	CString str_C;

	GetDlgItem(IDC_EDIT1)->GetWindowText(str_A);

	strcpy(A, str_A);
	strcpy(B, str_B);
	strcpy(C, str_C);
	HINSTANCE calc;
	calc = LoadLibrary(TEXT("JiebaCom.dll"));
	if (NULL == calc)
	{
		MessageBox("cant't find dll");
		return;
	}

	Split _Split = (Split)::GetProcAddress(calc, "Split");
	Getkey _Getkey = (Getkey)::GetProcAddress(calc, "Getkey");

	if (NULL == _Split)
	{
		MessageBox("cant't find function Split");
		return;
	}
	else
	{
		_Split(A, B);
		SetDlgItemText(IDC_EDIT2, B);
		//CString boxMsg;
		//boxMsg.Format("分词结果:%s\n", B);
		//MessageBox(boxMsg);
	}
	if (NULL == _Getkey)
	{
		MessageBox("cant't find function Getkey");
		return;
	}
	else
	{
		_Getkey(B,C);
		SetDlgItemText(IDC_EDIT3, C);
	}
}

(4)添加动态链接库JiebaCom.dll

将JiebaCom.dll文件复制到JiebaTest/JiebaTest文件夹下
在这里插入图片描述

(5)运行程序,查看结果

四、总结

在本次实验中遇到了一些问题,现在总结一下:

(1)在利用jieba.NET包进行分词的时候,只添加jieba.NET包是不行的,还需要把jieba.NET文件夹下面的Resources文件夹复制到源程序的Debug文件夹下,否则使用会报错。

(2)在用VC调用c#生成动态链接库中,c#在编译动态链接库时报“无法注册程序集***dll- 拒绝访问。请确保您正在以管理员身份运行应用程序。对注册表项”***“的访问被拒绝。”的错误。引起这个问题的根本原因是win10的管理员权限问题,在win10系统中安装程序时,系统都会各种要管理员权限才能执行。

(3)最后一个问题是c++不管在调用.NET组件还是COM组件时,都不能正常的调用jieba.NET的方法,c++引入的.NET Framework框架是4.0版本的,而jieba.NET只有在.NET Framework4.5版本下才能正常工作,所以本次实验中,只实现了VC++调用c#的动态链接库里的爬虫方法,分词方法无法正常调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值