PDA 가로보기, 세로보기 전환 (Landscape, Portrait)

enumDO에 정의되어 있는 모드를 차례대로 변환 합니다.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

[CODE type=c++]
#include <windows.h>
#include "EzLotator.h"

DWORD getDisplayOrientation(DEVMODE* pDevmode);
void setDisplayOrientation(DEVMODE* pDevmode, DWORD angle);

int WINAPI WinMain(HINSTANCE hInstance,
                  HINSTANCE hPrevInstance,
                  LPTSTR    lpCmdLine,
                  int       nCmdShow)
{
       DEVMODE devmode = {0,};

       DWORD enumDO[] = { DMDO_0, DMDO_90 };

       DWORD currentDO = getDisplayOrientation(&devmode);

       int countEDO = sizeof(enumDO)/sizeof(enumDO[0])-1;

       int i=0;

       for(i=0; i<countEDO; i++)
               if ( currentDO == enumDO[i] )
                       break;

       if ( i<countEDO )
       {
               setDisplayOrientation( &devmode, enumDO[++i] );
       }
       else
       {
               setDisplayOrientation( &devmode, enumDO[0] );
       }


       return 0;
}


DWORD getDisplayOrientation(DEVMODE* pDevmode)
{
       pDevmode->dmSize = sizeof(DEVMODE);
       pDevmode->dmFields = DM_DISPLAYORIENTATION;
       ChangeDisplaySettingsEx(NULL, pDevmode, NULL, CDS_TEST, 0);

       return pDevmode->dmDisplayOrientation;
}

void setDisplayOrientation(DEVMODE* pDevmode, DWORD angle)
{
       getDisplayOrientation( pDevmode );

       pDevmode->dmDisplayOrientation = angle;
       ChangeDisplaySettingsEx(NULL, pDevmode, NULL, CDS_RESET, 0);
}

[/HTML][/CODE]

How To Use CeRapiInvoke

How To Use CeRapiInvoke()
View products that this article applies to.
Article ID : 299891
Last Review : August 30, 2004
Revision : 1.0
This article was previously published under Q299891
On This Page
SUMMARY
MORE INFORMATION
  Using CeRapiInvoke() in Block Mode
  Using CeRapiInvoke in Stream Mode
  Building Modules That Use RAPI
APPLIES TO

SUMMARY
The remote API (RAPI) CeRapiInvoke() enables you to invoke your routines in the Windows CE device from a desktop application. The routines are implemented in a DLL and the DLL must export those routines. You can use CeRapiInvoke() in two ways: in block mode or in stream mode. This article demonstrates how to use CeRapiInvoke() in both ways.
Back to the top

MORE INFORMATION
The following is a prototype for CeRapiInvoke in RAPI.dll to be used by an application running on the desktop.

HRESULT CeRapiInvoke(LPCWSTR pDllPath, LPCWSTR pFunctionName,
DWORD cbInput, BYTE *pInput, DWORD *pcbOutput,
BYTE **ppOutput, IRAPIStream **ppIRAPIStream,
DWORD dwReserved)
                               

CeRapiInvoke() will be called in block mode if IRAPIStream** is NULL. Otherwise it will be called in the stream mode.

The following is a prototype DLL routine that runs on the device: int CallMyFunction(DWORD cbInput, BYTE* pInput,
                  DWORD* pcbOutput, BYTE** ppOutput,
                  IRAPIStream* pStream)
                               
Back to the top

Using CeRapiInvoke() in Block Mode
In the block mode, calls to CeRapiInvoke() return only after the DLL routine has executed and returned. The ppOutput pointer must be allocated by the DLL through the use of LocalAlloc() and the desktop application must call LocalFree() on the same [what?]. In block mode, ppIRAPIStream must be set to NULL. The value returned by the DLL routine will be returned by CeRapiInvoke.

The following sample code demonstrates use of CeRapiInvoke() in the block mode by calling a method in the DLL that resides in the \Windows folder of the device. The DLL method returns the available physical memory in the device in kilobytes. // test console application to call in blocking mode
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "rapi.h"


int main(int argc, char* argv[])
{
       DWORD        cbOut;
       BYTE*        pOut;
       HRESULT        hr;

//Initialize Windows CE RAPI
   hr = CeRapiInit();
//Invoke CallMyFunction routine in MyRapi DLL in the \Windows directory.
   hr = CeRapiInvoke(L"MyRapi", L"CallMyFunction",
        0, NULL, &cbOut, &pOut, NULL, 0);
       if(cbOut)
               printf("Your device got %s KB of Physical Memory available", pOut);
       else
               printf("No memory available in the device");

//Uninitialize Windows CE RAPI
   hr = CeRapiUninit();
//Free the DLL allocated memory.
       if(pOut)
               LocalFree(pOut);
       return 0;
}
                               

The following code shows implementation of the CallMyFunction routine inside MyRapi.dll: #include <windows.h>
#include "rapi.h"
extern "C"
{
       __declspec(dllexport) int CallMyFunction(DWORD cbInput,
               BYTE* pInput, DWORD* pcbOutput, BYTE** ppOutput,
               IRAPIStream* pStream);
}

int CallMyFunction(DWORD cbInput, BYTE* pInput,
                  DWORD* pcbOutput, BYTE** ppOutput,
                  IRAPIStream* pStream)
{
       MEMORYSTATUS structMemStatus;
       DWORD dwMemAvailPhys;
       char        szFree[10];
       //Initialize buffer to all NULLs
       ZeroMemory(szFree, 10);
       GlobalMemoryStatus(&structMemStatus);
       dwMemAvailPhys = structMemStatus.dwAvailPhys;
       sprintf(szFree, "%d", dwMemAvailPhys/1024);
       //Provide extra char for NULL
       *ppOutput = (BYTE*)LocalAlloc(LPTR, strlen(szFree)+1);
       if(*ppOutput)
       {
               //Copy along with NULL
               strncpy((char*)*ppOutput, szFree, strlen(szFree)+1);
               *pcbOutput = strlen(szFree) + 1;
       }
       else
               *pcbOutput = 0;
       return 0;
}
                               
Back to the top

