Kriss Liu

击长空、博千里,笑慑鬼魅,坦荡万象。四海皆是可有作为,宇内必有余之归宿。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

  多样、互动的WinForm UI设计与开发思路一文中,我提到过把Flash作为控件嵌入到WinForm程序中以提高软件的互动效果并降低开发难度这样一种思路。但这样一个系统,我们往往不希望随应用程序打包,或者让用户看到,很多很多的FLASH文件。其中一个办法就是把flash文件作为资源嵌入到系统中。但这样又出现一个问题,Flash Player只有一个Movie属性用于指定当前播放的媒体,而这个字符串属性只能是本地文件或者一个URL。我们可以在运行时把资源里的flash释放到一个临时目录,但这样一来失去了打包的意义,二来无法保护flash文件。

  我们希望最好的解决方案,就是Flash Player提供类似于DataSet.ReadXml()这样的方法,既可以从指定名字的Xml文件读取数据,又可以从一个数据流(Stream)读数据。但是找遍了Flash Player的所有参数,似乎只有MovieData这个属性有点接近,如果我们能直接把Flash文件的内容赋值给它就好了。不过目前我还没有找到关于这个属性的资料 -_-

  另外一个思路,就是在我们的程序运行的时候,动态建立一个自己的http服务器,可以直接从资源中读取内容。事实上,对于大部分要求文件或url形式的内容,都可以通过建立虚拟http服务器来模拟。但实现起来要费点功夫,而且http会在本机留下缓存,安全性不高。

  最后在whoo从内存播放Flash一文找到了另外一种方法,就是通过命名管道(Named Pipe)。
首先我们看看命名管道为什么能够解决这个问题。这要从它的运行机制讲起。当一个命名管道被建立起来后,它的服务端和客户端之间的数据交流,就完全是通过和操作文件一样的方式来进行,即CreateFile(),ReadFile()和WriteFile()三个函数。也就是说客户端可以像读取文件一样,来读取管道里的数据。

  我们通过调用Win32 API 既可创建管道。这里先简单讲一下服务器端的处理流程:
  1、通过CreateNamedPipe()函数创建一个命名管道,如果成功,会返回这个管道实例的句柄。
  2、管道创建成功后,调用ConnectNamedPipe()函数等到客户端连接。
  3、如果有客户端连接进来,可以通过对文件的操作来交换数据,例如ReadFile()和WriteFile()。
  在.NET里我们不必用这些Win32 API函数来读写文件,直接用System.IO命名空间下的类即可(将文件读写的内存指针指向管道句柄即可)。

  注:目前该方法只针对在Flash Player播放器打开 \\.\pipe\testPipe 才有效。如果您使用的是Flash Player 7.0,那么一定要用 \\?\pipe\testPipe才行。
  至于在html页面或WinForm里的Flash Player控件,指定其Movie属性为"\\.\pipe\testPipe",目前尚无效。

  以下是简单的例子:

using System;
using System.IO;

namespace PipeServer {
    
class Entry {
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>

        [STAThread]
        
static void Main(string[] args) {
            CreatePipe();
        }


        
private const int BUFFER_SIZE = 1024;
        
private const int PIPE_TIMEOUT = 1000;
        
private const string PIPE_NAME = @"\\.\pipe\testPipe";

        
public static void CreatePipe(){
            
            API.SECURITY_ATTRIBUTES sec 
= new API.SECURITY_ATTRIBUTES();
            
int pipeHandle = API.CreateNamedPipe( PIPE_NAME, API.PIPE_ACCESS_DUPLEX
                , API.PIPE_WAIT, API.PIPE_UNLIMITED_INSTANCES, BUFFER_SIZE, BUFFER_SIZE
                , PIPE_TIMEOUT, 
ref sec);
            Console.WriteLine(pipeHandle);

            
if ( pipeHandle == API.INVALID_HANDLE_VALUE ){
                Console.WriteLine(
"Cannot create pipe.");
                
return;
            }
else{
                Console.WriteLine(
"Create pipe successful. Waiting for connection");
            }


            API.OVERLAPPED lapped 
= new API.OVERLAPPED();
            
int connected = API.ConnectNamedPipe( pipeHandle, ref lapped );

            
if ( connected > 0 ){
                
// 如果从资源文件读入swf文件,用下面这句
                
// using ( Stream swf = typeof(API).Assembly.GetManifestResourceStream("PipeServer.test.swf")) {
                using ( Stream swf = File.Open(@"test.swf", FileMode.Open)) {
                    
using (FileStream pipe = new FileStream( new IntPtr(pipeHandle), FileAccess.Write)) {
                        
int reads;
                        
byte[] buffer = new byte[BUFFER_SIZE];
                        
while ( (reads = swf.Read(buffer, 0, BUFFER_SIZE) ) > 0 ) {
                            pipe.Write(buffer, 
0, reads);
                        }

                        pipe.Flush();
                    }

                }

                
                Console.WriteLine(
"Someone arrived.");
                API.DisconnectNamedPipe(pipeHandle);
                API.CloseHandle( pipeHandle );
            }
else{
                API.CloseHandle( pipeHandle );
            }

        }

    }

}

using System;
using System.Runtime.InteropServices;

namespace PipeServer {

    
public class API {

        
public const int INVALID_HANDLE_VALUE = -1;
        
public const int PIPE_ACCESS_INBOUND = 0x1;
        
public const int PIPE_ACCESS_OUTBOUND = 0x2;
        
public const int PIPE_ACCESS_DUPLEX = 0x3;
        
public const int PIPE_WAIT = 0x0;
        
public const int PIPE_UNLIMITED_INSTANCES = 255;

        [DllImport(
"kernel32.dll", EntryPoint="CreateNamedPipe")]
        
public static extern int CreateNamedPipe (
            
string lpName,
            
int dwOpenMode,
            
int dwPipeMode,
            
int nMaxInstances,
            
int nOutBufferSize,
            
int nInBufferSize,
            
int nDefaultTimeOut,
            
ref SECURITY_ATTRIBUTES lpSecurityAttributes
        );

        [StructLayout(LayoutKind.Sequential)]
            
public struct SECURITY_ATTRIBUTES {
            
public int nLength;
            
public int lpSecurityDescriptor;
            
public int bInheritHandle;
        }


        [DllImport(
"kernel32.dll", EntryPoint="ConnectNamedPipe")]
        
public static extern int ConnectNamedPipe (
            
int hNamedPipe,
            
ref OVERLAPPED lpOverlapped
        );

        [StructLayout(LayoutKind.Sequential)]
            
public struct OVERLAPPED {
            
public int Internal;
            
public int InternalHigh;
            
public int offset;
            
public int OffsetHigh;
            
public int hEvent;
        }


        [DllImport(
"kernel32.dll", EntryPoint="CloseHandle")]
        
public static extern int CloseHandle (
            
int hObject
        );

        [DllImport(
"kernel32.dll", EntryPoint="DisconnectNamedPipe")]
        
public static extern int DisconnectNamedPipe (
            
int hNamedPipe
        );
    }

}
posted on 2005-03-14 23:43  Kriss Liu  阅读(3178)  评论(9编辑  收藏  举报