基于J2ME蓝牙的手机与计算机通信系统设计6

第六章 系统实施

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,此对象也可以用来服务查询。当执行setbtDeviceRemoteDevice btDevice)函数时,将得到一个远程的蓝牙设备对象,此对象保存在btDevice 中。当执行 srevicesearch_connect() 函数时,discoveryagent会调用searchServices(null,uuid,this.btDevice,this)方法执行蓝牙服务查询(uuid为服务识别标志),此时会执行回调函数servicesDiscoveredserviceSearchCompleted,完成服务查询,然后调用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也支持其他的媒体文件,比如 WMAAVIMPG等)。

         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,&param1, &param2, 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接口。除了SpeakIspVoice接口还有许多成员函数,下面说一下几个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”中。

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值