Using CeRapiInvoke in Stream Mode
The following code demonstrates how to use CeRapiInvoke() in stream mode. It communicates with the device through IRAPIStream interface and displays the available physical memory in an Edit control. This simple application will loop indefinitely when reading data from the RAPI stream until a key is pressed. Once a key is pressed, the application exits the loop and writes to the RAPI stream, notifying code in MyRapi.dll that it is exiting. // test console application to call in stream mode
//

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include "rapi.h"

//Serious Error conditions and execution paths are not
//considered in this example. Purpose is just to demonstrate
//the usage of CeRapiInvoke() in Stream mode.


struct MY_MEM_STRUCT
{
       DWORD        dwPhysMemAvail;
       DWORD        dwPhysTotal;
};

int main(int argc, char* argv[])
{
       IRAPIStream *pStream;
       DWORD        cbOut;
       DWORD        dwLength, cbBytesRead;
       MY_MEM_STRUCT        structMyDeviceMem;

       HRESULT        hr = CeRapiInit();
       hr = CeRapiInvoke(L"MyRapi", L"CallMyFunction", NULL,
               NULL, &cbOut, NULL, &pStream, 0);

       _tprintf(_T("Press any key to exit\n"));
       fflush(stdin);
       while(!kbhit())
       {
               cbBytesRead = 0;
               //First read the length information.
               hr = pStream->Read(&dwLength, sizeof(DWORD), &cbBytesRead);
               if(FAILED(hr) || (dwLength != sizeof(MY_MEM_STRUCT)))
               {
                       _tprintf(_T("Error while reading from stream"));
                       break;
               }
               //Read the bytes into the structure.
               hr = pStream->Read(&structMyDeviceMem, dwLength,
                       &cbBytesRead);
               if(FAILED(hr))
               {
                       break;
               }
               if(cbBytesRead > 0)
               {

                       _tprintf(_T("\rTotal Physical Memory = %d KB, Available Memory = %d KB."),
                               structMyDeviceMem.dwPhysTotal, structMyDeviceMem.dwPhysMemAvail);
               }
               Sleep(2000);
       }
       fflush(stdin);
       DWORD        dwDataWrite = 1 ;
       DWORD        cbWritten;
       hr = pStream->Write(&dwDataWrite, sizeof(dwDataWrite), &cbWritten);
       hr = CeRapiUninit();
       return 0;
}
                               

On the device a separate thread is dedicated for reading the notification from the desktop. The read operation will block the thread until it reads the notification from the stream. The main thread writes to the stream every two seconds. struct MY_MEM_STRUCT
{
       DWORD        dwPhysMemAvail;
       DWORD        dwPhysTotal;
};

UINT MyThreadProc(LPVOID pParam);
CRITICAL_SECTION        g_structCriticalSect;
BOOL        g_fContinue = TRUE;

HRESULT        SendToMyRapiStream(IRAPIStream* pStream,
                       void*        pWriteStruct, int nDataWrite)
{
       HRESULT hr;
       DWORD        cbWritten, dwWrite;
       dwWrite = sizeof(nDataWrite);
       //Write the length information first
       hr = pStream->Write(&nDataWrite, dwWrite, &cbWritten);
       if(FAILED(hr))
               return E_FAIL;
       //Write the structure into the stream
       hr = pStream->Write(pWriteStruct, nDataWrite, &cbWritten);
       if(FAILED(hr))
               return E_FAIL;
       return S_OK;
}

int CallMyFunction(DWORD dwInput, BYTE* pInput,
                  DWORD* pcbOutput, BYTE** ppOutput,
                  IRAPIStream* pStream)
{
       MEMORYSTATUS structMemStatus;
       BOOL        bContinue = TRUE;
       MY_MEM_STRUCT        structMyMem;
       HRESULT        hr;

       DWORD        dwThreadId;
       InitializeCriticalSection(&g_structCriticalSect);

       HANDLE hThread = CreateThread(NULL, NULL,
               (LPTHREAD_START_ROUTINE)MyThreadProc, pStream,
               NULL, &dwThreadId);

       while(1)
       {
               EnterCriticalSection(&g_structCriticalSect);
               if(g_fContinue == FALSE)
               {
                       LeaveCriticalSection(&g_structCriticalSect);
                       break;
               }
               LeaveCriticalSection(&g_structCriticalSect);
               GlobalMemoryStatus(&structMemStatus);
               structMyMem.dwPhysMemAvail = structMemStatus.dwAvailPhys / 1024;
               structMyMem.dwPhysTotal = structMemStatus.dwTotalPhys / 1024;
               hr = SendToMyRapiStream(pStream, &structMyMem, sizeof(structMyMem));
               if(FAILED(hr))
               {
                       break;
               }
                 /*wait for 2 seconds. if WaitForSingleObject() comes out
               in less than 2 seconds then client has requested to stop
               sending the data or an error has occured.*/
               if(WaitForSingleObject(hThread, 2000) != WAIT_TIMEOUT)
                       break;
       }
       //Make sure the other thread has terminated
       WaitForSingleObject(hThread, 5000);
       DeleteCriticalSection(&g_structCriticalSect);
       g_fContinue = FALSE;
        return 0;

}

UINT MyThreadProc(LPVOID pParam)
{
       DWORD        dwInput, cbReadBytes;
       IRAPIStream* pStream = (IRAPIStream*)pParam;
       HRESULT hrStream = pStream->Read(&dwInput, sizeof(dwInput), &cbReadBytes);
       EnterCriticalSection(&g_structCriticalSect);
       g_fContinue = FALSE;
       LeaveCriticalSection(&g_structCriticalSect);
       return 0;
}
                               
For additional information, see the MSDN CeRapiInvoke function reference:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcesdkr/htm/_wcesdk_cerapiinvoke.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcesdkr/htm/_wcesdk_cerapiinvoke.asp)
Back to the top

Building Modules That Use RAPI
The Rapi.h header file and Rapi.lib library file are available for use in compiling and linking desktop code to use RAPI. The header file and library files for use with RAPI functions are found in the following folder in a typical installation:

C:\Windows CE Tools\wce300\MS Pocket PC\support\ActiveSync\Inc
C:\Windows CE Tools\wce300\MS Pocket PC\support\ActiveSync\Lib
To compile the device-side DLL, you can use the same Rapi.h file mentioned earlier. There is no RAPI library file necessary to create the device DLL.

NOTE: RAPI.dll is not redistributable. It is installed when ActiveSync is installed on the desktop host computer.


