ではこのHello1.Class1のDCOMサーバをCE上に作成してみましょう。

DCOMサポートのPlatformの作成

カタログからDCOM(アプリケーションとサービスの開発の中)、その中のDCOMリモートアクセスを追加します。 また、Parameter Viewのcesysgen.batファイルを編集し、
set DCOM_MODULES=dcomcnfg
を追加します。 プラットフォームをリビルドして、 ダウンロード/初期化でCEPCをデバッグ実行しておきます。 以後の作業は基本的にCEPCをデバッグ実行したまま行います。

ユーザ名の登録

CEOSをDCOMサーバにしてWindows2000から接続するためには、 まずCEOS上にユーザと対応するパスワードを登録しなければなりません。 クライアント側は標準ではそのクライアントプログラムを実行したユーザ、 つまり、ログインしたユーザ(この例ではAdministrator)を認証に用いますので、 それを登録します。
eVC4でHello Worldアプリケーションを作成し、 適当なところ(例:WndProcのIDM_HELP_ABOUT)に以下のコードを追加します。
extern "C" BOOL WINAPI NTLMSetUserInfo(LPTSTR, LPTSTR);
...
case IDM_HELP_ABOUT:
  NTLMSetUserInfo(L"Adinistrator", L"PASSWORD");
  DialogBox(hInst, ....
また、設定…でデバッグのリンクのライブラリに c:\myproj\mycepc\WINCE410\CEPC\cesysgen\oak\lib\x86\debug\ntlmssp.lib を加えます。パスは適当に読み替えてください。

Platform builderでCEPCをデバッグ状態で実行していることを確認し、 eVCのPlatform Managerの構成でKITLトランスポートCESHサーバとなっていることを確認した後、 ビルド、実行し、ABOUTボックスを出すと、NTLMユーザが登録されます。

実際のパスワードはHKEY_LOCAL_MACHINE\Comm\Security\UserAccounts\Administrator にNTという160バイトのデータとして格納されます。 格納されているのはパスワードのLANMANハッシュ値(DES)とNTハッシュ値(MD4)なので、 生パスワードがそのまま書かれているわけではないです。

Hello1 DLLプロジェクトの作成

では実際のHello1.Class1サーバを作成します。
eVCを起動し、ATL COM AppWizardで新規プロジェクトHello1をDLLサーバで作成します。 次に、ATLオブジェクトの新規作成をします。 SimpleObjectを選び、ショートネームにClass1と打ち、 タイプをHello1.Class1 ClassとでもしてOKします。
作成されたClass1.rgsファイルを編集し、代理親のための項目を設定します。
HKCR
{
  Hello1.Class1.1 = s 'Hello1.Class1 Class'
  {
    CLSID = s '{091D8751-4E65-4B7C-8AC8-0C92B8FB43E9}'
  }
  Hello1.Class1 = s 'Hello1.Class1 Class'
  {
    CLSID = s '{091D8751-4E65-4B7C-8AC8-0C92B8FB43E9}'
    CurVer = s 'Hello1.Class1.1'
  }
  NoRemove CLSID
  {
    ForceRemove {091D8751-4E65-4B7C-8AC8-0C92B8FB43E9} = s 'Hello1.Class1 Class'
    {
      ProgID = s 'Hello1.Class1.1'
      val AppID = s '{E85159E6-07C1-4C91-B9E3-724E9A8A1D3D}'
      VersionIndependentProgID = s 'Hello1.Class1'
      ForceRemove 'Programmable'
        InprocServer32 = s '%MODULE%'
        {
          val ThreadingModel = s 'Apartment'
        }
      'TypeLib' = s '{E85159E6-07C1-4C91-B9E3-724E9A8A1D3D}'
    }
  }
  NoRemove AppID
  {
    {E85159E6-07C1-4C91-B9E3-724E9A8A1D3D} = s 'Hello1 Component'
    {
      val DllSurrogate = s ''
    }
  }
}
次にメソッドSumを追加します。 ClassViewからIClass1をポイントし、右クリックでメソッドの追加を選び、 メソッド名にSum、パラメータに int x, int y, [out,retval] int* sum を入力します。 追加された関数CClass1::Sumに以下のように入力します。
STDMETHODIMP CClass1::Sum(int x, int y, int* sum)
{
  // TODO: この位置にインプリメント用のコードを追加してください
  *sum= x+y;
  return S_OK;
}
書き換えたら、CEPCがデバッグ状態で実行されていることを確認し、ビルドします。 ビルドすると、Hello1.dllが作成され、自動的にCEPC:\Windowsに転送され、 さらにHello1.dllのDllRegisterServerが呼び出されます。 つまり、上記で指定したレジストリが反映されます。 代理親を指定しているので、Hello1.dllをデバッグ実行しておく必要はありません。

dcomcnfgの実行

次に、デバッグ実行中のCEPC上でdcomcnfgを起動します。 CEPC上でファイル名を指定して実行、でもいいですし、Platform Builderのターゲットコントロールから Windows CE>s dcomcnfg でもかまいません。
(レジストリが足りなくて起動できない、という時は リモートレジストリエディタでHello1.Class1のCLSIDのレジストリ項目を 確認してください。 最低でも1つのAppID値を持つCLSIDが確認できないとdcomcnfgは実行できません。)
Hello1.Class1 Classを選んで、 そのSecurity-Launchに Administrator、 Accessにも Administrator を追加します。

この追加した内容はHKCR\AppID\{E85159E6-07C1-4C91-B9E3-724E9A8A1D3D}の、 LaunchPermissionとAccessPermission値に設定されます。 プログラムからこれを設定する場合はDCOMAccessControlオブジェクトのIAccessControlを使います。

クライアントから接続

ではクライアントから接続してみましょう。 クライアント機にAdministratorでログインします。 以前のVB同士の実験の時とはHello1.Class1に対応するCLSID値が変更されていますので、 まずこれをクライアント機でregeditを実行して修正か追加します。
HKEY_CLASSES_ROOT\Hello1.Class1\CLSID = {091D8751-4E65-4B7C-8AC8-0C92B8FB43E9}
なお、クライアント側でのProgID(Hello1.Class1)とサーバCEPC側でのProgIDは違っていてもかまいません。あくまでCLSIDが一致していればいいです。 で、以前のVBクライアントのコードを修正し、 ボタンのイベントに以下のように記述します。
Private Sub Command1_Click()
  Dim a As Object
  Set a = CreateObject("Hello1.Class1", "192.168.0.10")
  MsgBox a.Sum(10, 10)
End Sub
CEPCは標準では自分のマシン名を持たず、WINSに登録したりしませんので、 IPアドレスによる指定が必要です。 デバッグ実行しているCEPCのIPアドレスを調べるには HKLM\Comm\VMINI1\Parms\TCPIPでも覗いてください。

では実行してボタンを押してみましょう。エラーなく終了しましたか? オブジェクトを作成できないとか書き込みエラーが出る場合は、 セキュリティではねられている場合がほとんどです。 NTLMSetUserInfoを実行したか、 そのユーザとパスワードでクライアントはログインしているか、 Hello1.dllを登録したか、 dcomcnfgを実行してアクセス権を設定したか確認します。

エラーの状況によっては、Platform Builderが例外をキャッチしてCEPCが一時停止する場合がありますが、そのまま何回か実行継続すれば通常実行状態にもどります。 うっとおしい時はCE例外処理…で当該例外番号を登録してください。

なお、dcomcnfgで起動ユーザとアクセスユーザにAdministratorを登録しましたが、 これは @* を登録するとすべてのユーザに権限が与えられます。 ただし、ここで @* を設定しても HKCR\AppID\{E8..}\AuthenticationLevelにNONE=1を設定しても、 dcomcnfgでのデフォルト値(HKLM\SOFTWARE\Microsoft\OLE\LegacyAuthenticationLevel)にNONE=1を設定しても、 NTLMSetUserInfoで登録されていないユーザは権限を与えられないようです。 誰にでもとりあえずオブジェクトを起動させて、 オブジェクト内部で簡単な認証を行うことが出来るといいんですがねー。 SOAPは出来るよなあ、これ。

実際に通信用として用いるにはシングルトンオブジェクトを活用したりすることに なります。その他CE側をクライアントにするとかは普通にできると思うので、 ヘルプ→Application Dev.→Component Services(DCOM)とか参照してください。 DCOM全般については、インサイドCOM+基本編が実用書として良いかと思います。

→デバイスはUSBで