第六章 系统实施
6.1 系统实施概述
在第五章系统设计进行了模块划分,在本章中将对各个模块的功能进行逐个编码实现。首先对手机端软件系统的各个模块进行编码实现,再对计算机端软件系统的各个模块进行编码实现。
6.2 手机端系统实施
6.2.1蓝牙设备搜索模块
蓝牙设备搜索模块是通过LANYA_FAXIAN类来实现,此类主要用于搜索蓝牙设备,它的部分代码如下。
public static LANYA_FAXIAN LANYA_FAXIANG_instance;
public int lanyageshu=0;
public boolean sousuowancheng=false;
public boolean meiyousousuodao=false;
public LocalDevice localdevice;
public DiscoveryAgent discoveryagent;
public String[] lanya_device_name;
public boolean serviceshiyong=false;
private Thread processorThread;
Vector devices=new Vector();
////////////////////////////////////////////////////////////////////////////
public LANYA_FAXIAN(){
LANYA_FAXIANG_instance=this;
try{
localdevice=LocalDevice.getLocalDevice();
}
catch(Exception e){
e.printStackTrace();
}
discoveryagent=localdevice.getDiscoveryAgent();
////////////////////////////////////////////////////////////////////
processorThread=new Thread(LANYA_FAXIANG_instance);
processorThread.start();
}
////////////////////////////////////////////////////////////////////////////
public void run(){
devicesearch();//设备搜索;
}
public void devicesearch(){
try{
discoveryagent.startInquiry(DiscoveryAgent.GIAC, LANYA_FAXIANG_instance);
}
catch(Exception e){
e.printStackTrace();
}
}
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
devices.addElement(btDevice);
}
public void inquiryCompleted(int discType) {
sousuowancheng=true;
if(devices.size()>4){ /////////////只允许有4个设备
lanyageshu=4;
}
else{
lanyageshu=devices.size();
}
if(discType==INQUIRY_COMPLETED){
if(lanyageshu>=1){
meiyousousuodao=false;
lanya_device_name=new String[lanyageshu];
showDevices();
}
else{
meiyousousuodao=true;
}
}
}
public void showDevices(){
for(int i=0;i<lanyageshu;i++){
RemoteDevice RD=(RemoteDevice)devices.elementAt(i);
try{
lanya_device_name[i]=RD.getFriendlyName(sousuowancheng);
}
catch(Exception e){
e.printStackTrace();
}
}
}
当执行时,首先会调用LANYA_FAXIAN类的构造函数,构造函数中会创建一个设备发现代理类的对象,此对象会调用自身的startInquiry方法,startInquiry方法则会让系统执行回调函数 deviceDiscovered()将搜索到的蓝牙远程设备保存在一个向量devices结构之中供以后使用,回调函数 deviceDiscovered()完成之后,系统会执行回调函数inquiryCompleted(),完成之后将远程蓝牙设备名称保存在数组lanya_device_name之中供以后使用。
6.2.2蓝牙服务查询和连接模块
蓝牙服务查询和连接模块是用REMOTE_DEVICE_Services类来实现,此类主要用于蓝牙服务查询和连接,它的部分代码如下。
public LocalDevice localdevice;
public DiscoveryAgent discoveryagent;
RemoteDevice btDevice;
////////////////////////////////////////////////////////////////////////
private static final UUID ECHO_SERVER_UUID= new UUID(0x1101);
private static UUID[] uuid;
boolean sousuoserviceswancheng=false;
Vector services=new Vector();
public StreamConnection streamconnection;
DataOutputStream dos;
////////////////////////////////////////////////////////////////////////
public REMOTE_DEVICE_Services(){
try{
localdevice=LocalDevice.getLocalDevice();
}
catch(Exception e){
e.printStackTrace();
}
discoveryagent=localdevice.getDiscoveryAgent();
uuid=new UUID[1];
uuid[0]=ECHO_SERVER_UUID;
sousuoserviceswancheng=false;
}
public void setbtDevice(RemoteDevice btDevice){
this.btDevice=btDevice;
}
public void srevicesearch_connect(){//查询服务
if(this.btDevice!=null){
try{
this.discoveryagent.searchServices(null,uuid,this.btDevice,this);
}
catch(BluetoothStateException e){
e.printStackTrace();
}
}
}
public void servicesDiscovered(int transID, ServiceRecord[] servRecord){
for (int i = 0; i < servRecord.length; i++){
services.addElement(servRecord[i]);
}
}
public void serviceSearchCompleted(int transID, int respCode) {
access_service();
}
public void access_service(){//连接到远程串口服务
ServiceRecord servRecord=null;
if(services.size()!=0){
for(int i=0;i<services.size();i++){
servRecord=(ServiceRecord)services.elementAt(i);
if(servRecord!=null){
break;
}
}
String
conURL=servRecord.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
try{
streamconnection=(StreamConnection)Connector.open(conURL);
dos=streamconnection.openDataOutputStream();
}
catch(Exception e){
e.printStackTrace();
}
}
}
public void close_connect(){//关闭连接 释放连接流
try{
if(dos!=null){
dos.close();
dos=null;
}
if(streamconnection!=null){
streamconnection.close();
dos=null;
}
}
catch(Exception e){
e.printStackTrace();
}
}
当系统执行时,首先会调用REMOTE_DEVICE_Services类的构造函数,构造函数中会创建一个设备发现代理类的对象discoveryagent,此对象也可以用来服务查询。当执行setbtDevice(RemoteDevice btDevice)函数时,将得到一个远程的蓝牙设备对象,此对象保存在btDevice 中。当执行 srevicesearch_connect() 函数时,discoveryagent会调用searchServices(null,uuid,this.btDevice,this)方法执行蓝牙服务查询(uuid为服务识别标志),此时会执行回调函数servicesDiscovered和serviceSearchCompleted,完成服务查询,然后调用access_service()来进行远程设备连接,并创建DataOutputStream对象 dos来与远程设备进行通信。连接关闭则调用close_connect()方法。
6.2.3媒体拍照模块
媒体拍照模块的功能放在LANYA_MEDIUM类中来实现。此类可以用来拍摄照片和保存照片,这是LANYA_MEDIUM的部分代码。
Player player=null;
VideoControl videocontrol=null;
byte[] imgData;
////////////////////////////////////////////////////////////////////////////
public LANYA_MEDIUM(){
try{
LANYA_XUANZE_MIDPCanvas canvas
=new LANYA_XUANZE_MIDPCanvas();
player=Manager.createPlayer("capture://video");
player.realize();
videocontrol=(VideoControl)(player.getControl("VideoControl"));
/////////////////////////////////////////////////////////////////////////////////////////////////
if(videocontrol!=null){
videocontrol.initDisplayMode
(VideoControl.USE_DIRECT_VIDEO,canvas);
videocontrol.setVisible(true);
videocontrol.setDisplaySize(240,320);
}
player.start();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////////
public void guan_bi_medium(){
if(player != null){
player.close();
player = null;
}
}
public void takesnapshot(){//拍照
if(player!=null){
try{
imgData=videocontrol.getSnapshot(null);
}
catch(Exception e){
e.printStackTrace();
}
}
}
在LANYA_MEDIUM类的构造函数中用Manager创建一个Player对象,再创建一个videocontrol对象,并且设置照片大小为240*320。当构造函数执行完毕后就可以调用takesnapshot()函数进行拍照,并键照片保存在imgData比特数组中。
6.2.4鼠标控制模块
鼠标控制模块的功能也是在REMOTE_DEVICE_Services类中实现,该类主要用来发送鼠标控制信息,当计算机端软件接收到相应的鼠标控制信息,就会产生相应的动作,如鼠标移动和鼠标点击等。下面是REMOTE_DEVICE_Services类中实现鼠标控制部分的代码。
public void mouse_up(){
try{
Mouse_Repeated MD=new Mouse_Repeated();
mouse_str="mouse_up";
stop=true;
MD.start();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public void mouse_down(){
try{
Mouse_Repeated MD=new Mouse_Repeated();
mouse_str="mouse_down";
stop=true;
MD.start();
}
catch(Exception e){
e.printStackTrace();
}
}
public void mouse_left(){
try{
Mouse_Repeated MD=new Mouse_Repeated();
mouse_str="mouse_left";
stop=true;
MD.start();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public void mouse_right(){
try{
Mouse_Repeated MD=new Mouse_Repeated();
mouse_str="mouse_right";
stop=true;
MD.start();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public void mouse_left_kick_down(){
try{
dos.writeChars("mouse_left_kick_down");
dos.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public void mouse_left_kick_up(){
try{
dos.writeChars("mouse_left_kick_up");
dos.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public void mouse_right_kick_down(){
try{
dos.writeChars("mouse_right_kick_down");
dos.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
public void mouse_right_kick_up(){
try{
dos.writeChars("mouse_right_kick_up");
dos.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
public void keyReleased_contral(){
stop=false;
}
public void set_sleep_time(){
if(sleep_time==0){
sleep_time=5;
}
else{
sleep_time=0;
}
}
/////////////////////////////////////////////////////////////////////////////
public class Mouse_Repeated extends Thread {
public void run() {
while(stop==true){
try{
dos.writeChars(mouse_str);
dos.flush();
Thread.sleep(sleep_time);
}
catch(Exception e){
e.printStackTrace();
}
}
}
}
当鼠标需要向上移动时调用mouse_up()方法。
当鼠标需要向下移动时调用mouse_download()方法。
当鼠标需要向左移动时调用mouse_left()方法。
当鼠标需要向右移动时调用mouse_right()方法。
上面的动作具体都是放在线程类 Mouse_Repeated中实现的,该线程循环把鼠标控制信息发送出去,实现鼠标指针不停移动。此外 keyReleased_contral() 方法用于停止发送鼠标控制信息,set_sleep_time()用于控制鼠标控制信息发送的速度来控制计算机端鼠标移动速度。
当调用mouse_left_kick_down()方法,发送鼠标左键按下控制信息。
当调用mouse_left_kick_up()方法,发送鼠标左键释放控制信息。
当调用mouse_right_kick_down()方法,发送鼠标右键按下控制信息。
当调用mouse_right_kick_up()方法,发送鼠标右键释放控制信息。
6.2.5照片传输模块
照片传输模块的功能也是在REMOTE_DEVICE_Services类中实现,该类主要用来发送照片,当计算机端软件接收到照片时,就会显示出来,当然也可以保存照片。下面是REMOTE_DEVICE_Services类中实现鼠标控制部分的代码。
public void mobile_video(){
try{
mobile_video_thread m_v_d=new mobile_video_thread();
dos.writeChars("mobile_video");
dos.flush();
m_v_d.start();
}
catch(Exception e){
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////
public class mobile_video_thread extends Thread{
public void run(){
try{
lanya_medium.takesnapshot();
pic_length=String.valueOf(lanya_medium.imgData.length);
dos.writeChars(pic_length);
dos.flush();
dos.write(lanya_medium.imgData);
dos.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
}
程序执行时先把控制信息发送过去,使计算机端软件转到照片接收程序,然后在线程中把照片发送过去,先发照片大小再发送照片数据。
6.2.6显示界面模块
在程序中使用下的类来构建手机端软件界面。
类名 |
QD_MIDPCanvas |
ZHUHUAMIAN_MIDPCanvas |
KAISHI_MIDPCanvas |
BANGZHU_MIDPCanvas |
GUANYU_MIDPCanvas |
LANYA_XUANZE_MIDPCanvas |
表5 界面模块类
这些类都继承自javax.microedition.lcdui.Canvas类(Displayable子类),因此可以被显示在屏幕上。
6.3 计算机端系统实施
6.3.1背景音乐播放模块
背景音乐播放模块放在动态链接库yinyue.dll中,在这个动态链接库中使用了DirectShow的音乐播放组件,下面对DirectShow作一下简单介绍。
在项目中使用MP3文件,需要使用DirectX中的 DirectShow组件,在这个组件的帮助下,只需几行短短的代码,就能播放任意的MP3文件了(DirectShow也支持其他的媒体文件,比如 WMA,AVI,MPG等)。
DirectX是一组COM接口组件,DirectShow也不例外,DirectShow中经常使用的组件如下:
IGraphBuilder:帮助建立滤过滤波图,滤波过滤图是一组对象或者接口的集合,用于处理某种媒体文件。
IMediaControl:控制数据在滤波图中的流程,使用该接口控制音乐的回放。
IMediaEvents:从滤波图中获取事件及通告,当希望知道在滤波图中发生了什么的时候这个对象很有用,比如希望知道一个音乐是否仍然在播放或者已经停止播放。
其中第一个接口IGraphBuilder是比较重要的对象,其他对象都必须依赖于它,或者靠它创建。它创建滤波器,用于处理媒体问题,另外很多有用的功能也是依靠这个对象。下面是yinyue.dll中的部分代码。
DWORD CyinyueApp::bofang(void){
pControl->Run();
return 0;
}
DWORD CyinyueApp::guanbi(void){
if(pControl!=NULL){
pControl->Release();
}
if(pEvent!=NULL){
pEvent->Release();
}
if(pGraph!=NULL){
pGraph->Release();
}
::CoUninitialize();
return 0;
}
DWORD CyinyueApp::zanting(void){
pControl->Pause();
return 0;
}
DWORD CyinyueApp::chushi(void){
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)){
AfxMessageBox(L"初始化失败!");
return 0;
}
hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void **)&pGraph);
if (FAILED(hr)){
AfxMessageBox(L"无法播放音乐!");
return 0;
}
hr = pGraph->QueryInterface(IID_IMediaControl,
(void **)&pControl);
if (FAILED(hr)){
AfxMessageBox(L"无法播放音乐!");
return 0;
}
hr = pGraph->QueryInterface(IID_IMediaPosition,
(LPVOID*)&g_pMediaPosition);
if (FAILED(hr)){
AfxMessageBox(L"无法播放音乐!");
return 0;
}
hr = pGraph->QueryInterface(IID_IMediaEvent,
(void **)&pEvent);
if (FAILED(hr)){
AfxMessageBox(L"无法播放音乐!");
return 0;
}
hr = pGraph->RenderFile(L"yinyue//yinyue.mp3", NULL);
if (FAILED(hr)){
//AfxMessageBox(L"无法加载音乐!请把mp3文件命名为yinyue.mp3");
return 0;
}
hr=pControl->Run();
OnGraphNotify();
return 0;
}
void OnGraphNotify(){
long evCode;
LONG param1, param2;
while(1){
pEvent->GetEvent(&evCode,¶m1, ¶m2, 0);
switch (evCode){
//我们的媒体播放结束了
case EC_COMPLETE:
//重设播放位置为,就是倒回的意思
g_pMediaPosition->put_CurrentPosition(0);
pControl->Run();
//以上这样,就是循环播放!
break;
};
pEvent->FreeEventParams(evCode, param1, param2);
::Sleep(100);
};
}
当程序执行时先调用chushi(void)函数,在chushi(void)函数中,第一步是调用 CoCreateInstance函数来创建一个滤波过滤图IGraphBuilder的对象pGraph。一旦创建IGraphBuilder成功,就可以请求另三个接口了:
pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
pGraph->QueryInterface(IID_IMediaPosition, (LPVOID*)&g_pMediaPosition);
pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
pControl用来控制播放功能,g_pMediaPosition用来设定播放位置,pEvent用来捕获播放过程中的各种事件,如播放停止事件。接下来就加载媒体文件了。实际上, DirectShow并不加载媒体文件,而是创建一个DirectShow滤波器到文件的连接。数据在解码的时候被流化,这样可以减少在播放过程中的内存使用,创建连接的过程叫做渲染(rendering)。渲染文件,需要调IGraphBuilder::RenderFile函数。然后执行pControl->Run(),就开始播放音乐了。执行OnGraphNotify()函数来进行循环播放,这主要是通过pEvent捕获播放事件进行的,当捕获到播放停止事件时,用g_pMediaPosition->put_CurrentPosition(0)让播放重新开始。
6.3.2文本语音转换模块
文本语音转换模块的功能放在动态链接库speek.dll中,此功能运用了微软的TTS技术,下面对TTS技术作一下介绍。
文本语音(Text-to-Speech,简称TTS),它主要的作用就是把通过TTS引擎把文本转化为语音输出。Microsoft Speech SDK是微软提供的软件开发包,提供的API for Text-to-Speech,就是微软TTS引擎的接口,通过它我们可以很容易地建立功能强大的文本语音程序,金山词霸的单词朗读功能就用到了这写API,而目前几乎所有的文本朗读工具都是用这个SDK开发的。下面是speek.dll的部分代码。
CSpeakApp::CSpeakApp(){
//COM初始化:
if (FAILED(::CoInitialize(NULL))){
return ;
}
//获取ISpVoice接口:
hrspeek = CoCreateInstance(CLSID_SpVoice,
NULL,
CLSCTX_ALL,
IID_ISpVoice,
(void **)&pVoice);
return ;
}
BOOL CSpeakApp::speek(bool flag,LPCWSTR speeking){
if(flag==true){
pVoice->Speak(speeking, 0, NULL);
}
return TRUE;
}
CSpeakApp::~CSpeakApp(){
if(!pVoice){
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
}
在CSpeakApp的构造函数中获取IspVoice接口,然后在speek函数中使用ISpVoice::Speak()把文本输出为语音,程序的核心就是IspVoice接口。除了Speak外IspVoice接口还有许多成员函数,下面说一下几个Speak函数用法。
HRESULT Speak(const WCHAR *pw,DWORD Flag,ULONG *StreamNum);
参数:
*pw 输入的文本字符串,必需为Unicode,如果是ansi字符串必需先转换为Unicode。
Flag 用来标志Speak的方式,其中SPF_IS_XML 表示输入文本含有XML标签,在本程序中设为0。
*StreamNum 用来获取去当前文本输入的等候播放队列的位置,只有在异步模式才有用。
6.3.3蓝牙功能模块
蓝牙功能模块放在CLANYA_PC中实现,主要用来创建蓝牙套接字和蓝牙串口服务的注册和发布,下面实现的部分代码。
DWORD CLANYA_PC::CREATE_CONNECT(void){
WORD wVersionRequested = 0x202;
WSADATA m_data;
::WSAStartup( wVersionRequested,&m_data);//初始化套接字
m_sock=socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (m_sock==INVALID_SOCKET)
return 1;
SOCKADDR_BTH SockAddrBthLocal = {0};//设置本地蓝牙地址
SockAddrBthLocal.addressFamily = AF_BTH;
SockAddrBthLocal.port = BT_PORT_ANY;//设置端口
if (bind(m_sock,
(SOCKADDR *)&SockAddrBthLocal,
sizeof(SOCKADDR_BTH))== SOCKET_ERROR){
return 2;
}
int iAddrLen = sizeof(SOCKADDR_BTH);
if (getsockname(m_sock, (struct sockaddr *)&SockAddrBthLocal,
&iAddrLen) == SOCKET_ERROR){
return 3;
}
LPCSADDR_INFO lpCSAddrInfo=//设置地址结构信息
(LPCSADDR_INFO)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(CSADDR_INFO));//分配内存
lpCSAddrInfo[0].LocalAddr.iSockaddrLength =
sizeof( SOCKADDR_BTH );
lpCSAddrInfo[0].LocalAddr.lpSockaddr =
(LPSOCKADDR)&SockAddrBthLocal;
lpCSAddrInfo[0].RemoteAddr.iSockaddrLength =
sizeof( SOCKADDR_BTH );
lpCSAddrInfo[0].RemoteAddr.lpSockaddr =
(LPSOCKADDR)&SockAddrBthLocal;
lpCSAddrInfo[0].iSocketType = SOCK_STREAM;
lpCSAddrInfo[0].iProtocol = BTHPROTO_RFCOMM;
WSAQUERYSET wsaQuerySet = {0};
ZeroMemory(&wsaQuerySet, sizeof(WSAQUERYSET));
wsaQuerySet.dwSize = sizeof(WSAQUERYSET);
wsaQuerySet.lpServiceClassId =
(LPGUID) &SerialPortServiceClass_UUID;
wsaQuerySet.lpszServiceInstanceName =
(LPWSTR)"YUANYUEXIANG";//服务名称
wsaQuerySet.lpszComment = L"";
wsaQuerySet.dwNameSpace = NS_BTH;
wsaQuerySet.dwNumberOfCsAddrs = 1;
wsaQuerySet.lpcsaBuffer = lpCSAddrInfo;
//注册服务,这样服务就可以被搜索到
if (WSASetService(&wsaQuerySet, RNRSERVICE_REGISTER, 0)==
SOCKET_ERROR){
return 4;
}
if (listen(m_sock, SOMAXCONN)==SOCKET_ERROR){
return 5;
}
//弄一条线程并使之循环等待连接
::AfxBeginThread(Accept_Connect,this);
return 0;
}
//等待连接,连接成功后,继续等待
UINT Accept_Connect(void* p){
CLANYA_PC* eg=(CLANYA_PC*)p;
while(1){
sClient = accept(m_sock, NULL, NULL );
if (sClient == INVALID_SOCKET) {
//printf_s("accept 失败: %d/n", WSAGetLastError());
return 0;
}
else {
//连接成功标志
lianjie_ok=true;
}
}
return 0;
}
当执行完CREATE_CONNECT函数后,如果蓝牙套接字创建和服务注册成功,就会执行线程Accept_Connect等待手机端软件连接的到来。
6.3.4系统调度模块
系统调度模块的功能也是放在CLANYA_PC类中实现的。下面是它的实现代码。
void control_central(CLANYA_PC* &eg){
while(1){
eg->str_recv.Empty();
memset(eg->buf,0x00,sizeof(eg->buf));
int ret=recv(sClient,eg->buf,64,0);
if(ret<=0){
lianjie_cancel=true;
::closesocket(sClient);
return;
}//接收信息并放到字符串类str_recv中
for(int i=0;i<64;i++){
if(eg->buf[i]!=NULL){
eg->str_recv.AppendChar(eg->buf[i]);
}
}
if(eg->str_recv==L"mouse_up"){
mouse_up();
}
if(eg->str_recv==L"mouse_down"){
mouse_down();
}
if(eg->str_recv==L"mouse_left"){
mouse_left();
}
if(eg->str_recv==L"mouse_right"){
mouse_right();
}
if(eg->str_recv==L"mouse_left_kick_down"){
mouse_left_kick_down();
}
if(eg->str_recv==L"mouse_left_kick_up"){
mouse_left_kick_up();
}
if(eg->str_recv==L"mouse_right_kick_down"){
mouse_right_kick_down();
}
if(eg->str_recv==L"mouse_right_kick_up"){
mouse_right_kick_up();
}
if(eg->str_recv==L"mobile_video"){
mobile_video(eg);
}//如果收到的信息都与上面的信息不一样就返回
}
}
control_central函数是在线程中执行的,主要功能为接收手机端软件发送过来的控制信息,然后根据控制信息转入鼠标控制模块或者照片接收保存模块。
6.3.5鼠标控制模块
鼠标控制模块功能也是放在CLANYA_PC类中实现的。下面是它的实现代码。
//鼠标向上移动一个像素
void mouse_up(void){
POINT lpPoint;
::GetCursorPos(&lpPoint);
::SetCursorPos(lpPoint.x,lpPoint.y-1);
return;
}
//鼠标向下移动一个像素
void mouse_down(void){
POINT lpPoint;
::GetCursorPos(&lpPoint);
::SetCursorPos(lpPoint.x,lpPoint.y+1);
return;
}
//鼠标向左移动一个像素
void mouse_left(void){
POINT lpPoint;
::GetCursorPos(&lpPoint);
::SetCursorPos(lpPoint.x-1,lpPoint.y);
return;
}
//鼠标向右移动一个像素
void mouse_right(void){
POINT lpPoint;
::GetCursorPos(&lpPoint);
::SetCursorPos(lpPoint.x+1,lpPoint.y);
return;
}
//鼠标左键按下
void mouse_left_kick_down(void){
::mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
return;
}
//鼠标左键弹起
void mouse_left_kick_up(void){
::mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
return;
}
//鼠标右键按下
void mouse_right_kick_down(void){
::mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);
return;
}
//鼠标右键弹起
void mouse_right_kick_up(void){
::mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);
return;
}
鼠标控制模块功能主要是根据系统调度模块所接收的鼠标控制信息,作一些鼠标的具体动作,在代码中已经注释。
6.3.6照片接收保存模块
照片接收放在CLANYA_PC类中实现的。下面是它的实现代码。
void mobile_video(CLANYA_PC* &eg){
int j=0;
char buffer[1024]; //用于接收图片大小及图片
char temp[1024]; //用于保存图片大小
memset(buffer,0,sizeof(buffer));//清空
memset(temp,0,sizeof(temp)); //清空
int rcv = recv(sClient,buffer, 1024, 0);
if (rcv <= 0){
//发生错误关闭套接字
::closesocket(sClient);
return ;
}
////保存图片大小
for (int i=0; i<1024; i++){
if(buffer[i]!=NULL){
temp[j] = buffer[i];
j++;
}
}
temp[j]='/0';
eg->lFileSize = atol(temp);//文件大小转换为整数
eg->pBuffer=new char[eg->lFileSize+1];
eg->pBuffer[0]='/0';
long iTemp = 0;
//接收图片
while(1){
rcv = recv(sClient,buffer, 1024, 0);
if (rcv <= 0){
//发生错误关闭套接字
::closesocket(sClient);
return ;
}
for (int i=0; i<=rcv; i++){
eg->pBuffer[i+iTemp] = buffer[i];
}
iTemp+=rcv;
//如果图片接收完成就跳出循环
if(iTemp==eg->lFileSize){
eg->picture_ok=true;
break;
}
}
return;
}
照片显示功能放在类CPage1中,下面是实现代码。
void CPage1::ShowPicture(char* buf,int iSize){
RECT rect; //保存显示区域大小
this->GetDlgItem(IDC_STATIC)->GetWindowRect(&rect);
CDC* pDC=this->GetDlgItem(IDC_STATIC)->GetDC();//获得DC
//分配内存
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, iSize); if(hGlobal == NULL){
::AfxMessageBox(L"内存不足");
}
void* pData = GlobalLock(hGlobal); //锁定内存
memcpy(pData,buf, iSize); //照片放入内存
GlobalUnlock(hGlobal);
CImage pic; //定义CImage对象
IStream* pStream = NULL;
//在指定区域绘图
if(CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) == S_OK){
pic.Load(pStream);
pic.Draw((HDC)pDC->m_hDC,
0,
0,
rect.right-rect.left,
rect.bottom-rect.top);
}
//释放资源
FreeResource(hGlobal); // 释放内存
pStream->Release();//释放流
}
在图片显示过程使用了类Cimage,这个类由系统提供。
照片保存功能放在类CLANYA_FWQDlg中,下面是实现代码。
//保存图片
void CLANYA_FWQDlg::OnBnClickedButton1(){
if(this->lanya_pc->lFileSize!=0){
//分配内存
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,
this->lanya_pc->lFileSize);
if(hGlobal == NULL){
::AfxMessageBox(L"内存不足");
}
//获得系统时间并使之作为文件名
CTime timeTime(CTime::GetCurrentTime());
file_name_str = timeTime.Format(L"%y%b%d%H%M%S");
file_name_str=L"tupian//"+file_name_str+L".jpg";
//将文件按大小放入内存中
void* pData = GlobalLock(hGlobal);
memcpy(pData,this->lanya_pc->pBuffer,
this->lanya_pc->lFileSize);
//解锁内存
GlobalUnlock(hGlobal);
CImage pic;
IStream* pStream = NULL;
if(CreateStreamOnHGlobal(hGlobal,TRUE,&pStream)==S_OK){
pic.Load(pStream);
pic.Save(file_name_str);
}
//释放内存
FreeResource(hGlobal);
this->msg_speek=L"图片已经保存";
::AfxBeginThread(SPEEK_MSG,this);
pStream->Release();
this->lanya_pc->lFileSize=0;
}
else{
this->msg_speek=L"没有收到图片";
::AfxBeginThread(SPEEK_MSG,this);
}
}
在照片保存中用系统时间作为照片名称,这主要是防止命名重复。图片保存在软件文件夹“tupian”中。