PDFに埋め込めるメタデータとしてXMP (Extensible Metadata Platform)というものがあります。

XMPファイルは、creativecommons.orgのライセンス付与のページ(Choose a License)で、作れるようになっています。具体的には、「他の人があなたへのクレジットを表示するのを助けてください! (Help others attribute you!)」のところで、各情報を入力し、「ライセンス・マーク (License mark)」でXMPをを選択すると、ダウンロードのリンクが現れます。

ただ、このXMPファイルをPDFに埋め込むツールが意外と全然見当たりません。XMPファイルを埋め込むことだけを行えるアプリが見つからないんです。そこで作ってみました: egtra/embed-xmp (GitHub)。実行ファイルのダウンロードはReleases · egtra/embed-xmpです。

コマンドラインアプリで、こんな感じに引数を指定します。

EmbedXMP (入力PDFファイル) (入力XMPファイル) (出力XMPファイル)

実行には.NET Framework 4.5.2が必要です。PDFファイルの操作にはiTextSharpを使いました。

参考: XMP Specification日本語版 (Adobe)

この記事のカテゴリ

  • ⇒ PDFにXMPメタデータファイルを埋め込むツールを作った

Boost.勉強会 #20 東京でこんな発表をしました: エクストリームC++11/14プログラミング

このスライドはCreative Commons — 表示 – 継承 4.0 国際 — CC BY-SA 4.0です。ダウンロードはこちらです。

「自分はこうしている」という話が意外と聞けたことが大変有益でした。

なお、std名前空間の中に利用者が定義を書くことは、ごく限られた状況に限り許されています。詳しい解説がstd名前空間にテンプレートの特殊化を追加する – Cry’s Diaryにあります。

この記事のカテゴリ

  • ⇒ Boost.勉強会 #20で発表
  • ⇒ Boost.勉強会 #20で発表

boost::make_iterator_range_n関数の話です。これを最近見つけました。これは、先頭要素を指すイテレータと要素数を実引数に渡すとRangeオブジェクトを返してくれる関数です。調べてみるとBoost 1.56.0からのものでした。

C言語のAPIでよくある、配列要素へのポインタと要素数という組み合わせをRange化するのに便利です。

#include <array>
#include <iostream>
#include <boost/range/iterator_range.hpp>
#include <windows.h>
#include <wtsapi32.h>
 
#pragma comment(lib, "wtsapi32.lib")
 
int main()
{
  WTS_SESSION_INFO* sessionInfoArray;
  DWORD count;
  if (!WTSEnumerateSessions(
    WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessionInfoArray, &count))
  {
    std::cerr << "Failed" << std::endl;
    std::quick_exit(1);
  }
  // sessionInfoArray[0]からsessionInfoArray[count - 1]に
  // データが書き込まれている。
 
  for (const WTS_SESSION_INFO& info
    : boost::make_iterator_range_n(sessionInfoArray, count))
  {
    std::cout << info.SessionId
      << ' ' << info.pWinStationName << std::endl;
  }
  std::quick_exit(0);
  // WTSFreeMemoryは省略
}

main関数の仮引数argcとargvに対しても使えます。

#include <iostream>
#include <boost/range/iterator_range.hpp>
 
int main(int argc, char** argv)
{
  // なお、argv[0]を除外したければ
  // boost::make_iterator_range_n(argv + 1, argc - 1)とすれば良い。
  auto r = boost::make_iterator_range_n(argv, argc);
  for (auto arg : r)
  {
    std::cout << arg << std::endl;
  }
}

以前より、先頭と終端(の次)のイテレータ2つからRangeを作ってくれるboost::make_iterator_range関数がありました。これを使って、boost::make_iterator_range(sessionInfoArray, sessionInfoArray + count)やmake_iterator_range(argv, argv + argc)というコードを良く書いていました。そのため、こういう関数が欲しいなあと思っていたところだったので、すでに存在していて嬉しいです。

この記事のカテゴリ

  • ⇒ 配列要素へのポインタと要素数からRangeを作る