[출처]
http://www.kbalertz.com/kb_Q299891.aspx

PDA에서 외장 메모리 삽입/삭제 시 이벤트

QA: How to implement autorun from a storage card?
By Joao Paulo Figueira, June 02, 2003.
Print version

Question
I want to start an application when the user inserts or removes a storage card. How do I implement such functionality?

Answer
You can execute an application when a storage card is inserted or removed from your device. This may allow your users to install applications, perform backups, or other action you like. The protocol is really simple:


Create one directory under the root of the card named after your CPU's ID. For instance, StrongARM is 2577, SH3 is 10003 and MIPS is 4000.
Your application must be named autorun.exe and it must be in that directory.
When the card is inserted, the application is called with 'install' as the command line parameter. When the card is removed, the parameter is 'uninstall'.

Here is a skeleton application. You can create it using eVC's Wizard, choosing the 'WCE Pocket PC 2002 Application'.

TCHAR szCFPath [MAX_PATH+1]; // Compact Flash path


// OnCardInsert
//
// The compact flash card has been inserted
//
void OnCardInsert()
{
   //
   // Get the path from where we are starting up
   //
   if(!SHGetAutoRunPath(szCFPath))
   {
       return;
   }
}


// OnCardRemove
//
// The compact flash card has been removed
//
void OnCardRemove()
{
}


// WinMain
//
// Main entry point
//
int WINAPI WinMain(HINSTANCE hInstance,
                  HINSTANCE hPrevInstance,
                  LPTSTR    lpCmdLine,
                  int       nCmdShow)
{
   if(lstrcmpi(lpCmdLine, _T("install")) == 0)
   {
       //
       // Card has been inserted
       //
       OnCardInsert();
   }
   else if(lstrcmpi(lpCmdLine, _T("uninstall")) == 0)
   {
       //
       // Card has been removed
       //
       OnCardRemove();
   }
   return 0;
}


Note that your application does not know where it is starting. You cannot infer that the startup path will be the one you are expecting.

Related resources:

[출처]
http://www.pocketpcdn.com/articles/autorun.html

CF기반에서 c#코딩한 소스 봤습니다. 질문좀 드릴께요

rda = new SqlCeRemoteDataAccess( "http://192.168.0.3/Mobile/sscesa20.dll","", "", strCon );

rda.Pull( "userinfo", strSQL, strOleCon );

이 부분이 무엇을 하는것인지는 잘 모르겠습니다. 알려주시면
감사하겠습니다.

테스트 결과는 잘 나왔습니다. 좋은 자료 감사드립니다.

[SQLCE강좌] 7. RDA 아키텍쳐와 구현 방법


안녕 하세요.
멍구 입니다.

일곱번째 강좌 시작 하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

RDA 아키텍쳐

RDA란 원격지의 SQL서버에 접근해 원하는 작업을 수행하는 방법으로
경우에 따라서 SQLCE DB에 결과를 받아 오기도 합니다.
SELECT문장의 경우가 대표적인 경우로 MR과 마찮가지로 PDA에 저장된
SQLCE DB를 ADOCE로 접근해서 원하는 데이타를 볼 수 있습니다.

사용자 삽입 이미지

MR과 비슷하지만 Server쪽에 OLE 프로바이더를 통해 직접 SQL서버에
접근 하는 것을 볼 수있습니다. 그래서 MR과 같이 별도의 서버 셋팅이 필요가 없습니다.
그리고, 원하는 작업을 바로 SQL서버에 반영 할 수가 있습니다.
하지만 DB접근을 하기위해 항상 통신이 활성화 되어 있어야 하므로 엄청난 통신비를
부담해야 합니다.
지금이야 CDMA를 통한 통신이 주를 이루고 있어서 MR이 더 적합하지만,
곧 무선 인터넷이 활성화 될경우 RDA방식으로 개발을 해야 할 것입니다.

------------------------------------------------------------------

EVC로 MR구현

역시 EVC를 실행하고 프로젝트를 하나 생성합니다.

사용자 삽입 이미지

이번에는 RDANorthwind라는 프로젝트를 생성하겠습니다.

RDA역시 MR과 마찮가지로 SQLCE와 ADOCE를 사용하기 위한 사전 작업을 해줘야 합니다
※ 수행할 작업이 MR과 같으므로, 스크린 샷은 MR용을 재활용 하겠습니다.

사용자 삽입 이미지

첫번째로 [Alt]-[F7]키를 눌러 Project setting 다이얼로그를 띄워 Link탭을 열어,
Obejct/library modules란에 ca_mergex20.lib를 입력합니다.

사용자 삽입 이미지

두번째로 Tools / Options 의 Options 다이얼로그를 띄워 Directories탭을 열어,
Show directorys의 Include Files로 맞추고, ADOCE와 SQLCE의 해더파일 경로를 맞춰줍니다

사용자 삽입 이미지

마찮가지로 Show directorys의 Library Files로 맞추고, ADOCE와 SQLCE의
해더파일 경로를 맞춰줍니다. 이때는 해당 PDA의 CPU타잎에 알맞는 경로를
지정해야 합니다.

사용자 삽입 이미지


ADOCE의 편한 사용을 위해 MR에서 사용한 기존 클래스를 추가합니다

#include <ca_mergex20.h>
#include "VORecordset.h"


MRNorthwindDlg.cpp파일 윗부분에 SQLCE와 ADOCE를 사용하기 위해
include문을 추가 합니다.

사용자 삽입 이미지


리소스는 위 그림과 같이 구성합니다.
파란색 글씨는 해당 콘트롤의 ID입니다.

각 버튼의 클릭 이벤트 함수를 정의 합니다.
      afx_msg void OnBtnSelect();
afx_msg void OnBtnUpdate();

OnInitDialog()함수에 리스트롤 초기화 하는 코드를 삽입합니다.
	CListCtrl* pList = (CListCtrl*) GetDlgItem( IDC_LIST );
pList->InsertColumn( 0, L"ID", LVCFMT_CENTER, 50 );
pList->InsertColumn( 1, L"Name", LVCFMT_CENTER, 100 );

OnBtnSelect()함수에 Orders테이블을 SQL서버에서 조회해
List에 출력해주는 아래의 코드를 삽입한다.

	// 0. 각종 변수 초기화 
