diff --git a/pykd/pykd_vc120.vcxproj b/pykd/pykd_vc120.vcxproj
index 92ddd55..e656bf6 100644
--- a/pykd/pykd_vc120.vcxproj
+++ b/pykd/pykd_vc120.vcxproj
@@ -590,6 +590,7 @@
+
@@ -623,6 +624,7 @@
+
diff --git a/pykd/pykd_vc120.vcxproj.filters b/pykd/pykd_vc120.vcxproj.filters
index 464d7d1..3f4b9b1 100644
--- a/pykd/pykd_vc120.vcxproj.filters
+++ b/pykd/pykd_vc120.vcxproj.filters
@@ -81,6 +81,9 @@
Header Files
+
+ Header Files
+
@@ -248,6 +251,9 @@
Source Files
+
+ Source Files
+
diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp
index a2218c4..624168a 100644
--- a/pykd/pymod.cpp
+++ b/pykd/pymod.cpp
@@ -20,6 +20,7 @@
#include "pytypeinfo.h"
#include "pycpucontext.h"
#include "pyprocess.h"
+#include "pytagged.h"
using namespace pykd;
@@ -597,6 +598,12 @@ BOOST_PYTHON_MODULE( pykd )
python::def( "removeSyntheticSymbol", pykd::removeSyntheticSymbol,
"The removeSyntheticSymbol function removes a synthetic symbol from a module in the current proces" );
+ // secondary callback data
+ python::def("enumTagged", pykd::enumTagged,
+ "Return the list of secondary callback data IDs (as a strings)" );
+ python::def("loadTaggedBuffer", pykd::loadTaggedBuffer,
+ "Read the buffer of secondary callback data by ID" );
+
python::class_( "numVariant", "numVariant", python::no_init )
.def("__init__", python::make_constructor(&NumVariantAdaptor::getVariant) )
.def( "__eq__", &NumVariantAdaptor::eq )
diff --git a/pykd/pytagged.cpp b/pykd/pytagged.cpp
new file mode 100644
index 0000000..a20b8d5
--- /dev/null
+++ b/pykd/pytagged.cpp
@@ -0,0 +1,76 @@
+
+#include "stdafx.h"
+
+#include "pytagged.h"
+#include "kdlib\tagged.h"
+#include "kdlib\exceptions.h"
+
+#include "pythreadstate.h"
+#include "stladaptor.h"
+
+#include
+#pragma comment(lib, "Rpcrt4.lib")
+
+
+namespace pykd {
+
+
+void __declspec(noreturn) throwRpcStatus( const std::string &functionName, RPC_STATUS status )
+{
+ std::stringstream sstr;
+ sstr << "Call " << functionName << " failed\n";
+ sstr << "RPC_STATUS 0x" << std::hex << status;
+ throw kdlib::DbgException( sstr.str() );
+}
+
+python::list enumTagged()
+{
+ std::list ids;
+ {
+ AutoRestorePyState pystate;
+ ids = std::move( kdlib::enumTagged() );
+ }
+
+ python::list result;
+
+ for (const auto &id : ids)
+ {
+ RPC_WSTR id_str = nullptr;
+ auto status = ::UuidToString(&id, &id_str);
+ if (RPC_S_OK != status)
+ throwRpcStatus("UuidToString", status);
+
+ const auto stringFree =
+ [](RPC_WSTR *str)
+ {
+ auto status = ::RpcStringFree(str);
+ if (RPC_S_OK != status)
+ throwRpcStatus("RpcStringFree", status);
+ };
+ std::unique_ptr freeGuard{&id_str, stringFree};
+
+ result.append( std::wstring(reinterpret_cast(id_str)) );
+ }
+ return result;
+}
+
+python::list loadTaggedBuffer(const std::wstring &id_str)
+{
+ const auto rcp_str =
+ reinterpret_cast( const_cast(id_str.c_str()) );
+ kdlib::TaggedId id;
+ auto status = ::UuidFromString(rcp_str, &id);
+ if (RPC_S_OK != status)
+ throwRpcStatus("UuidFromString", status);
+
+
+ kdlib::TaggedBuffer buff;
+ {
+ AutoRestorePyState pystate;
+ buff = std::move( kdlib::loadTaggedBuffer(id) );
+ }
+
+ return vectorToList(buff);
+}
+
+} // namespace pykd
diff --git a/pykd/pytagged.h b/pykd/pytagged.h
new file mode 100644
index 0000000..d688e51
--- /dev/null
+++ b/pykd/pytagged.h
@@ -0,0 +1,12 @@
+
+#pragma once
+
+namespace python = boost::python;
+
+namespace pykd {
+
+python::list enumTagged();
+
+python::list loadTaggedBuffer(const std::wstring &id_str);
+
+} // namespace pykd
diff --git a/test/scripts/pykdtest.py b/test/scripts/pykdtest.py
index 810f0ee..df433b9 100644
--- a/test/scripts/pykdtest.py
+++ b/test/scripts/pykdtest.py
@@ -27,6 +27,7 @@ import excepttest
import targetprocess
import ehloadtest
import synsymtest
+import taggedtest
pykd.initialize()
@@ -65,6 +66,7 @@ def getTestSuite( singleName = "" ):
unittest.TestLoader().loadTestsFromTestCase( mspdbtest.MsPdbTest ),
unittest.TestLoader().loadTestsFromTestCase( targetprocess.ProcessTest ),
unittest.TestLoader().loadTestsFromTestCase( ehloadtest.EhLoadTest ),
+ unittest.TestLoader().loadTestsFromTestCase( taggedtest.TaggedTest ),
#unittest.TestLoader().loadTestsFromTestCase( excepttest.ExceptionTest ),
] )
diff --git a/test/scripts/pykdtest.pyproj b/test/scripts/pykdtest.pyproj
index 0708c66..61f3761 100644
--- a/test/scripts/pykdtest.pyproj
+++ b/test/scripts/pykdtest.pyproj
@@ -50,6 +50,7 @@
+
diff --git a/test/scripts/taggedtest.py b/test/scripts/taggedtest.py
new file mode 100644
index 0000000..49d7a4a
--- /dev/null
+++ b/test/scripts/taggedtest.py
@@ -0,0 +1,31 @@
+import unittest
+import pykd
+import sys
+import os
+
+class TaggedTest(unittest.TestCase):
+ def setUp(self):
+ dump_file = os.path.join( os.path.dirname(sys.argv[0]),
+ r"..\..\kdlibcpp\kdlib\tests\dumps\win8_x64_mem.cab" )
+ self.dump_id = pykd.loadDump( dump_file )
+
+ self._existing_id = "D03DC06F-D88E-44C5-BA2A-FAE035172D19"
+ self._non_existing_id = "88597A32-1493-41CA-BF87-2A950DF4CEE0"
+
+ def tearDown(self):
+ pykd.closeDump( self.dump_id )
+
+ def testEnum(self):
+ ids = pykd.enumTagged()
+
+ self.assertTrue( self._existing_id.lower() in [i.lower() for i in ids] )
+
+ self.assertFalse( self._non_existing_id.lower() in [i.lower() for i in ids] )
+
+
+ def testLoadBuffer(self):
+ buff = pykd.loadTaggedBuffer( self._existing_id )
+ self.assertEqual( len(buff), 0x410 )
+
+ self.assertRaises( pykd.DbgException, pykd.loadTaggedBuffer, self._non_existing_id )
+