2012년 8월 28일 화요일

FluorineFX의 RTMP (Video Recording)이 정상적으로 작동하지 않는 경우

  • 환경 : FluorineFX, Flash Builder 4.5, IIS 7.5
  • 개요 : FluorineFX을 이용하여 Video Recording이 가능한 모듈을 개발하였으나 
     NetConnection.Connect.Failed 오류가 발생함.
  • 증상
    1. FluorineFX에서 제공되는 Sample(설치폴더\Samples\Flex\Rtm\VideoChat) 코드를 Visual Studio 2010에서 ASP.NET Development Server를 통해서 실행하면- 즉 Debug을 실행하면 - 정상적으로 FluorineFX의 RTMP에 접속됨.
    2. 위의 코드를 기반으로 해서 만들어진 Video Recoring 모듈을 IIS 7.5 포팅을
      하고 기동을 하면 NetConnection.Connect.Failed 오류가 발생함.

  • 원인 분석
    1. IIS 7.5의 응용프로그램 풀에서 유휴시간 때문인 것으로 분석이 됨.
      (정확한 분석은 아니다. 이렇게 처리를 하니까 문제가 발생하지 않아서 미루어 짐작하는 것이다. IIS7.0 에서는 문제가 없었다)
      유휴 시간으로 설정된 시간이 초과되면 관련 프로세스가 중지되는 것으로 보인다.
  • 처리 방안
    1. "응용프로그램 풀 >> 프로세스 모델 >> 유휴시간 제한"을 0으로 설정하여 제약을 두지 않는다.
  • RTMP 관련 참고 사항
    1. rtmp://localhost/VideoRecording => 웹 루트/apps/VideoRecording 폴더와 맵핑되고
      웹 루트/apps/VideoRecording/streams 폴더 밑에 동영상이 저장된다.
    2. VideoRecording이 사용되는 서버 모듈에 대한 정의는 웹 루트/apps/app.config에 정의되어 있다.

      아래는 video recording과 관련된 app.config이다.

      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <!-- Application object. Specify a fully qualified type name for your handler -->
        <application-handler type="RIALab.Framework.Streaming.VideoRecordingApplication"/>
        <!--streamFilenameGenerator type="RIALab.Framework.Streaming.FileNameGen"/-->
      </configuration>
    3. WEB-INF/flex/services-config.xml 을 flex에 지정할 필요가 없다.
      RTMP 통신은 rtmp://localhost/VideoRecording 와 통신을 직접하기 때문이다.
    4. Web.Config에 (.Net 4.0 기준) RTMP - fluorineFx 관련 설정
      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
        <configSections>
          <sectionGroup name="fluorinefx">
            <section name="settings" type="FluorineFx.Configuration.XmlConfigurator, FluorineFx" requirePermission="false" />
          </sectionGroup>
          <!--log4net setup : start-->
          <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
          <!--log4net setup : end-->
          <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          <section name="securityCryptographyConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Configuration.CryptographySettings, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        </configSections>
        <!--log4net setup : start-->
        <log4net>
          <root>
            <level value="ALL" />
            <appender-ref ref="RollingFileAppender" />
          </root>
          <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
            <param name="file" value="./log/pasteleditor" />
            <appendToFile value="true" />
            <rollingStyle value="Date" />
            <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
            <datePattern value="_yyyyMMdd&quot;.log&quot;" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%message%newline" />
            </layout>
          </appender>
        </log4net>
        <!--log4net setup : end-->
        <appSettings>
          <add key="BACKGROUND_IMG_VPATH" value="/uploadData/backgroundImage" />
          <!--배경이미지 가상경로-->
          <add key="BACKGROUND_MUSIC_VPATH" value="/uploadData/music" />
          <!--음악파일경로(mp3)-->
          <add key="UPLOAD_IMAGE_VPATH" value="/uploadData/image/" />
          <add key="UPLOAD_CLIPART_VPATH" value="/uploadData/clipart_thumb/" />
          <add key="UPLOAD_DOCUMENT_VPATH" value="/uploadData/document/" />
          <add key="UPLOAD_MOVIE_VPATH" value="/uploadData/movie/" />
          <add key="UPLOAD_FRAME_VPATH" value="/uploadData/frame/" />
          <add key="UPLOAD_DRAW_VPATH" value="/uploadData/draw/" />
          <add key="UPLOAD_SHAPE_VPATH" value="/uploadData/shape/" />
          <add key="UPLOAD_CHART_VPATH" value="/uploadData/chart/" />
          <add key="UPLOAD_TEXT_VPATH" value="/uploadData/text/" />
          <add key="UPLOAD_LINK_MOVIE_VPATH" value="/uploadData/linkmovie/" />
          <add key="UPLOAD_LINK_VIEWER_VPATH" value="/uploadData/viewer/" />
          <add key="UPLOAD_BACK_THUMB_VPATH" value="/uploadData/backgroundImage_thumb/" />
          <add key="UPLOAD_VOICE_VPATH" value="/uploadData/voice/" />
          <add key="UPLOAD_EQUATION_VPATH" value="/uploadData/equation/" />
          <add key="UPLOAD_DOCUIMG_VPATH" value="/uploadData/docuimage/" />
          <add key="UPLOAD_TEMPLATE_VPATH" value="/uploadData/template/" />
          <add key="CMD_ENCODER_VPATH" value="/encoderData/" />
          <add key="CMD_TEX_VPATH" value="/texData/" />
          <add key="CMD_FLEX_VPATH" value="/flexData/" />
          <add key="CMD_SWF_VPATH" value="/swfData/" />
          <add key="TEMPDATA_VPATH" value="/tempData/" />
          <add key="META_XML_VPATH" value="/mxmlData/metaXML/" />
          <add key="USER_MXML_VPATH" value="/mxmlData/userMXML/" />
          <add key="COMPILED_MXML_VPATH" value="/mxmlData/compiledMXML/" />
          <add key="SMALLSWF_VPATH" value="/mxmlData/smallSWF/" />
          <add key="MAKEIMAGE_VPATH" value="/mxmlData/makeimage/" />
          <add key="MXML_THUMBLAIL_VPATH" value="/mxmlData/thumbnail/" />
          <add key="MXML_BACKGROUNDIMAGE_VPATH" value="/mxmlData/backgroundimage/" />
          <add key="MXML_TEMPLATEDATA_VPATH" value="/mxmlData/templateData/" />
          <add key="VOICEDATA_STREAMS_VPATH" value="/voicedata/streams/" />
          <add key="STREAMDATA_VPATH" value="/streamData/vod/media/" />
          <add key="BACKUPDATA_VPATH" value="/backup/" />
          <add key="HOST_NAME" value="localhost" />
          <add key="UPLOAD_DATA_VPATH" value="/uploadData/" />
          <add key="ADMIN_DATA_VPATH" value="/pastelData/adminData/" />
          <!--단위 : MB-->
          <add key="IMAGE_MAX_SIZE" value="1"/>
          <add key="MOVIE_MAX_SIZE" value="300"/>
          <add key="DOCUMENT_MAX_SIZE" value="100"/>
          <add key="ETC_MAX_SIZE" value="1"/>
          <add key="MP3_MAX_SIZE" value="10"/>
          <!--add key="OPENOFFICE_PATH" value="D:\temp\OpenOfficePortable\OpenOfficePortable.exe"/-->
          <add key="EBS_THUMBLAIL_VPATH" value="/thumbnailData" />
          <add key="OPENOFFICE_PAGE_LIMIT" value="true" />
          <add key="OPENOFFICE_PAGE_LIMIT_COUNT" value="10" />
        </appSettings>
        <fluorinefx>
          <settings>
            <!-- <application-handler>FluorineFx.Messaging.Adapter.ApplicationAdapter</application-handler> -->
            <rtmpServer>
              <threadpool minWorkerThreads="0" maxWorkerThreads="25" idleTimeout="60000" />
              <!-- Ping clients every "pingInterval" ms. Set to 0 to disable ghost detection code. -->
              <!-- Disconnect client after "maxInactivity" ms of not responding. -->
              <!-- Max. time in milliseconds to wait for a valid handshake. -->
              <rtmpConnection pingInterval="0" maxInactivity="60000" maxHandshakeTimeout="0" />
              <rtmptConnection pingInterval="5000" maxInactivity="60000" maxHandshakeTimeout="5000" />
              <rtmpTransport receiveBufferSize="4096" sendBufferSize="4096" tcpNoDelay="true" />
            </rtmpServer>
          </settings>
        </fluorinefx>
        <connectionStrings>
          <!--add name="KMDB" connectionString="provider=MSDAORA;Data Source=xxxxxxx;User ID=xxxxx;Password=xxxxxx;" providerName="System.Data.OleDb"/-->
          <add name="PastelEditorConnectionString" connectionString="data source=C:\6000.IIS_Data\Kings_2012\dataRoot\xxxxxxxx.db3" providerName="System.Data.SQLite" />
          <!--add name="PastelEditorConnectionString" connectionString="data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=xxx.xxx.xxx.xxx)(PORT=1545))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=DBPEDRB)));user id=xxxxxx;password=xxxxxxx;pooling=false" providerName="System.Data.OracleClient" /-->
          <!--add name="PastelEditorEntities"          connectionString="metadata=res://*/DataSet.Model1.csdl|res://*/DataSet.Model1.ssdl|res://*/DataSet.Model1.msl;provider=System.Data.SQLite;provider connection string='data source=&quot;F:\2000.Project Data\2300.Year_Section\FY2010\201008.xxxxx\02.개발\04.Database\xxxxx.db3&quot;'" providerName="System.Data.EntityClient"/-->
        </connectionStrings>
        <securityCryptographyConfiguration>
          <hashProviders>
            <add algorithmType="System.Security.Cryptography.MD5CryptoServiceProvider, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" saltEnabled="false" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_MD5" />
            <add algorithmType="System.Security.Cryptography.SHA1Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" saltEnabled="false" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_SHA1" />
            <add algorithmType="System.Security.Cryptography.SHA256Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" saltEnabled="false" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_SHA256" />
            <add algorithmType="System.Security.Cryptography.SHA384Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" saltEnabled="false" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_SHA384" />
            <add algorithmType="System.Security.Cryptography.SHA512Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" saltEnabled="false" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_SHA512" />
          </hashProviders>
          <symmetricCryptoProviders>
            <add algorithmType="System.Security.Cryptography.RijndaelManaged, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" protectedKeyFilename="C:\ubion\EncKey.key" protectedKeyProtectionScope="LocalMachine" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.SymmetricAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="CL_Rijndael" />
          </symmetricCryptoProviders>
        </securityCryptographyConfiguration>
        <system.web>
          <!--
                  컴파일된 페이지에 디버깅 기호를 삽입하려면
                  compilation debug="true"로 설정하십시오. 이렇게 하면
                  성능에 영향을 주므로 개발하는 동안에만 이 값을
                  true로 설정하십시오.
              -->
          <compilation debug="true" targetFramework="4.0">
          </compilation>
          <!--
                  <authentication> 섹션에서는 ASP.NET에서 사용되는
                  보안 인증 모드의 구성을 설정하여 들어오는
                  사용자를 식별할 수 있습니다.
              -->
          <authentication mode="Windows" />
          <!--
                  <customErrors> 섹션에서는 요청을 실행하는 동안
                  처리되지 않은 오류가 발생하는 경우 수행할 작업을
                  구성할 수 있습니다. 특히 이 섹션에서는
                  개발자가 오류 스택 추적 대신 html 오류 페이지가 표시되도록
                  구성할 수 있습니다.
    5.         <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
                  <error statusCode="403" redirect="NoAccess.htm"/>
                  <error statusCode="404" redirect="FileNotFound.htm"/>
              </customErrors>
              -->
          <httpRuntime executionTimeout="90" maxRequestLength="2097000" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100" />
          <httpModules>
            <add name="FluorineGateway" type="FluorineFx.FluorineGateway, FluorineFx" />
          </httpModules>
          <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
        </system.web>
        <!--
              IIS(Internet Information Services) 7.0에서 ASP.NET AJAX를 실행하려면
              system.webServer 섹션이 필요합니다. 이전 버전의 IIS에서는 필요하지 않습니다.
          -->
        <system.webServer>
          <security>
            <requestFiltering>
              <requestLimits maxAllowedContentLength="2000000000" />
            </requestFiltering>
          </security>
          <modules>
            <add name="FluorineGateway" type="FluorineFx.FluorineGateway, FluorineFx" preCondition="managedHandler" />
          </modules>
          <handlers>
            <add name="*.vbhtml_*" path="*.vbhtml" verb="*" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.vbhtm_*" path="*.vbhtm" verb="*" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.cshtml_*" path="*.cshtml" verb="*" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.cshtm_*" path="*.cshtm" verb="*" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.aspq_*" path="*.aspq" verb="*" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.xamlx_*" path="*.xamlx" verb="*" type="System.Xaml.Hosting.XamlHttpHandlerFactory, System.Xaml.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.xoml_*" path="*.xoml" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.svc_*" path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.soap_*" path="*.soap" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.rem_*" path="*.rem" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*.asmx_*" path="*.asmx" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="ScriptResource.axd_GET,HEAD" path="ScriptResource.axd" verb="GET,HEAD" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="*_AppService.axd_*" path="*_AppService.axd" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv2.0" />
            <add name="eurl.axd_*" path="eurl.axd" verb="*" type="System.Web.HttpNotFoundHandler" preCondition="integratedMode,runtimeVersionv2.0" />
          </handlers>
          <validation validateIntegratedModeConfiguration="false" />
        </system.webServer>
        <startup>
          <supportedRuntime version="v2.0.50727" />
        </startup>
      </configuration>

    2012년 8월 21일 화요일

    iTextSharp에서 한글-CJK 사용하기

    iTextSharp을 이용해서 한글을 삽입하는 방법

    1. dll로 감싸여져 있는 iTextSharp에서 제공하는 폰트를 이용하는 방법

      Embedding 방식이 아닌 것으로 추정되며 생성된 PDF의 파일 크기가 작은 특징을 가지고 있다.

      iTextSharp에서 제공하는 Font 이름은
      HYGoThic-Medium
      HYSMyeongJo-Medium
      HYSMyeongJoStd-Medium
      이다.

      글자의 쓰기 방향을 결정하는 CMap은
      UniKS-UCS2-H
      UniKS-UCS2-V
      이 있다.
      (H로 끝나는 것은 가로 쓰기, V는 세로쓰기)

      (사용방식)
      BaseFont.AddToResourceSearch("iTextAsian.dll");
      BaseFont bf = BaseFont.CreateFont("HYSMyeongJoStd-Medium", "UniKS-UCS2-H", BaseFont.NOT_EMBEDDED);
      Font font = new Font(bf, 12, Font.BOLD|Font.UNDERLINE, BaseColor.RED);
      위와 같이 사용을 하면 된다. 그리고 iTextAsian.dll 파일은 debug 폴더(또는 만들어진 프로그램이 설치되는 폴더)에 위치하면 된다.
    2. PC에 존재하는 폰트를 불러 들이는 방법
      Embedding 방식으로 추정이 된다. 동일한 내용에 대해서 1번에 의해 생성된 파일보다  4배 정도의 사이즈 차가 발생한다.

      (사용방식)
      BaseFont bf = BaseFont.CreateFont("malgun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);OrBaseFont bf = BaseFont.CreateFont("HMFMMUEX.TTC,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
      Font font = new Font(bf, 12, Font.BOLD|Font.UNDERLINE, BaseColor.RED);
      의 방식으로 사용한다.
      PC에 존재하는 폰트를 사용하기 위해서는 다음의 폰트 유형 중에 하나이어야 한다.

      * Type1 font files (.afm, .pfm, .pfb)로서 잘 알지는 못하지만 Adobe에서 만들어진 폰트로 알고 있다.
      * TrueType font files - .ttf (Adobe의 Type1 폰트와 경쟁하기 위해서 Apple에서 개발된 폰트)
      * OpenType font files - .otf, .ttf, .tttc (Unicode에 근간한 cross-platform 폰트 파일 포맷)

      ttf 형식의 폰트는 폰트명을 "malgun.ttf"로 지정하지만 ttc 형식의 폰트는 폰트명을"HMFMMUEX.TTC,0"와 같이 ",0"이 추가된다. 이것은 ttc는 ttf의 collection을 위해서 사용되는 것이기 때문에 ttf 중에서 첫번째 ttf를 사용하도록 하기 위한 것이다.

      폰트명을 지정할 때, 해당 폰트가 debug 폴더(또는 만들어진 프로그램이 설치되는 폴더)에 위치하는 경우에는 경로없이 폰트 파일 이름을 기재하면 되지만 직접 경로를 기재해도 된다. 예를 들어서, C:/windows/fonts/malgun.ttf와 같이 기재할 수 있다.

      여기서 주의할 것은 한글이 제대로 표시되려면 한글전용폰트를 사용해야 한다는 것이다. courier, arial과 같은 폰트도 윈도우에서는 지원되지만 iTextSharp에서는 해당 폰트를 사용하면 한글이 표시 되지 않는다. (이 부분은 내가 잘못 알고 있는 부분일지도 모른다.)adobe의 폰트인 afm 파일도 마찬가지로 Courier.afm등의 파일을 iTextSharp을 통해서
      불러들이면 한글이 표시되지 않는 현상이 발생한다.


      (대략적인 사용방법)
      using iTextSharp.text;
      using iTextSharp.text.pdf;

      ......

      //iTextSharp의 폰트를 사용할 경우
      BaseFont.AddToResourceSearch("iTextAsian.dll");
      BaseFont bf = BaseFont.CreateFont("HYSMyeongJoStd-Medium", "UniKS-UCS2-H", BaseFont.NOT_EMBEDDED);

      //폰트 파일을 사용하는 경우 - ttf
      BaseFont bf = BaseFont.CreateFont("malgun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
      //폰트 파일을 사용하는 경우 - ttc
      BaseFont bf = BaseFont.CreateFont("HMFMMUEX.TTC,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

      Font font = new Font(bf, 12, Font.BOLD|Font.UNDERLINE, BaseColor.RED);
                       
      writer.CompressionLevel = 0;
      UTF8Encoding utf8 = new UTF8Encoding();
      Byte[] utf8String = utf8.GetBytes("Hello World 안녕하세요");
      Phrase hello = new Phrase();
      Chunk chunk = new Chunk(utf8.GetString(utf8String), font);

      hello.Add(chunk);
      PdfContentByte canvas = writer.DirectContentUnder;
      ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, hello, 100, 100, 0);

      위의 코드에서 PDFWrite와 Document에 대한 참조는 생략되었으므로 주의하라.

    2012년 8월 16일 목요일

    Visual Studio 2010 - Windows Service 구성 및 배포 파일 구성

    Windows Service를 구성하고 배포 파일을 만들 때 마다 매번 시행착오를 거치는 관계로 간단하게 내용을 정리한다.

    1. Windows Service 구성 방식은 아래의 URL을 참고하여 구성을 한다.

    http://msdn.microsoft.com/ko-kr/library/zt39148a.aspx

    해당 Service에 들어가는 코드는 기존에 만들어진 서비스를 참고해서 코드를 추가한다.
    위의 URL에서 참고하고자 하는 내용은 ProjectInstaller, ServiceInstaller1, ServiceProcessInstaller1의 구성 방법에 대한 내용 들이다.

    2. 1을 통해서 Windows Service가 구성이 되면 이를 배포하는 프로젝트를 만들어서 Setup.exe를 실행하면 해당 Windows Service가 Local System에 설치가 되고 Service에 자동으로 등록이 되도록 하는 방법은 다음과 같다.

    기존에 만들어진 배포프로젝트 대로 구성을 하면 되는데 항상 아래의 내용을 빼 먹는 관계로
    Service에 만들어진 서비스 프로그램이 등록되지 않았다.



    위의 그림에서 배포프로젝트를 우측 마우스로 클릭해서 "View->Custom Action"을 선택한다.
    선택해서 아래의 그림이 표시되면 "Install", "Commit", "Rollback", "Uninstall"에 1에서 구성한 Window Service 프로그램에 대한 Primary Output을 지정하여야만 설치시에 자동으로 만들어진 Service가 Service 목록에 등록이 된다.



    MS Office Interop를 이용해서 개발한 Windows Service가 정상적으로 작동하지 않을 때..

    • 환경 : Microsoft Office 2010, Visual Studio 2010, Windows 7, Windows 2008 Server
    • 개요 : MS Office 2010 Interop (14version)을 이용해서 Powerpoint, Word, Excel 파일을
      PDF로 변환하는 Windows Service Program을 개발하여 포팅하였으나 Program이 정상적으로 구동되지 않았다.
    • 증상
      1. 변환 프로그램을 개발하는 Visual Studio 2010에서 Test Program인 Console Application으로 작동을 시켜보면 정상적으로 Program이 기동되었다.
      2. 1의 테스트를 확인하고 Windows Service로 배포하는 프로젝트를 생성하여 정상적으로 Windows Service에등록이 된 것을 확인하고, 구동을 시켜보니 정상적으로 작동하지 않았다.
      3. Program에서 작동하는 로그을 확인해 보니,
        "개체 참조가 개체의 인스턴스로 설정되지 않았습니다."라는 메세지와
        "'C:\temp\document\7.xlsx' 파일을 사용할 수 없습니다. 원인은 다음과 같습니다
        해당 파일 이름이나 경로가 없습니다. 다른 프로그램에서 파일을 사용하고 있습니다.저장하려고 하는 통합 문서 이름이 현재 열려 있는 통합 문서의 이름과 같습니다."라는 메세지가 표시되었다.
      4. Excel -> PDF, PPT -> PDF, Word -> PDF 모듈이 작동하지 않았다.
    • 원인 분석
      1. Windows 7, Vista, Windows Server 2008에서 Windows Service는 Session0라고 불리는 session (or something)에서 구동이 된다. Session0는 Desktop에서 구동되는 프로그램에 접근이 불가능한 영역(Desktop-less wasteland)인데 개발된 Windows Service는 Desktop Session에서 구동되는(Desktop-ed session) Office Program인 관계로 Windows Service가 정상적으로 작동되지 않았던 것이다.
        (참고 URL : http://stackoverflow.com/questions/241190/vista-office-interop-not-working)
    • 처리 방안
      1. 첫 번째 처리 내용 : System Profile을 위한 desktop folder를 생성한다.
        이 방법은 msdn에서 확인한 방법으로서 H.Ogawa가 제시한 방법이다
        http://social.msdn.microsoft.com/Forums/en/innovateonoffice/thread/b81a3c4e-62db-488b-af06-44421818ef91

        아래의 경로에서 볼 수 있듯이 systemprofile이라는 폴더에 Desktop이라는 폴더를 만들고 해당 폴더 권한에 Everyone을 추가하고 모든 권한을 주면 된다.

        c:\Windows\SysWOW64\config\systemprofile\Desktop, or
        c:\Windows\System32\config\systemprofile\Desktop

        개발된 Windows Service 프로그램이 64bit로 빌드하였으면 SysWOW64에 Desktop을 등록하고 그렇지 않으면 System32에 등록하면 된다.

        => 처리 후 테스트 결과 : Excel, Word는 모두 정상적으로 작동을 하였으나 Powerpoint는 여전히 동작하지 않았다. 그래서 아래의 처리 방식을 추가로 처리하였다.
      2. 두 번째 처리 내용 : 1의 방법에도 여전히 Powerpoint가 구동되지 않았다.
        Powerpoint가 구동되기 위해서는 User Profile 정보가 필요한데 Windows Service로 구동을 하다 보니 충분한 User Profile 정보를 가져올 수 없어서 에러가 발생했던 것이다. (참고 URL : http://stackoverflow.com/questions/729609/powerpoint-interop-fails-in-a-windows-service-but-works-fine-in-a-windows-form-a)

        이에 대한 처리를 위해서, Windows Service에 등록되어 있는 Program의 속성 창에서 "로그온(Logon)" Tab을 클릭하여 "서비스와 데스크톱 상호 작용 허용"을 선택하면 Powerpoint도 이상 없이 구동된다.

      3. 추가 사항 : 1과 2의 방식을 통해서 Windows Service가 정상적으로 구동이 되었더니 다음과 같은 화면이 자꾸 표시되는 현상이 발생을 하였다.



        Windows Service가 구동되는 Session0에서 Word, Powerpoint등의 프로그램이 메세지를 발생시켜서 위의 화면이 표시되는 것으로 추측이 되었다.

        위의 메세지를 발생시키는 것은 다른 Windows Service에 의한 것으로서 Service 이름은 "Interactive Services Detection"이다. 해당 서비스는 "대화형 서비스에 대한 사용자 입력의 사용자 알림을 사용할 수 있도록 하는 서비스"로서 해당 서비스를 사용하지 않도록 Service 설정을 하면(사용안함으로 설정)된다.



    2012년 8월 14일 화요일

    IIS7.x에서 FluorineFX가 정상적 작동되지 않는 경우

    • 환경 : FluorineFX, Flash Builder 4.5, IIS 7.5
    • 개요 : FluorineFX를 통해서 Server Module을 구성하고 Flash Builder를 통해서 Client Module을 구성하여 IIS 7.5에 Porting을 하면 "BadVersion" 오류가 발생하였다.
    • 증상
      1. FluorineFX에서 제공하는 Sample - FluorineFX v1.0.0.15 installer를 설치하면 생성되는 샘플 코드 (설치폴더\Samples\Flex\Remoting\ArrayCollection) - 을 Visual Studio 2008에서 ASP.NET Development Server를 통해서 실행하면- 즉 Debug을 실행하면 - 정상적으로 구동이 된다.
      2. Visual Studio 2008을 통해서 웹사이트를 게시하여 IIS에서 구동을 하면
        BadVersion 오류가 발생한다
      3. Fiddler를 통해서 Gateway.aspx의 결과 값이 리턴되는 것을 확인해 보면
        - 정상적인 경우 : Content-Type이 "application/x-amf"으로 표시된다.
        - 오류가 있는 경우 : Content-Type이 "text/html"로 표시된다.
    • 원인 분석
      1. 정상적인 경우와 오류가 있는 경우의 Gateway.aspx가 리턴되는 값이 서로 틀린 것을 보고 IIS의 설정 상에 문제가 있는 것으로 짐작을 하게 되었으며 응용프로그램 풀의 설정에서 "관리되는 파이프라인 모드"를 "클래식"으로 바꾸니까 IIS에서도 정상적으로 실행이 되었다.
      2. 포팅된 웹사이트의 응용 프로그램 풀은 "FluorineFX_Test"로 설정을 하였으며
        원인은 FluorineFX_Test의 "관리되는 파이프라인 모드"가 기본적으로 "통합"으로
        설정이 되어 있기 때문이다.
      3. "Managed Pipeline mode"는 "integrated"와 "Classic"으로 설정이 되는데
        IIS 6.0 이전에는 "Classic"으로 관리가 되었다고 한다. Classic으로 관리가 되면
        ASP.NET 페이지는 ISAPI Extension과 ISAPI filters를 통해서 컨트롤이 되는 것으로서 통합 모드를 사용하는 것 보다는 관리상이나 성능상에서 많은 손해를 보게 된다고 한다.
      4. 이전에 만들어진 ASP.Net 프로그램이 IIS 7.x에서 정상적으로 구동이 되지 않는 경우에 "클래식"으로 설정을 하게 되면 정상적으로 작동이 된다. 결과적으로 FluorineFX는 이전에 만들어진 ASP.Net 코드이기 때문에 FluorineFX가 자동으로 생성하는 Web.Config의 내용이 IIS 6.0 이전의 기준에 맞춰져 있기 때문인 것이다.
    • 처리 방안
      1. 첫 번째 방법 : "관리되는 파이프라인 모드"를 "클래식"으로 설정한다.
        => 이렇게 하면 정상적으로 구동이 되기는 하지만 IIS 7.x의 성능을 제대로 활용하지 못하게 된다.
      2. 두 번째 방법 : IIS6 기준의 web.confing를 IIS7 기준으로 migration 한다.
        =>  %systemroot%\system32\inetsrv\APPCMD.EXE migrate config "응용프로그램풀 이름/" (주의할 점 => 응용프로그램풀 이름 뒤에 "/"가 들어가는 것을 주의한다.)
    • web.config의 변화

      migration을 하고 난 이후에 system.webServer/modules 에
      <add name="FluorineGateway" type="FluorineFx.FluorineGateway, FluorineFx" preCondition="managedHandler" /> 이 추가되었다.