ISSCEEngine *pEngine;
ISSCERDA *pRda;
CVORecordset rs; // ADOCE 레코드셋
CString strSQL; // 쿼리 문장
CString strPath; // PDA 저장될 SQLCE DB 경로
CString strRemoteServer; // SQL서버 IP또는 이름
CString strDBName; // SQL서버에서 작업할 DB이름
CString strTblName; // SQLCE에 결과를 저장할 테이블이름
CString strLocalConnection; // SQLCE DB 연결 문자열
CString strRemoteConnection; // SQL서버 연결 문자열
CListCtrl* pList; // 리스트 콘트롤 포인터
CString str;
HRESULT hr;

pList = (CListCtrl*) GetDlgItem( IDC_LIST );

strSQL = L"SELECT OrderID, ShipName FROM Orders";
strPath = L"\\My Documents\\rNorthwind.sdf";
strRemoteServer = L"192.168.0.2";
strDBName = L"Northwind";
strTblName = L"Orders";
strLocalConnection.Format( L"Provider=microsoft.sqlserver.oledb.ce.2.0;Data Source=%s",
strPath );
strRemoteConnection.Format( L"provider=sqloledb; data source=%s; Initial Catalog=%s; user id=sa; password=sa",
strRemoteServer, strDBName );
// 1. 임시로 SQLCE DB파일을 생성한다.
hr = CoCreateInstance( CLSID_Engine, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCEEngine, (LPVOID*)&pEngine );
DeleteFile( strPath );
str.Format( _T("Data Source = %s;"), strPath );
hr = pEngine->CreateDatabase( (LPTSTR)(LPCTSTR)str );
pEngine->Release();
// 2. SQL서버에 접속해 SQLCE DB에 결과를 저장한다.
// COM사용을 위한 초기화
CoCreateInstance( CLSID_RemoteDataAccess, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCERDA, (LPVOID*)&pRda);
// 웹서버 접근을 위한 설정
pRda->put_InternetURL( L"http://192.168.0.2/Mobile/sscesa20.dll" );
pRda->put_InternetLogin( L"" );
pRda->put_InternetPassword( L"" );
// PDA저장
pRda->put_LocalConnectionString( (BSTR)(LPCTSTR)strLocalConnection );
pRda->Pull( (BSTR)(LPCTSTR)strTblName,
(BSTR)(LPCTSTR)strSQL,
(BSTR)(LPCTSTR)strRemoteConnection,
TRACKINGOFF_INDEXES, L"tblError" );
// 3. 저장된 결과를 List에 채운다.
hr = rs.Open( (BSTR)(LPCTSTR)strPath,
(BSTR)(LPCTSTR)strSQL,
adOpenKeyset, adLockOptimistic);

// 결과를 리스트에 넣는다.
pList->DeleteAllItems();
for (int i=0; !rs.IsEOF(); i++)
{
pList->InsertItem( i, rs.GetFieldValueString( 0 ) );
pList->SetItemText( i, 1, rs.GetFieldValueString(1) );

rs.MoveNext();
}

rs.Close();

1. 임시로 SQLCE DB파일을 생성한다. 부분에서는
기존의 Northwind.sdf파일을 삭제하고, ISSCEEngine 객체를 사용해
임시 DB파일을 생성합니다.

2. SQL서버에 접속해 SQLCE DB에 결과를 저장한다. 부분에서는
웹서버의 서버에이전트를 통해 oledb 프로바이더로 명령을 수행합니다.
이부분에서는 "SELECT" 쿼리를 사용하는데, 이렇게 테이블 형태의
작업 결과가 있는 명령을 실행할 경우 ISSCERDA 객체의 Pull 매소드를 사용합니다.
Pull 매소드를 사용해 작업 결과를 1번에서 생성한 임시DB에 저장합니다.

3. 저장된 결과를 List에 채운다. 부분에서는
PDA에 저장된 임시DB를 읽어 리스트에 채워주는 역활을 합니다.

OnBtnUpdate()함수에 아래 코드를 삽입합니다.

	// 0. 각종 변수 초기화 
ISSCERDA *pRda;
CString strSQL; // 쿼리 문장
CString strRemoteConnection; // SQL서버 연결 문자열
static CString strShipName; // 변경여부를 보여주기 위해

if ( !strShipName.Compare(L"newtype") )
strShipName = L"Vins et alcools Chevalier";
else
strShipName = L"newtype";

strSQL.Format( L"UPDATE Orders SET ShipName='%s' WHERE OrderID=10248",
strShipName );
strRemoteConnection=L"provider=sqloledb; data source=192.168.0.2; Initial Catalog=Northwind; user id=sa; password=sa";
// 1. SQL서버에 접속해 명령을 실행한다.
// COM사용을 위한 초기화
CoCreateInstance( CLSID_RemoteDataAccess, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCERDA, (LPVOID*)&pRda);
// 웹서버 접근을 위한 설정
pRda->put_InternetURL( L"http://192.168.0.2/Mobile/sscesa20.dll" );
pRda->put_InternetLogin( L"" );
pRda->put_InternetPassword( L"" );

// PDA저장
pRda->SubmitSQL( (LPTSTR)(LPCTSTR)strSQL, (LPTSTR)(LPCTSTR)strRemoteConnection );

여기서는 OrderID가 10248인 첫번째 레코드의 ShipName을 바꾸는 역활을 합니다.
결과가 없는 Update쿼리문이므로 ADOCE를 사용할 필요가 없습니다.
이렇게 결과가 없는 쿼리문은 Push나 SubmitSQL 매소드를 사용합니다.
제가 이해한 Push와 SubmitSQL의 차이점은 Push의 경우 Pull로 가져온 SQLCE DB를
사용해 명령을 수행 한다는 것이고, SubmitSQL은 SQL서버에 직접 명령을 수행하는
것으로 이해 했습니다. 그렇다 보니 Push가 조금은 더 사용이 번거롭네요.
틀린점이나 부족한 점이 있으면
지적해주세요


이제 예제를 실행 해보겠습니다.

사용자 삽입 이미지


처음 실행했을때 그림 입니다. 별로 볼것 없네요..^^;


사용자 삽입 이미지