ありそうで見当たらなかったので、そういうサンプルコードを書いておこうと思いました。

  • 同期モードです。非同期モードは少しコードの分量が増えるので見送りました。
  • #import使っているので、Visual C++限定です。
#define UNICODE
#define _UNICODE
 
#include <iostream>
#include <locale>
 
#import <msxml6.dll>
 
int main()
{
  std::locale loc(std::locale::classic(), "", std::locale::ctype);
  std::wcout.imbue(loc);
  std::wcerr.imbue(loc);
 
  try
  {
    _com_util::CheckError(CoInitializeEx(
      nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
 
    MSXML2::IXMLHTTPRequestPtr xhr(__uuidof(MSXML2::XMLHTTP60));
    xhr->open(
      L"GET",
      L"https://www.google.co.jp/robots.txt",
      VARIANT_FALSE);
    xhr->send();
 
    auto status = xhr->status;
    if (200 <= status && status <= 299)
    {
      std::wcout << xhr->responseText << std::endl;
    }
    else
    {
      std::wcerr << xhr->status << L' ' << xhr->statusText << std::endl;
    }
 
    CoUninitialize(); // RAII化していないのは手抜き。
  }
  catch (const _com_error& e)
  {
    std::wcerr << e.Error() << L' ' << e.ErrorMessage() << std::endl;
  }
}

対比して理解できるよう、VBScriptとJavaScript(共にWSH)で書いたものも置いてきます。C++版のcatch節に相当する部分を欠いているのはご容赦ください。

Set xhr = CreateObject("Msxml2.XMLHTTP.6.0")
xhr.open "GET", "https://www.google.co.jp/robots.txt", False
xhr.send
If 200 <= xhr.status And xhr.status <= 299 Then
  WScript.Echo xhr.responseText
Else
  WScript.Echo xhr.status & " " & xhr.statusText
End If
var xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0")
xhr.open("GET", "https://www.google.co.jp/robots.txt", false);
xhr.send();
if (200 <= xhr.status && xhr.status <= 299) {
  WScript.Echo(xhr.responseText);
} else {
  WScript.Echo(xhr.status + " " + xhr.statusText);
}

HTTPステータスコードが200番台以外だった場合(else側)はhttpstat.usで試しました。こういう、任意のHTTPステータスコードを試せるウェブサービスはほかにもあるようです。

細かい制御が要らないなら、意外と短いコードになるので、Visual C++で使うのもアリではないかと思いました。

MSDNライブラリのリファレンス: IXMLHTTPRequest Members

この記事のカテゴリ

  • ⇒ XMLHttpRequestをVC++で使う例
  • ⇒ XMLHttpRequestをVC++で使う例
  • ⇒ XMLHttpRequestをVC++で使う例
  • ⇒ XMLHttpRequestをVC++で使う例

初めにお断りしておきます。タイトルは少し嘘です。正確には、WinPcapのデバイスドライバnpf.sysを使わずにWiresharkを動かします。wpcap.dllは使います。

以前書いた記事、netsh traceと同じようにパケットキャプチャーするプログラムを作ったの続きです。

今回作ったもののソースコードはegtra/ndiscap-packet (GitHub)にあります。

前回のあらすじ

Windows 7より、OSにパケットキャプチャ機能が搭載されており、netsh traceコマンドで呼び出せます。これを他のアプリから使う方法が公開されていないようだったので、なんのAPIを使っているのか調べました。そして、以下のソースコードが出来上がりました。

Gist: “netsh trace start caputre=yes traceFile=D:\packet.etl”の再現 · GitHub

今回やったこと

前回のコードを基に、WinPcap互換のDLLを作ろうと考えました。WinPcapを使う代表的なアプリケーションとしてWireshark(Wireshark – 窓の杜)を対象とし、Wiresharkがとりあえず動くところまで作りました。

動機

始めたきっかけは、近頃のWindowsとWinPcapのnpf.sysの組み合わせにおける安定性への疑問からです。npf.sysなしでWinPcapを使えたら良いと思っていました。

もっとも、現状ではWin10Pcap – WinPcap for Windows 10が良い解決策でしょう。私も普段はWin10Pcapを使っています。

動かし方

  1. Githubのリポジトリegtra/ndiscap-packetにあるソースコードをVisual Studio 2015でビルドする。packet.dllが出来上がる。
  2. WinPcap · DownloadからWinPcapをダウンロードし、wpcap.dllを取り出す。WinPcapのインストールは不要。
  3. C:\Windows\System32など、パスの通るところにwpcap.dllとビルドしたpacket.dllを置く。
  4. WiresharkをWinPcap抜きでインストールする。
  5. Wiresharkを「管理者として実行」する。packet.dllの影響で起動に時間がかかる(私の場合、10秒ほど)ので、少々待つ。
  6. 「NdisCapPacket」というインタフェースが1つ存在するはずなので、それに対してキャプチャーを開始。

ndiscap-packetがWiresharkで認識されている様子

メニューバーの「キャプチャ」→「オプション」をクリックするとこういう表示になります。もちろん、このウィンドウを呼び出さず、メインウィンドウからキャプチャを開始しても問題ありません。

WinPcapのAPI

WinPcap互換のものを作ろうとしているので、作るにあたってWinPcapのおさらいをしました。

WinPcap: WinPcap internalsで説明されているように、WinPcapは以下のような階層になっています。

Application Wpcap.dll Packet.dll Npf.sys

Wpcap.dllはlibpcapとほぼ互換の高水準なAPI、Packet.dllは低水準なAPIを提供しています。アプリからWpcap.dllが呼び出され、Packet.dllが呼び出され、デバイスドライバNpf.sysに到達するという階層です。

余談ですが、Win10PcapのWin10Pcap.sysは、Npf.sysと互換となっているのですね。

というわけで、Wpcap.dllまたはPacket.dllのいずれかと互換性のあるDLLを作れば良いというわけです。そこで、双方のAPIを見て回り、作るのが簡単そうだったPacket.dllの互換品を作ることにしました。

まとめ

WinPcapのデバイスドライバnpf.sysあるいはその代替となるWin10Pcapを使うことなく、WinPcapを使うアプリケーションを動作させたいと考えました。

突貫工事で作ったので、まだ実用になる状態ではありません。プルリク歓迎です。無線LANなどいろいろな環境で試したり、実装をサボっているAPIをまじめに実装したりしないと使い物にならないでしょう。

この記事のカテゴリ

  • ⇒ WiresharkをWinPcap抜きで動かす
  • ⇒ WiresharkをWinPcap抜きで動かす

こういうコードを書いたとき、38ってWindows SDKのどこかで#defineされていないの?と疑問に思いました。

#include <iostream>
#include <windows.h>
 
int main()
{
  // {00000000-0000-0000-0000-000000000000}
  GUID guid = GUID_NULL;
 
  WCHAR s[39]; // ← これ!!!39ってみんな書くでしょ
 
  StringFromGUID2(guid, s, ARRAYSIZE(s));
  std::wcout << s << std::endl;
}

というわけで、grepしたところ以下のものが見つかりました。

  • axcore.idl:#define CHARS_IN_GUID 39
  • cfgmgr32.h:#define MAX_GUID_STRING_LEN 39
  • strmif.h:#define CHARS_IN_GUID 39
  • Msi.h:#define MAX_GUID_CHARS 38

38なのは、最後のヌル文字を含まない場合ですね。

いずれも、<windows.h>などからインクルードされるものではありません。なので、いつでも使おうと無条件に言える感じがしませんが、気が向いたら使ってみてはどうでしょうか、と締めてみます。

この記事のカテゴリ

  • ⇒ GUIDの文字列表現の文字数のための定数
  • ⇒ GUIDの文字列表現の文字数のための定数

RegisterActiveObject関数は内部でRunning Object Table (ROT)を使っていると、MSDNライブラリにも書かれてあります: Registration Functions (Automation)

ROTは、モニカーをキーとしてオブジェクトを登録する、つまり連想配列のようなものです。ならば、クラスIDをキーに登録するRegisterActiveObjectはどういうモニカーでROTに登録するのか、ということを知りたくなりました。

調べた結果、クラスIDの文字列のアイテムモニカーであることが分かりました。すなわち、モニカーの文字列表現は"!{XXXXXXXX-……}"という形式になります。

今回のサンプルプログラムは、RegisterActiveObjectで登録したオブジェクトをIRunningObjectTable::GetObjectで取り出したり、IRunningObjectTable::Registerで登録したオブジェクトをGetActiveObjectで取り出したりするというものです。

#define UNICODE
#define _UNICODE
#define WIN32_LEAN_AND_MEAN
#define _ATL_NO_AUTOMATIC_NAMESPACE
 
#include <iostream>
#include <cstdlib>
#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlutil.h>
 
// {EECAE0DA-5F2B-48E2-9F22-09A788DA83FE}
static const GUID CLSID_Hoge =
{
  0xeecae0da, 0x5f2b, 0x48e2,
  { 0x9f, 0x22, 0x9, 0xa7, 0x88, 0xda, 0x83, 0xfe }
};
 
struct Hoge : IUnknown
{
  IFACEMETHOD(QueryInterface)(REFIID riid, void** ppv) override
  {
    if (riid == IID_IUnknown)
    {
      *ppv = (IUnknown*)this;
      return S_OK;
    }
    return E_NOINTERFACE;
  }
  IFACEMETHOD_(DWORD, AddRef)() override { return 1; }
  IFACEMETHOD_(DWORD, Release)() override { return 1; }
};
 
void ExitIfFailed(_In_ PCWSTR functionName, HRESULT hr)
{
  if (SUCCEEDED(hr))
  {
    return;
  }
  std::wclog << functionName << '\n';
  std::wclog << std::showbase << std::hex << hr << '\n';
  std::wclog << ATL::AtlGetErrorDescription(hr).GetString() << std::endl;
  std::quick_exit(static_cast<int>(hr));
}
 
int main()
{
  std::wclog.imbue(std::locale(""));
  auto hrInit = CoInitializeEx(
    nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  ExitIfFailed(L"CoInitializeEx", hrInit);
 
  ATL::CComPtr<IRunningObjectTable> rot;
  auto hrRot = GetRunningObjectTable(0, &rot);
  ExitIfFailed(L"GetRunningObjectTable", hrRot);
 
  ATL::CComPtr<IMoniker> mk;
  auto hrMk = CreateItemMoniker(
    L"!", L"{EECAE0DA-5F2B-48E2-9F22-09A788DA83FE}", &mk);
  ExitIfFailed(L"CreateItemMoniker", hrMk);
 
  std::cout << std::boolalpha;
  {
    Hoge obj;
    DWORD reg;
    auto hrReg = RegisterActiveObject(
      &obj, CLSID_Hoge, ACTIVEOBJECT_STRONG, &reg);
    ExitIfFailed(L"RegisterActiveObject", hrReg);
 
    ATL::CComPtr<IUnknown> unk;
    auto hrGet = rot->GetObject(mk, &unk);
    ExitIfFailed(L"IRunningObjectTable::GetObject", hrGet);
 
    std::cout << unk.IsEqualObject(&obj) << std::endl;
 
    RevokeActiveObject(reg, nullptr);
  }
  {
    Hoge obj2;
    DWORD reg;
    auto hrReg = rot->Register(
      ROTFLAGS_REGISTRATIONKEEPSALIVE, &obj2, mk, &reg);
    ExitIfFailed(L"RegisterActiveObject", hrReg);
 
    ATL::CComPtr<IUnknown> unk;
    auto hrGet = GetActiveObject(CLSID_Hoge, nullptr, &unk);
    ExitIfFailed(L"GetActiveObject", hrGet);
 
    std::cout << unk.IsEqualObject(&obj2) << std::endl;
 
    rot->Revoke(reg);
  }
  std::quick_exit(0);
}

クラスモニカーを使っているのかと思っていましたが、その予想は外れてしまいました。

この記事のカテゴリ

  • ⇒ RegisterActiveObjectとROT
  • ⇒ RegisterActiveObjectとROT
  • ⇒ RegisterActiveObjectとROT

デバイスインスタンスパス(デバイスインスタンスID)からIPアドレスやMACアドレスなどの情報を取り出したり、あるいはその逆でデバイスインスタンスパスを得る話です。WMIを使って良ければ、Win32_NetworkAdapterのPnPDeviceIdプロパティで解決ですが、諸般の事情でこれを使いたくない場合の方法です。

Setup APIやCfgMgr APIなどで取り扱われるデバイスインスタンスパスはWindowsに接続されているデバイスを識別する文字列です。一方、ネットワーク関係のAPIでは、各ネットワークインタフェースの識別方法にGUID、LUID、インタフェースインデックスなど複数あります。これらを結び付ける方法が知りたいわけです。

これには、Network Configuration InterfacesのAPIを使います。紹介記事がBindview とINetCfg API | Japan WDK Support Blogにあります。このAPIを使うと、デバイスインスタンスパスとインタフェースGUIDの組み合わせが判明します。

以下、サンプルプログラムです。

#include <iostream>
#include <comdef.h>
#include <windows.h>
#include <netcfgx.h>
#include <devguid.h>
 
_COM_SMARTPTR_TYPEDEF(INetCfg, __uuidof(INetCfg));
_COM_SMARTPTR_TYPEDEF(INetCfgClass, __uuidof(INetCfgClass));
_COM_SMARTPTR_TYPEDEF(IEnumNetCfgComponent, __uuidof(IEnumNetCfgComponent));
_COM_SMARTPTR_TYPEDEF(INetCfgComponent, __uuidof(INetCfgComponent));
 
int main()
{
  std::locale l(std::locale::classic(), "", std::locale::ctype);
  std::wcout.imbue(l);
  std::wclog.imbue(l);
 
  HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  if (FAILED(hr))
  {
    return static_cast<int>(hr);
  }
 
  {
    INetCfgPtr nc(CLSID_CNetCfg, nullptr, CLSCTX_INPROC_SERVER);
    nc->Initialize(nullptr);
    INetCfgClassPtr ncc;
    nc->QueryNetCfgClass(&GUID_DEVCLASS_NET, IID_PPV_ARGS(&ncc));
    IEnumNetCfgComponentPtr e;
    ncc->EnumComponents(&e);
    ULONG fetched;
    INetCfgComponentPtr component;
    while (e->Next(1, &component, &fetched) == S_OK)
    {
      DWORD characteristics;
      if (SUCCEEDED(component->GetCharacteristics(&characteristics)))
      {
        if ((characteristics & NCF_HIDDEN) != 0)
          continue;
      }
      PWSTR displayName;
      if (SUCCEEDED(component->GetDisplayName(&displayName)))
      {
        std::wcout << displayName << std::endl;
        CoTaskMemFree(displayName);
      }
      PWSTR devNodeId;
      if (SUCCEEDED(component->GetPnpDevNodeId(&devNodeId)))
      {
        std::wcout << '\t' << devNodeId << std::endl;
        CoTaskMemFree(devNodeId);
      }
      GUID guid;
      if (SUCCEEDED(component->GetInstanceGuid(&guid)))
      {
        WCHAR buf[39];
        StringFromGUID2(guid, buf, ARRAYSIZE(buf));
        std::wcout << '\t' << buf << std::endl;
      }
      std::wcout << std::hex << "\tcharacteristics: "
        << characteristics << std::endl;
    }
    nc->Uninitialize();
  }
  CoUninitialize();
}

このプログラムで出力しているGUIDは、Win32_NetworkAdapter.GUIDと一致します。

また、IP_ADAPTER_ADDRESSES::Luidとは、以下の関数で相互に変換できます。

この2関数はWindows Vistaで追加された関数です。もし、それ以前を対象とする場合はNhpAllocateAndGetInterfaceInfoFromStack関数を使いましょう。こちらでは、インタフェースインデックス (IP_ADAPTER_ADDRESSES::IfIndex)と結び付けられます。

この記事のカテゴリ

  • ⇒ デバイスインスタンスパスからネットワークインタフェースの情報を引き出す(あるいはその逆)
  • ⇒ デバイスインスタンスパスからネットワークインタフェースの情報を引き出す(あるいはその逆)

以前の記事、UCRTをアプリと同じフォルダに置く (VS2015 RTM)の更新版です。Visual Studio 2015 Update 1以降の変更を反映させました。

おさらい

Visual C++ 2015でCRTの構成が大きく変わりました。C標準ライブラリ関数の多くがucrtbase.dll(リリースビルド), ucrtbased.dll(デバッグビルド)に移され、Windows SDKの配下になりました。これがユニバーサルCRT (Universal CRT; UCRT)です。

ucrtbase.dllはOSの一部という扱いになりました。Windows 10では最初から入っています。Vista~8.1に対してもWindows Updateで配信されており、最新はKB311841です: Windows での汎用の C ランタイムの更新プログラム。なお、VS 2015 RTM時点ではKB2999226でした。

あまり変わらない場合

以下の場合、Visual C++ 2013までとほぼ変わりありません。

  • 再頒布パッケージvcredist_x86.exe, vcredist_x64.exeを使う場合: UCRTも同時にインストールされます。
  • コンパイラオプション/MT/MTdで静的リンクする場合: UCRT部分も静的にリンクされます。

なお、日本語版の再頒布パッケージは、マイクロソフトのダウンロードセンターからダウンロードできます: Download Visual Studio 2015 の Visual C++ 再頒布可能パッケージ from Official Microsoft Download Center

ちょっと変わる場合

問題は、VC++ランタイムDLLを同梱し、アプリと同じフォルダにランタイムDLLを置く場合です。

  • ユニバーサルCRT(KB311841やKB2999226)がインストール済みであることをあてにしてよければ、概ね今までどおりです。すなわち、VC++ 2015でビルドしたアプリと同じフォルダにVC++ランタイムDLLを置くだけで良いです。
  • それをあてにできない場合、VC++ 2015のランタイムDLLに加え、ユニバーサルCRT(ucrtbase.dllとそれが依存するファイル)も同じフォルダに置くことになります。

ようするにランタイムDLLのファイルがたくさん増えたというだけで、難しいことではないです。

なお、マージモジュールを使う場合も同じく影響があるはずです。なぜなら、VC++ 2015のマージモジュールには、UCRTが含まれていないそうです。しかし、私がマージモジュールを使ったことがないので、お話しできません。

DLLの場所

というわけで、VC++ 2015とUCRTのランタイムDLLをアプリに同梱するにあたり、必要なDLLファイルの場所を紹介していきます。なお、この先現れるC:\Program Files (x86)は、環境に応じて適当に読み替えてください。

リリースビルドの場合、

  • Visual C++ 2015ランタイム(リリースビルド用)
  • ユニバーサルCRT(リリースビルド用)

で紹介するファイルが必要です。デバッグビルドの場合

  • Visual C++ 2015ランタイム(デバッグビルド用)
  • ユニバーサルCRT(デバッグビルド用)

で紹介するファイルが必要です。

いつもどおりであれば、デバッグビルド用のランタイムライブラリは再頒布可能ではないはずです。すなわち、自分でデバッグするためなどの用途でしか使えないと思います。注意してください。

Visual C++ 2015ランタイム(リリースビルド用)

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redistの中にファイルがあります。VC++ 2013までと同じなので、以前のバージョンを使ったことがあるかたなら、すぐ分かるでしょう。

このフォルダの中のx86ならびにx64フォルダの中、以下のファイルが必要になります。

  • Microsoft.VC140.CRT\vcruntime140.dll: ほぼすべての場合
  • Microsoft.VC140.CRT\msvcp140.dll: C++標準ライブラリを使用している場合
  • Microsoft.VC140.CRT\vccorlib140.dll: C++/CXプログラムの場合
  • Microsoft.VC140.CRT\concrt140.dll: 同時実行ランタイム (ConcRT)を使用している場合
  • Microsoft.VC140.CXXAMP\vcamp140.dll: AMPを使用している場合
  • Microsoft.VC140.OPENMP\vcomp140.dll: OpenMPを使用している場合

Visual C++ 2015ランタイム(デバッグビルド用)

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\debug_nonredistにリリースビルド用の場合と同じような階層構造でファイルが置いてあります。

x86ならびにx64の中に、以下のようなファイルがあります。

  • Microsoft.VC140.DebugCRT\vcruntime140d.dll
  • Microsoft.VC140.DebugCRT\msvcp140d.dll
  • Microsoft.VC140.DebugCRT\vccorlib140d.dll
  • Microsoft.VC140.DebugCRT\concrt140d.dll
  • Microsoft.VC140.DebugCXXAMP\vcamp140d.dll
  • Microsoft.VC140.DebugOpenMP\vcomp140d.dll

ユニバーサルCRT(リリースビルド用)

x86, x64それぞれ、以下の場所にあるファイルすべてです。ucrtbase.dllとそれに必要なDLLがすべて置いてあります。

  • C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64
  • C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86

念のため、前回同様にファイルの一覧も載せておきます。

  • api-ms-win-core-console-l1-1-0.dll
  • api-ms-win-core-datetime-l1-1-0.dll
  • api-ms-win-core-debug-l1-1-0.dll
  • api-ms-win-core-errorhandling-l1-1-0.dll
  • api-ms-win-core-file-l1-1-0.dll
  • api-ms-win-core-file-l1-2-0.dll
  • api-ms-win-core-file-l2-1-0.dll
  • api-ms-win-core-handle-l1-1-0.dll
  • api-ms-win-core-heap-l1-1-0.dll
  • api-ms-win-core-interlocked-l1-1-0.dll
  • api-ms-win-core-libraryloader-l1-1-0.dll
  • api-ms-win-core-localization-l1-2-0.dll
  • api-ms-win-core-memory-l1-1-0.dll
  • api-ms-win-core-namedpipe-l1-1-0.dll
  • api-ms-win-core-processenvironment-l1-1-0.dll
  • api-ms-win-core-processthreads-l1-1-0.dll
  • api-ms-win-core-processthreads-l1-1-1.dll
  • api-ms-win-core-profile-l1-1-0.dll
  • api-ms-win-core-rtlsupport-l1-1-0.dll
  • api-ms-win-core-string-l1-1-0.dll
  • api-ms-win-core-synch-l1-1-0.dll
  • api-ms-win-core-synch-l1-2-0.dll
  • api-ms-win-core-sysinfo-l1-1-0.dll
  • api-ms-win-core-timezone-l1-1-0.dll
  • api-ms-win-core-util-l1-1-0.dll
  • api-ms-win-crt-conio-l1-1-0.dll
  • api-ms-win-crt-convert-l1-1-0.dll
  • api-ms-win-crt-environment-l1-1-0.dll
  • api-ms-win-crt-filesystem-l1-1-0.dll
  • api-ms-win-crt-heap-l1-1-0.dll
  • api-ms-win-crt-locale-l1-1-0.dll
  • api-ms-win-crt-math-l1-1-0.dll
  • api-ms-win-crt-multibyte-l1-1-0.dll
  • api-ms-win-crt-private-l1-1-0.dll
  • api-ms-win-crt-process-l1-1-0.dll
  • api-ms-win-crt-runtime-l1-1-0.dll
  • api-ms-win-crt-stdio-l1-1-0.dll
  • api-ms-win-crt-string-l1-1-0.dll
  • api-ms-win-crt-time-l1-1-0.dll
  • api-ms-win-crt-utility-l1-1-0.dll
  • ucrtbase.dll

さてはて、ucrtbase.dllとVC++ランタイムの依存関係を見ても、ここにあるファイルすべてが必要なわけではなさそうに見えます。Windows XPに持っていって動かしてみましたが、やはりそうです。少し気になります。

ユニバーサルCRT(デバッグビルド用)

上記ユニバーサルCRT(リリースビルド用)で紹介した場所にあるファイルのうち、ucrbase.dll以外と、以下の場所にあるucrtbased.dllが必要となります。

  • C:\Program Files (x86)\Windows Kits\10\bin\x86\ucrt
  • C:\Program Files (x86)\Windows Kits\10\bin\x64\ucrt

むすび

Visual Studio 2015 RTMでは、パッケージングでミスしてC:\Program Files (x86)\Windows Kits配下のファイルが使えないという話でしたが、Update 1で直ったようです: Introducing the Universal CRT | Visual C++ Team Blogの“Distributing Software that uses the Universal CRT”の6.。そのため、今回の記事を書きました。

上記では、x86とx64のみを紹介しましたが、ほとんどにはarmも、一部にはarm64もあります。ただ、デスクトップアプリを作れるわけではないので、今のところ出番は無いと思います。

2016年4月26日追記: UCRTの更新KB311841とVC++ 2015 Update 2の再頒布パッケージの記載を追加しました。Nさん(コメント)より情報提供いただきました。ありがとうございます。

2016年7月16日追記: VC++ Update 3の再配布パッケージの記載に差し替えました。Update 1とUpdate 2の再配布パッケージは、ダウンロードセンターから削除されたようです。

この記事のカテゴリ

  • ⇒ UCRTをアプリと同じフォルダに置く (VS2015 Update 1以降)

Windowsでパケットキャプチャをするなら、WiresharkとWinPcapの組み合わせが最も一般的だと思います。しかし、Windowsにもパケットキャプチャの機能が標準搭載されています。それはnetsh trace startコマンドです。

Windows 7以降のnetsh traceコマンドでパケットをキャプチャする方法 – Eiji James Yoshidaの記録

ならば、自分のアプリからも使えないものかと思い、netsh trace startがやっていることを調べてみました。同じようにAPIを呼び出せば、同じようにパケットキャプチャできるはずという、至極単純な発想です。


というわけで作ったプログラムがこちらです。

gist: “netsh trace start caputre=yes traceFile=D:\packet.etl”の再現

Windows 8.1とServer 2012 R2、ともにx64で動作を見ました。これを実行すると、netsh trace start capture=yes traceFile=D:\capture.etlを実行したかのように、パケットキャプチャが始まります。停止するには、netsh trace stopを実行します。

出力ファイルの拡張子が.ETLなので、ETW APIを使っていることは、即座に分かりました。しかし、それだけでは足りず、最終的に以下の処理が必要でした。

  1. ndiscapサービス(デバイスドライバ)を起動する。
  2. ndiscap関係のレジストリを書き換える。
  3. INetCfgでms_ndiscapを有効化する
  4. ETWのStartTrace, EnableTraceEx2関数を呼び出し、以下2つのトレースを開始する。
    • {83ED54F0-4D48-4E45-B16E-726FFD1FA4AF}: Microsoft-Windows-Networking-Correlation
    • {2ED6006E-4729-4609-B423-3EE7BCD678EF}: Microsoft-Windows-NDIS-PacketCapture
  5. netsh関係のレジストリを書き換える。

さて、これはまだnetshと同様にファイルに書き出すだけです。次は、これをファイルに書き出さず、パケットの中身を取れるようにしたいと考えるのが自然な発想でしょう。

実は、ETWのStartTraceでリアルタイムモードを指定してもパケットを取れることは確認しました。というわけで、次は、WinPcapのnpf.sys抜きでwpcap.dllを動かせないか、試しているところです。2016年6月27日追記:やった結果をWiresharkをWinPcap抜きで動かすに書きました。

この記事のカテゴリ

  • ⇒ netsh traceと同じようにパケットキャプチャーするプログラムを作った
  • ⇒ netsh traceと同じようにパケットキャプチャーするプログラムを作った

次ページへ »