[Select]버튼을 눌렀을때 그림입니다.
이 결과는 버튼이 눌렸을때, 서버에서 결과를 받아 잠시 PDA에 저장하여
리스트에 뿌려주는 것입니다.
여기서 첫번째 래코드의 ShipName이 "Vins et alcools Chevalier"인것을 확인할 수 있습니다.


사용자 삽입 이미지


위의 그림은 [Update]버튼을 누르고, 잠시후에
[Select]버튼을 눌렀을때 그림입니다.
보시면 첫번째 래코드의 ShipName이 바뀐것을 알 수 있습니다.


강좌의 전체 소스 받고 싶으시면
를 클릭하세요



이상으로 RDA의 설명이 끝났습니다.
RDA는 작업 결과를 확인할 필요가 있을경우 MR보다 사용이 더 복잡한 것을
알 수 있습니다. RDA 역시 제가 조금은 쉽게 사용할 수 있도록 클래스를 만들 었습니다.
아래의 파일을 다른 이름으로 대상 저장 하여 사용 하시면 됩니다.


RDANorthwind2.exe

업데이트된 전체


-----------------------------------------------------------------

덧글

RDA에서 Stored Procedure의 사용

RDA의 경우 SQL서버에 직접 접근하는 방식이기 때문에 Stored Procedure(이하 SP)를
사용할 수가 있습니다.

strSQL.Format( _T("EXEC p_UserLoginEx '%s', '%s'"), strUserID, strPwd );

위와 같은 코드면 간단하게 사용이 가능 합니다.
단, 주의 해야할 점은 Output Parameter를 사용할 수 없다는 점 입니다.
꼭 결과를 보고자 할때는 Select문을 사용해 Pull로 보여주어야 합니다.
이때 ISNULL로 Null값을 없애고, AS 구분을 사용해 반드시 필드명을 지정해
주어야 합니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 일곱번째 강좌를 마칩니다.

휴 이제야 강좌가 끝이 났네요.
부디 도움이 되었으면 좋겠네요.

정말 수고하셨습니다..^^


[SQLCE강좌] 6. MR 아키텍쳐와 구현 방법



안녕 하세요.
멍구 입니다.

여섯번째 강좌 시작 하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

MR 아키텍쳐

MR이란 DBMS의 병합 복제 기능을 이용하여 SQLCE와 동기화 하는 방법입니다.
MSSQL의 DB를 복제해서 SQLCE의 DB로 싱크받아 PDA에 저장하고 있는 것이지요.
SQLCE DB의 경우 하나의 DB가 하나의 물리적인 파일로 보관 됩니다.
보완을 위해 암호를 걸 수 도 있습니다.

사용자 삽입 이미지

위의 그림을 보면 가장 아래 SQL서버가 있고, 그 위에 SQL Server Reconciler는
저번 강좌에서 MR을 위한 SQL서버 셋팅 부분 입니다.

그 위에 SQL Server CE Server Agent는 ssceda20.dll로서
IIS에서 구동되는 ISAPI로 구현 되어있습니다.

Client영역의 SQL Server CE Client Agent는 PDA에 SQLCE를 설치하면서 등록한
DLL파일들입니다. 아마 ssceca20.dll라는 파일이 아닌가 생각 됩니다.
그 옆으로는 DB생성을 담당하는 SQL Server CE Database Engine이
있고, 그 아래 SQLCE의 실제 DB인 확장자가 sdf인 파일입니다.

이렇게 MR은 싱크를 받는 실제 Data를 Client( PDA )의 파일로 저장하게 됩니다.
그리고 필요에 따라 ADOCE를 통해 SQL Server CE Database에 접근해
SQL Server CE Database Engine를 사용하여 DB를 생성하거나
아니면 기 생성된 DB를 삽입/삭제 /추가 가 가능 합니다.


------------------------------------------------------------------

EVC로 MR구현

먼저 EVC를 실행하고 프로젝트를 하나 생성합니다.

사용자 삽입 이미지

여기서는 MRNorthwind라는 프로젝트를 생성하겠습니다.
간단하게 다이얼로그 베이스로 나머지 설정은 다 기본값을 넘어갑니다.

그렇게 생성된 프로젝트에서 코드를 보기 전에
먼서 EVT에서 몇가지 설정할 것이 있습니다.
설치한 SQLCE와 ADOCE를 연결하기 위한 과정입니다.

사용자 삽입 이미지

첫번째로 [Alt]-[F7]키를 눌러 Project setting 다이얼로그를 띄워 Link탭을 열어,
Obejct/library modules란에 ca_mergex20.lib를 입력합니다.

사용자 삽입 이미지

두번째로 Tools / Options 의 Options 다이얼로그를 띄워 Directories탭을 열어,
Show directorys의 Include Files로 맞추고, ADOCE와 SQLCE의 해더파일 경로를 맞춰줍니다

사용자 삽입 이미지

마찮가지로 Show directorys의 Library Files로 맞추고, ADOCE와 SQLCE의
해더파일 경로를 맞춰줍니다. 이때는 해당 PDA의 CPU타잎에 알맞는 경로를
지정해야 합니다.

만약 SQLCE를 설치하고 처음 작업하는 프로젝트라면 ADOCE 해더파일이 없습니다.
EVT를 설치한 C:\Program Files\Microsoft eMbedded Tools\EVC\WCE300\BIN
폴더를 열어보면 midl.exe라는 파일을
ADOCE가 설치 되어 있는 C:\Windows CE Tools\dataaccess31\include로
카피한후

>midl adoce31.idl
>midl adocec31.idl

위와 같이 실행하여 idl로 해더 파일을 생성해 주어야 합니다.
아래의 그림은 그 결과입니다.

사용자 삽입 이미지


사용자 삽입 이미지


리소스는 위 그림과 같이 구성합니다.
파란색 글씨는 해당 콘트롤의 ID입니다.

각 버튼의 클릭 이벤트 함수를 정의 합니다.

	afx_msg void OnBtnSync();
afx_msg void OnBtnSetList();

OnInitDialog()함수에 리스트롤 초기화 하는 코드를 삽입합니다.

#include <ca_mergex20.h>
#include <ADOCE31.H>

MRNorthwindDlg.cpp파일 윗부분에 SQLCE와 ADOCE를 사용하기 위해
include문을 추가 합니다..
      CListCtrl* pList = (CListCtrl*) GetDlgItem( IDC_LIST );
pList->InsertColumn( 0, L"ID", LVCFMT_CENTER, 50 );
pList->InsertColumn( 1, L"Name", LVCFMT_CENTER, 100 );
OnBtnSync()함수에 DB싱크를 하는 코드를 삽입합니다.
	ISSCEMerge* pCEMerge;
// SQLCE사용을 위한 COM 초기화
CoCreateInstance( CLSID_Replication, NULL,
CLSCTX_INPROC_SERVER, IID_ISSCEMerge,
(LPVOID*)&pCEMerge );
// 웹서버 접근을 위한 경로 및 권한 설정
pCEMerge->put_InternetURL( _T("http://192.168.0.2/mobile/sscesa20.dll") );
pCEMerge->put_InternetLogin( _T("") );
pCEMerge->put_InternetPassword( _T("") );
// SQL서버에 접속해 데이타를 동기화 시키기 위한 설정
pCEMerge->put_Publication( _T("Northwind") );
pCEMerge->put_PublisherDatabase( _T("Northwind") );
pCEMerge->put_Publisher( _T("TESTSERVER") );
pCEMerge->put_PublisherSecurityMode( (REPL_SECURITY_TYPE)DB_AUTHENTICATION );
pCEMerge->put_PublisherLogin( _T("sa") );
pCEMerge->put_PublisherPassword( _T("sa") );
// SQLCE에 싱크를 받아 저장하기 위한 설정
pCEMerge->put_Subscriber( _T("newtype") );
pCEMerge->put_SubscriberConnectionString( _T("Data Source = \\My Documents\\Northwind.sdf") );
pCEMerge->AddSubscription(CREATE_DATABASE);
// 각 설정을 반영한다.
pCEMerge->Initialize();
// 실제 동기화를 시작한다.
pCEMerge->Run();
// 사용한 자원 해제
pCEMerge->Terminate();
pCEMerge->Release();

위의 코드는 SQL서버와 동기화 부분입니다.
코드에 주석을 달아 그렇게 어렵지 않을 것입니다.
단, put_Subscriber의 인자 값을 조정하면 SQL서버의 필터링 설정에서
HOST_NAME()함수를 사용해 필터링기능을 사용할 수 있습니다.

이제 ADOCE를 사용하는 부분이 남았는데, ADOCE를 사용하는데 많이 불편합니다.
그래서 ADOCE를 조금은 편하게 사용할 수 있도록 만들어 놓은 클래스를 사용하겠습니다.
먼저 아래의 파일을 다른 이름으로 대상 저장을 해 프로젝트에 추가 합니다.


위의 클래스를 사용하깅 위해 include문을 추가 합니다..

#include "VORecordset.h"

OnBtnSetList()함수에 아래의 코드를 삽입합니다.

	CListCtrl*   pList = (CListCtrl*) GetDlgItem( IDC_LIST );
HRESULT hr;
CVORecordset rs;
CString strSQL = L"SELECT OrderID, ShipName FROM Orders";
hr = rs.Open( L"\\My Documents\\Northwind.sdf",
((BSTR)(LPCTSTR)strSQL),
adOpenKeyset, adLockOptimistic);
// 결과를 리스트에 넣는다.
for (int i=0; !rs.IsEOF(); i++)
{
pList->InsertItem( i, rs.GetFieldValueString( 0 ) );
pList->SetItemText( i, 1, rs.GetFieldValueString(1) );
rs.MoveNext();
}
rs.Close();

이제 컴파일 하여 실행해 봅시다.
처음에 실행을 하면 아래 그림과 같은 아무것도 없는 리스트가 나옵니다.
사용자 삽입 이미지

여기서 [Sync]버튼을 클릭하고
C:\Windows CE Tools\wce300\MS Pocket PC\emulation\palm300\My Documents
폴더를 열어 보면 Northwind.sdf파일이 생성된것을 볼 수 있습니다.
이 파일은 SQLCE파일로서 SQL서버에서 동기화을 받은 Orders테이블의 내용인것입니다.

이제 [List]버튼을 클릭합니다.

사용자 삽입 이미지

리스트에 내용이 채워지는 것을 볼 수 있습니다.

강좌의 전체 소스 받고 싶으시면
를 클릭하세요



이상으로 MR의 설명이 끝났습니다.
위와 같은 방법으로 MR을 사용하면 컴 객체 초기화등 여러 불편한 점이 많이 있습니다.
그래서 제가 MR을 조금은 쉽게 사용할 수 있도록 클래스를 만들 었습니다.
아래의 파일을 다른 이름으로 대상 저장 하여 사용 하시면 됩니다.


MRNorthwind2.exe

업데이트된 전체

-----------------------------------------------------------------

덧글

EVC를 에뮬에서 컴파일 하면 wincore에러가 나는 경우가 있습니다.
이것은 .NET계열의 IME와 충돌하는 것으로 별도 코드를 추가해야 합니다.

App객체에 PreTranslateMessage를 재정의 해 아래의 코드를 추가합니다.
BOOL CMRNorthwindApp::PreTranslateMessage(MSG* pMsg) 
{
#ifdef _WIN32_WCE_EMULATION
if( ! ::IsWindow(pMsg->hwnd))
return TRUE;
#endif
return CWinApp::PreTranslateMessage(pMsg);
}


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 긴 여섯번째 강좌를 마칩니다.
강좌라는게 쉬운게 아니군요.
원래의 계획은 간단하게 코드만 설명하려 했는데..
아니다 싶어 일을 크게 벌려 놨더니 힘드네여.. ㅡㅡ;;
그래도 이제 끝이 보이네요.

다음 강좌는 RDA 아키텍쳐와 구현 방법에 대해 알아 보겠습니다.


[SQLCE강좌] 5. MR 서버 셋팅 방법


안녕 하세요.
멍구 입니다.

다섯번째 강좌 시작 하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

MR의 경우 SQL서버에 별도의 셋팅을 필요로 합니다.
EM을 사용해서 셋팅 하므로 그렇게 어려지 않습니다.
역시 스샷을 보며 간단하게 설명해 나가겠습니다.

일단 Northwind DB를 MR셋팅을 해보겠습니다.

사용자 삽입 이미지

도구 / 복제 / 게시 만들기 및 관리 를 선택 합니다.

사용자 삽입 이미지

Northwind를 선택하고, 개시 만들기를 클릭합니다.

사용자 삽입 이미지

마법사가 실행되면 다음 단계로 넘어갑니다.


사용자 삽입 이미지

배포자는 첫 번째 항목의 자체 배포자를 선택합니다.

사용자 삽입 이미지

스냅샷 폴더는 sscesa20.dll이 위치한 가상폴더를 지정해 줍니다.

사용자 삽입 이미지

게시데이타 베이스도 Northwind를 선택합니다.

사용자 삽입 이미지

게시유형 선택에서 [병합게시]를 선택합니다.

사용자 삽입 이미지

구독자 유형은 [SQL Server CE를 실행하는 장치]를 선택합니다.

사용자 삽입 이미지

아티클 지정은 동기화 시킬 테이블을 지정 할 수 있습니다.

사용자 삽입 이미지

아티클 문제는 원래 테이블 설계시 고려해야할 사항이지만, 지금은 Northwind라는 셈플 DB를 사용함으로 그냥 참조만 합니다.

사용자 삽입 이미지

게시 이름은 Northwind를 입력합니다.

사용자 삽입 이미지

복제 필터링은 복제 대상의 조건을 지정하는 것으로 필요에 따라 선택합니다.
여기서는 지정하지 않겠습니다.

사용자 삽입 이미지

사용자 삽입 이미지


완료후 DB가 복제 된것을 확인 할 수 있습니다.

사용자 삽입 이미지

개시 속성창을 열어 [구독기간 제한 없음]을 선택합니다.

사용자 삽입 이미지

IIS 가상 폴더를 열어보면 unc라는 새로운 폴더가 생긴것을 확인 할 수 있습니다.
이는 스냅샷을 찍었을때 생기는 것으로 반드시 sscesa20.dll이 위치한 가상폴더에 존재해야 합니다.



이렇게 서버 셋팅이 끝이 났습니다.

이 셋팅 과정에서 동기화할 사용자를 지정한 다든지,
사용자별로 특정 범위만 동기화를 한다든지,
일부분만 동기화 한다든지 하는 세부 설정을 할 수가 있습니다

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 다섯번째 강좌를 마칩니다.

다음 강좌는 MR 아키텍쳐와 구현 방법에 대해 알아 보겠습니다.


[SQLCE강좌] 4. PDA에 SQLCE설치

안녕 하세요.
멍구 입니다.

네번째 강좌 시작 하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

SQLCE를 사용하기 위해서는 PDA에도 설치를 해줘야 합니다.

먼저 PDA의 windows폴더에 카피할 파일들 이 있습니다.

SQLCE를 설치하게 되면 ADOCE 3.1도 설치가 되는데
ADOCE의 경로를 보면 C:\Windows CE Tools\dataaccess31\wce300
아래 여러 폴더가 있는 설치하고자 하는 PDA에 맞는 CPU폴더 아래에
파일을 PDA의 windows폴더에 카피합니다.

     adoce31.dll
     adocedb31.dll
     adoceoledb31.dll
     adoxce31.dll
     msdadc.dll
     msdaeren.dll

SQLCE가 설치된 C:\Program Files\Microsoft SQL Server CE 2.0\Device
아래 역시 여러 폴더가있는데, 역시 PDA에 맞는 CPU타잎을 선택합니다
IPAQ의 경우 \Arm\Sa1100 폴더 밑에 파일을 카피하시면 됩니다.

     ssceerror20en.dll
     dllregister.exe
     ssce20.dll
     ssceca20.dll

카피 작업이 끝났으면 PDA의 파일 탐색기를 열어 windows폴더로 갑니다.
스타일러스 팬을 사용하여 dllregister.exe를 실행 합니다.
그러면 성공적으로 설치 되었다는 메시지를 볼 수 있습니다.

이것으로 PDA에 SQLCE설치가 끝났습니다.

위에 카피하신 파일들은 ADOCE용 파일과 SQLCE용 파일들 인데,
파일이 빠졌을경우 dllregister.exe에서 빠진 파일이 없다며 에러 메시지를 보여줍니다.
설치시 어려운 점은 없을 것입니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 네번째 강좌를 마칩니다.
이번 강좌는 너무 대충 넘어간 느낌이 드네요..^^;

다음은 MR을 위한 서버 셋팅 방법을 알아 보겠습니다.

[SQLCE강좌] 3. 기본적인 Server Agent 설정

안녕 하세요.
멍구 입니다.

세번째 강좌 시작하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

기본적인 Server Agent의 설정

저번 강좌를 통해 SQL서버와 SQLCE의 동기화를 위해 SQL서버에는 IIS가 있고,
SQLCE를 설치하면 같이 설치가 되는 sscesa20.dll이란 파일이 있다는걸 알았습니다.

HTTP프로토콜을 통해 이 sscesa20.dll을 접근 할 수 있게 셋팅이 필요합니다.

셋팅 방법은 두가지 방법이 있습니다. SQLCE를 설치하면
\Program Files\Microsoft SQL Server CE 2.0\Server 폴더 밑에
SQL Server CE Connectivity Management.msc라는 파일을 이용하는 방법과

위의 유틸리티를 사용하지 않고 하나하나 일일이 설정하는 방법있습니다.

SQL Server CE Connectivity Management.msc는 일종의 마법사로서
설정에 필요한 과정을 MMC콘솔 하나로 가능하게 해주는 유틸리티 입니다.

설정에 필요한 과정은 다음과 같습니다.

1. 가상 폴더 지정
2. IIS 권한 설정
3. NTFS 권한 설정
4. sscesa20.dll 복사
5. 레지스트리 등록

마법사는 1~3까지의 과정을 수행하는데 도와줍니다.

사용자 삽입 이미지




















<그림 1> 마법사의 IIS권한 설정

사용자 삽입 이미지



















<그림2> 인터넷 서비스 관리자에서 가상 디렉터리를 지장

사용자 삽입 이미지
























<그림3> 인터넷 서비스 관리자에서 폴더 경로 및 권한 설정

IIS 권한 설정부분에서는 읽기와 쓰기를 체크해 줘야합니다.

사용자 삽입 이미지




















<그림 4> 마법사의 IIS 접근 사용자 지정

사용자 삽입 이미지



















<그림5> 인터넷 서비스 관리자에서 IIS 접근 사용자 지정

IIS의 접근 사용자는 인터넷 게스트 계정을 사용합니다.

사용자 삽입 이미지




















<그림 6> 버튼을 클릭합니다.

사용자 삽입 이미지




























<그림 7> 마법사의 ntfs권한을 설장하는 부분입니다.

사용자 삽입 이미지



























<그림8> 탐색기에서 NTFS 사용자 및 권한 지정

NTFS권한은 IIS접근 사용자를 추가 하고, 모든 권한을 줘야합니다.

권한 설정이 끝났으면 SQLCE를 설치한 \Program Files\Microsoft SQL Server CE 2.0\Server
폴더 밑에 있는 sscesa20.dll라는 파일을 가상 디렉토리로 지정한 폴더에 카피합니다.

마지막으로 도스 창을 열어 가상 디렉토리로 지정할 폴더로 이동합니다.

regsvr32를 통해 sscesa20.dll을 등록합니다.

ex> regsvr32 sscesa20.dll

이렇게 서버의 기본적인 sqlce셋팅이 끝났습니다.

여기에 MR의 경우 부가적인 SQL셋팅이 더 필요 하고,
RDA의 경우는 더 이상의 셋팅은 필요 없습니다.

정상적으로 셋팅이 되었는지는 웹브라우져를 통해 확인해 볼 수 있습니다.
사용자 삽입 이미지




















주소창에 http://서버의 IP/가상 디렉토리명/sscesa20.dll을 입력해서
위 그림과 같이 나온다면 성공한 것입니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 세번째 강좌를 마칩니다.
이번 강좌는 그림을 갈무리 하느라 힘들었네요..^^;

다음은 PDA에 SQLCE를 설치하는 방법을 알아 보겠습니다.

[SQLCE강좌] 2. MR? RDA?

안녕 하세요.
멍구 입니다.

두번째 강좌 시작하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

MR? RDA?

SQLCE를 별도 DB로 사용할 수도 있겠지만, 대부분의 경우 SQLCE를 사용한다는건
기간 MS-SQL과 연동해 사용하는 경우가 대부분입니다.
실제 사용하는 DBMS가 두개가 되는 것이지요.
DB SERVER에 저장되어 있는 기간 MS-SQL서버와
PDA안에서 구동되는 SQLCE가 그것이지요. 문제는 이 두개의 DBMS를 동기화
하는 것인데, Microsoft에서는 용도에 따라 두가지 방법을 제시 하고 있습니다.

MR(Merge & Replacation)과 RDA(Remote Data Access)가 그것입니다.

MR과 RDA에 관해 알아보기 전에 SQLCE가 어떤 아키텍쳐로 기간 서버와
동기화를 하는지 알아야 하는데 MSDN을 뒤져보던 중 좋은 그림이 있더군요.

사용자 삽입 이미지

















위의 그림을 보시면 Server Environment부분에 IIS가 있습니다.
바로 ISAPI로 구현된 Server Agent를 통해 SQL서버와 동기화를 하는것입니다.
SQLCE가 설치된 \Program Files\Microsoft SQL Server CE 2.0\Server
폴더를 보시면 sscesa20.dll라는 파일이 있습니다.
이 파일이 바로 그 역활을 하는 Sql Server CE Server Agent입니다.
Server Agent가 하는 역활이 Client Agent의 요청을 SQL서버에 넘겨주는 역활을 합니다.
Server Agent의 경우 별서 서버 셋팅이 필요한데, 다음 강좌에서 자세히 알아보겠습니다.


이제 MR과 RDA에 대해 간단히 소개해 드리겠습니다.

MR은 이름에서도 알수 있듯이 DBMS의 병합과 복제 기능을 이용해 동기화를 합니다.
그렇다 보니 용도가 서버와의 실시간 동기보다는 특정 시점에 동기화를
필요로 하는 업무에 보다 효율 적입니다.

외근 영업사원이 사용하는 팜플랫 대신 그런 자료들을 MR로 싱크 받아,
가지고 다니다가 팜플랫이 변경될 경우만 싱크를 새로 받는 경우가 되겠네요.

장점은 한번만 싱크를 받으면 변경되기 전까진 사용할 수 있으므로 싱크
받고자하는 시점에만 통신 연결을 해서 ( ActiveSync를 통할 수 도 있습니다 )
동기화를 하므로 통신비를 절감 할 수 있다는 것이죠.

단점은 서버가 언제 변경되었는지를 알 수 없다는 것입니다.. 게으른 영업사원의 경우
철지난의 팜플랫으로 영업을 할 수도 있겠네요.

RDA는 필요할 때마다 SQL서버에 직접 접근을 합니다.
그러다 보니 사용중에는 항상 통신 연결이 되어 있어야 합니다. 당연히 MR에
비해 통신비가 많이 나옵니다.
대신 속도가 MR에 비해 빠르다는 장점이 있습니다.

--------------------------------------------------------------------

<덧글>

Relay

SQLCE는 SQL서버와 동기화를 위해 HTTP 프로토콜을 통한 통신을 지원해야 합니다.
하지만 ActiveSync 3.1이하의 경우 ActiveSync를 통한 통신을 지원하지 않습니다.

그래서 Mircosoft는 별도의 유틸리티를 제공합니다.

SQLCE가 설치된 C:\Program Files\Microsoft SQL Server CE 2.0\Relay 폴더를
보면 sscerelay.exe라는 파일이 존재합니다.
이 파일이 ActiveSync 3.1이하의 경우도 sscesa.dll을 통한 동기화를 가능하게 해주는
유틸리티 입니다.

IIS가 구동중인 SQL서버에서 실행하며 사용법은 아래와 같습니다.

sscerelay.exe /clientport 81 /servername 210.110.135.23 /serverport 80 /register

각 파라이터 인자로 상황에 알맞는 포트번호와 IP를 지정해 주면 됩니다.

이 유틸리티는 ActiveSync로 싱크를 받지 않는 경우나, ActiveSync 3.1 보다
상위 버전을 사용할 경우에는 설치 할 필요가 없습니다.
2003.08현제 ActiveSync 3.7까지 나와 있습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 두번째 강좌를 마칩니다.
강좌가 욕심을 쫒아 가지 못하는것 같아 아쉽네요.

다음은 sscesa20.dll의 셋팅 방법에 대해 알아보겠습니다.