Dynamically add event handlers to controls in a custom .NET control with C#

A few years back I came up with something called “FRF” (Forms Rendering Framework) which allowed me to render any UI (whether they are Web or Windows based) based on a template definition (Yes, this was long before Microsoft released XAML). Anyways, on one of my personal projects I had a requirement to dynamically create and host Windows controls inside native applications (property pages more specifically). The challenge here was being able to notify the container window (Property page) that something has been modified or changed in the contained user control and then being able to notify the property sheet’s window procedure of that change.  In my personal case, I had to monitor changes made to any textbox (TextChanged) inside the hosted control, the way I got it sorted is depicted below

private IEnumerable<Control> CreateHelper(ITemplateDefinition definition) { var retval = new List<Control>(); var flag = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public; var targetAsm = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(x => string.Equals(x.GetName().Name, " System.Windows.Forms", StringComparison.OrdinalIgnoreCase)); if (targetAsm != null) { definition.TemplateElements.ToList().ForEach(x => { try { var controlType = targetAsm.GetTypes() .FirstOrDefault(z => z.IsSubclassOf(typeof(Control)) && !z.IsAbstract && z.Name.ToUpperInvariant() .Contains(x.Class.ToUpperInvariant())); // Expected to have a default constructor without any parameters var control = controlType.GetConstructors(flag) .FirstOrDefault().Invoke(null) as Control; PropertySetter(control, x); // Does it have a TextChanged event? var ei = control.GetType().GetEvent("TextChanged"); if (ei != null) ei.AddEventHandler(control, new EventHandler((sender, args) => ChangeMonitor.HasChanges = true)); retval.Add(control); } catch (Exception ex) { Logger.LogError(ex); } }); } return retval; }

Pass information to .NET control from Native Code with Visual C++

As a follow up to this post on interoperability between .NET and native code, this post describes how we can host a .NET user control from an ActiveX control, but more importantly set properties that drive or change the behaviour of the .NET user control.

First of all, we need to define an interface with the properties that are required by the user control, but more importantly accessible to native code, and we declare it similarly we do with any other interface but we need to decorate it with a Guid attribute. In this case the control stores the path for a given file to do something, at the same time it’s important to note that if some operations are required to be done to the control then the control should return its HWND (as depicted below)

[Guid("EBD2C511-4EEB-488A-9BF1-EE14FB631C92")] public interface IMetaFsProps { /// <summary> /// Gets the control HWND. /// </summary> /// <returns>IntPtr.</returns> IntPtr GetControlHwnd(); /// <summary> /// Gets or sets the selected file. /// </summary> /// <value>The selected file.</value> string SelectedFile { get; set; } }

Then our .NET user control must implement the interface previously mentioned, but the user control class must also be decorated with a few attributes which are

[ClassInterface(ClassInterfaceType.None)] [ComSourceInterfaces(typeof(IMetaFsProps))] [Guid("917937B1-1AD8-4951-A417-3BED065089A0")] public partial class MyFileControl : UserControl, IMetaFsProps { /// <summary> /// Gets the control HWND. /// </summary> /// <returns></returns> public IntPtr GetControlHwnd() { return Handle; } /// <summary> /// Gets or sets the selected file. /// </summary> /// <value>The selected file.</value> public string SelectedFile { get; set; } }

 

In our Visual C++ COM object we instantiate and host the .NET user control as shown next

HRESULT MyNativeComponent::AddPages(IN LPFNADDPROPSHEETPAGE lpfnAddPage, IN LPARAM lParam) { HPROPSHEETPAGE hPage; PROPSHEETPAGE psp = {0}; auto retval = E_INVALIDARG; psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_DEFAULT; psp.hInstance = _AtlBaseModule.GetResourceInstance(); psp.pszTemplate = MAKEINTRESOURCE(IDD_EMPTYPAGE); psp.pszTitle = _T("My dialog's name goes here"); psp.pcRefParent = NULL; psp.lParam = reinterpret_cast<LPARAM>(new wstring(m_szFile.data())); psp.pfnDlgProc = [](HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)->LRESULT { switch (uiMsg) { case WM_INITDIALOG: CoInitialize(NULL); CComPtr<IDispatch> pUnk; CAxWindow container; auto psp = reinterpret_cast<PROPSHEETPAGE*>(lParam); shared_ptr<wstring> selectedFile(reinterpret_cast<wstring*>(psp->lParam)); container.Attach(hwnd); pUnk.CoCreateInstance(L"MyAssembly.MyFileControl", NULL); // We set the propery here!!! pUnk.PutPropertyByName(_bstr_t("SelectedFile"), &_variant_t(selectedFile->data())); container.AttachControl(pUnk, NULL); CoUninitialize(); pWndProc = (WNDPROC)SetWindowLongPtr(GetParent(hwnd), GWLP_WNDPROC, (LONG_PTR)&CustomWndProc); break; } return FALSE; }; if ((hPage = CreatePropertySheetPage(&psp)) != NULL) { if (!lpfnAddPage(hPage, lParam)) DestroyPropertySheetPage(hPage); retval = NOERROR; } return retval; }

Unit testing our Visual C++ code with Microsoft Unit Testing Framework

One of the things I love about unit testing (besides all of the well-known benefits) is the ability to test code without writing a tester or test harness application, but I can pretty much leverage what Microsoft have already built for us. Image below depicts an unit test I’ve written for this JNI .NET bridge or adapter to piggy back on existing libraries (adapters) provided by  Mulesoft 

image

More information here

Beware of where you call _CrtDumpMemoryLeaks

The Visual C++ compiler has a myriad of great features to build robust and high-quality software. A good example of this could be _CrtDumpMemoryLeaks function that allows to detect any memory leaks  that has occurred in the debug heap.  There is an entire section on debugging native code here.  The results produced by the function in question here (_CrtDumpMemoryLeaks) can be easily misinterpreted if the call to the function is not made in the right place, but let’s illustrate this with an example

1 #include "stdafx.h" 2 #include <iostream> 3 #include <string> 4 #include <memory> 5 #include <stdlib.h> 6 #include <crtdbg.h> 7 #define _CRTDBG_MAP_ALLOC 8 9 int _tmain(int argc, _TCHAR* argv[]) { 10 11 auto dodgyPtr = new int[5]; // Fail (delete is missing) 12 auto mySmartPtr = std::make_unique<int[]>(512); // Ok in "theory" 13 auto myString = std::string("This my test string"); // Ok in "theory" 14 15 _CrtDumpMemoryLeaks(); 16 17 return 0; 18 } 19

The produced output is and you’ll be like Disappointed smile why? if I’m using smart pointers and handling strings with std:: string class… Yes, I must admit it that I haven’t call delete operator but I knew I was leaking one pointer only but in modern C++ this should not happen!!!

1 Detected memory leaks! 2 Dumping objects -> 3 {202} normal block at 0x01046E08, 32 bytes long. 4 Data: <This my test str> 54 68 69 73 20 6D 79 20 74 65 73 74 20 73 74 72 5 {201} normal block at 0x01037748, 8 bytes long. 6 Data: <0 > 30 FB D8 00 00 00 00 00 7 {200} normal block at 0x010465C8, 2048 bytes long. 8 Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9 {199} normal block at 0x010411A8, 20 bytes long. 10 Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 11 Object dump complete. 12 The thread 0x29a4 has exited with code 0 (0x0). 13 The program '[11056] Temp.exe' has exited with code 0 (0x0). 14

Well, reason being is that the string and smart pointers are still “alive” when the call to _CrtDumpMemoryLeaks was made, therefore that memory allocation has not been freed because the destructor of the objects has not been invoked as yet. One of the nicest features in C++ is that the destruction of objects is deterministic, hence objects are destroyed once they go outside of scope. If we make minors changes to the code shown above we would then get a different result which is the correct one.

1 void AllocateMyObjectsInMemory() { 2 auto itemsInArray = 5; 3 auto dodgyPtr = new int[itemsInArray]; // Fail (remember to delete it) 4 auto mySmartPtr = std::make_unique<int[]>(512); // Ok 5 auto myString = std::string("This my test string"); // Ok 6 7 std::cout << "How many bytes am I leaking? " << 8 sizeof(int) * itemsInArray << std::endl; 9 } 10 11 int _tmain(int argc, _TCHAR* argv[]) { 12 13 AllocateMyObjectsInMemory(); 14 15 _CrtDumpMemoryLeaks(); 16 17 return 0; 18 } 19

And the newly produced output is accurate

1 Detected memory leaks! 2 Dumping objects -> 3 {199} normal block at 0x006311A8, 20 bytes long. 4 Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 5 Object dump complete. 6 The thread 0x1540 has exited with code 0 (0x0). 7 The program '[4632] Temp.exe' has exited with code 0 (0x0).

Why am I leaking 20 bytes? Well, an integer is 4 bytes * 5 (number of items in array)

image

Visual Studio also provides us with memory windows that allow us to view into the application’s memory space and it’s very useful when debugging

image

Easy and convenient way to convert Wide Characters to Multibyte with modern C++

One  of the most common tasks we developers have to do comes in the form of data conversion. A good example would be converting  void* (equivalent of Object in .NET) to a specific pointer of a different class through reinterpret_cast, this is however one example. Today’s post is about converting strings as Wide Characters to Multibyte using modern C++.

By modern C++, I mean using smart pointers instead of naked or raw pointers, string class and since it’s a functionality that will be used in different sections of code, it could be in the form of a function template or lambda, in this case, I’ll use a lambda as shown below

1 auto ConvertWstringToChar = [=](std::wstring& original) -> std::string { 2 std::string retval; 3 size_t cntConverted; 4 5 if (!original.empty()) { 6 wcstombs_s(&cntConverted, nullptr, NULL, original.c_str(), 7 original.size()); 8 9 auto buffer = std::make_unique<char[]>(cntConverted); 10 11 memset(buffer.get(), ' ', original.size()); 12 13 wcstombs_s(&cntConverted, buffer.get(), strlen(buffer.get()) + 1, 14 original.c_str(), original.size()); 15 16 retval.append(buffer.get()); 17 } 18 19 return retval; 20 }; 21 22 std::wstring test(L"This is my very funky string!!!!"); 23 auto converted = ConvertWstringToChar(test); 24 }

Some tools that every Visual C++ / .NET developer should have in their toolbox

m4s0n501

Hi Community,

This post is about some of the tools I use when building stuff or diagnosing misbehaving applications, so I thought it’d come handy to have them, or learn about them [in case you don’t know them]

Regards,

Angel

Native Windows Service Example

Hi Community,

This blog post describes and explains the steps required to build a native Windows Service. One of the projects I’m currently working on is about a metadata filesystem for Windows (Similar to what WinFS was supposed to be but different in many aspects, mainly because users will have a greater level of customization and it will be template driven). One of the artefacts required (besides Minifilter driver and managed .NET assemblies it’s in the form of a Windows Service).

Building Windows Service in .NET is pretty straightforward, mainly because the project template in Visual Studio does pretty much all of the scaffolding for developers, however, in native code using Visual C++ that template is completely missing and it’s up to developers to do it by themselves.

In my use scenario, the rationale for having a Windows Service is to interact with Minifilter driver. Please bear in mind, most of the stuff we developers build run in User mode and drivers and such do it in Kernel mode, thus the need of having a Windows Service. User and Kernel mode are isolated one from the other, but there are ways to exchange information between the two (if required).

image

The source code for Windows Service is below (please disregard InitializeClr method).

1 #include "stdafx.h" 2 3 // Global variables 4 SERVICE_STATUS g_SvcStatus = {0}; 5 SERVICE_STATUS_HANDLE g_svcHandle = NULL; 6 HANDLE g_filterEventHandle = INVALID_HANDLE_VALUE; 7 HANDLE g_svcStopNotification = INVALID_HANDLE_VALUE; 8 9 10 11 12 /// <summary> 13 /// _tmains the specified argc. 14 /// </summary> 15 /// <param name="argc">The argc.</param> 16 /// <param name="argv">The argv.</param> 17 /// <returns>int.</returns> 18 int _tmain(int argc, _TCHAR* argv[]) { 19 auto retval = 0; 20 21 InitializeClr(); 22 23 SERVICE_TABLE_ENTRY ServiceTable[] = {{SERVICE_NAME, ServiceMain}}; 24 25 if (!StartServiceCtrlDispatcher(ServiceTable)) { 26 WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to start MetaFSAgent Service.")); 27 retval = GetLastError(); 28 } 29 30 return retval; 31 } 32 33 34 /// <summary> 35 /// Services the controller. 36 /// </summary> 37 /// <param name="ctlCode">The control code.</param> 38 VOID WINAPI ServiceController(DWORD ctlCode) { 39 switch (ctlCode) { 40 case SERVICE_CONTROL_STOP: 41 if (g_SvcStatus.dwCurrentState == SERVICE_RUNNING) { 42 ConfigureService(ConfigOption::STOP_SERVICE); 43 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 44 SetEvent(g_filterEventHandle); 45 SetEvent(g_svcStopNotification); 46 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"MetaFS Agent Service stopped.\nVersion 1.0.0.0.\n\nChanges made to metadata on existing filesystem objects will not be monitored")); 47 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Stop Pending).")); 48 } 49 break; 50 } 51 } 52 53 /// <summary> 54 /// Services the main. 55 /// </summary> 56 /// <param name="argc">The argc.</param> 57 /// <param name="argv">The argv.</param> 58 VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { 59 HANDLE hThread; 60 61 if ((g_svcHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceController)) != NULL) { 62 ConfigureService(ConfigOption::START_SERVICE); 63 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 64 if ((g_svcStopNotification = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL) { 65 ConfigureService(ConfigOption::RUNNING_SERVICE); 66 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 67 InitializeCommunicationWithDriver(); 68 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"MetaFS Agent Service started.\nVersion 1.0.0.0")); 69 hThread = CreateThread(NULL, NULL, AsyncFilterWorker, NULL, NULL, NULL); 70 WaitForSingleObject(hThread, INFINITE); 71 CloseHandle(g_svcStopNotification); 72 ConfigureService(ConfigOption::STOPPED_SERVICE); 73 SetServiceStatus(g_svcHandle, &g_SvcStatus); 74 CloseHandle(hThread); 75 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Running).")); 76 } else { 77 WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to CreateEvent(g_ServiceStopEvent) for MetaFSAgent Service.")); 78 ConfigureService(ConfigOption::STOP_SERVICE); 79 SetServiceStatus(g_svcHandle, &g_SvcStatus); 80 } 81 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Started).")); 82 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to start MetaFSAgent Service.")); 83 } 84 85 /// <summary> 86 /// Asynchronouses the filter worker. 87 /// </summary> 88 /// <param name="lpParam">The lp parameter.</param> 89 /// <returns>DWORD.</returns> 90 DWORD WINAPI AsyncFilterWorker(LPVOID lpParam) { 91 while (WaitForSingleObject(g_svcStopNotification, INFINITE) != WAIT_OBJECT_0) { 92 // Has there been changes to the filesystem? 93 if (WaitForSingleObject(g_filterEventHandle, INFINITE) != WAIT_OBJECT_0) { 94 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"Notification from driver...")); 95 SetEvent(g_filterEventHandle); 96 } 97 } 98 return ERROR_SUCCESS; 99 } 100 101 /// <summary> 102 /// Configures the service. 103 /// </summary> 104 /// <param name="option">The option.</param> 105 VOID ConfigureService(ConfigOption option) { 106 switch (option) { 107 case ConfigOption::START_SERVICE: 108 ZeroMemory(&g_SvcStatus, sizeof(g_SvcStatus)); 109 g_SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 110 g_SvcStatus.dwControlsAccepted = 0; 111 g_SvcStatus.dwCurrentState = SERVICE_START_PENDING; 112 g_SvcStatus.dwWin32ExitCode = 0; 113 g_SvcStatus.dwServiceSpecificExitCode = 0; 114 g_SvcStatus.dwCheckPoint = 0; 115 break; 116 117 case ConfigOption::STOP_SERVICE: 118 g_SvcStatus.dwControlsAccepted = 0; 119 g_SvcStatus.dwCurrentState = SERVICE_STOPPED; 120 g_SvcStatus.dwWin32ExitCode = GetLastError(); 121 g_SvcStatus.dwCheckPoint = 1; 122 break; 123 124 case ConfigOption::RUNNING_SERVICE: 125 g_SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 126 g_SvcStatus.dwCurrentState = SERVICE_RUNNING; 127 g_SvcStatus.dwWin32ExitCode = 0; 128 g_SvcStatus.dwCheckPoint = 0; 129 break; 130 131 case ConfigOption::STOPPED_SERVICE: 132 g_SvcStatus.dwControlsAccepted = 0; 133 g_SvcStatus.dwCurrentState = SERVICE_STOPPED; 134 g_SvcStatus.dwWin32ExitCode = 0; 135 g_SvcStatus.dwCheckPoint = 3; 136 } 137 } 138 139 /// <summary> 140 /// Initializes the CLR. 141 /// </summary> 142 VOID InitializeClr() { 143 HINSTANCE hInstance; 144 145 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); 146 147 if ((hInstance = LoadLibraryEx(INTEROP_LIBRARY, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) != NULL) { 148 ptrInitializeClr funcPtr = (ptrInitializeClr)GetProcAddress(hInstance, "InitializeClr"); 149 funcPtr(); 150 } 151 152 SetErrorMode(NULL); 153 154 FreeLibrary(hInstance); 155 } 156 157 /// <summary> 158 /// Initializes communication with driver 159 /// </summary> 160 VOID InitializeCommunicationWithDriver() { 161 HANDLE hFile; 162 DWORD cbSize; 163 164 if ((hFile = CreateFile(TARGET_DRIVER, GENERIC_READ | GENERIC_WRITE, 165 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) { 166 if ((g_filterEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL) { 167 if (DeviceIoControl(hFile, IOCTL_REGISTER_EVENT, &g_filterEventHandle, sizeof(g_filterEventHandle), NULL, NULL, &cbSize, NULL)) 168 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"Event successfully created....")); 169 } 170 CloseHandle(hFile); 171 } 172 } 173 174 /// <summary> 175 /// Writes the event. 176 /// </summary> 177 /// <param name="ei">The ei.</param> 178 VOID WriteEvent(const EventInformation& ei) { 179 HANDLE hEventLog; 180 LPWSTR messages[1] = {(WCHAR*)ei.Message.c_str()}; 181 182 if ((hEventLog = OpenEventLog(NULL, L"Application")) != NULL) { 183 ReportEvent(hEventLog, ei.Type, 0x1, 0x1, NULL, 1, NULL, (LPCWSTR*)messages, NULL); 184 CloseEventLog(hEventLog); 185 } 186 }

Regards,

Angel

Interoperability between Java World and Microsoft World by implementing a MSMQ Bridge in Visual C++

 

Hi Community,

As Developers, consultants or architects we always have to face challenges when it comes to integrating to existing systems. This is a bit harder when technologies aren’t from the same vendor. This blog post describes an IPC mechanism I had to implement for an integration project that comprised Microsoft technologies and Java.

The beauty of C++ is that one can virtually build anything with it, and almost every technology or platform provides means to be extended through the use of C++. Java is not exception because it provides JNI that allows running native code from the JVM. This would be the equivalent of the DllImportAttribute in .NET.

MSMQ provides reliable delivery of messages and it also provides a SDK that enables developers to implement queues in their applications, below code snippet to delete a queue

1 2 QueueOpResult CMSMQBridge::DeleteQueue(int nQueueId) { 3 HRESULT ret; 4 QueueOpResult retval; 5 retval.isSuccess = false; 6 wchar_t szFormatName[MAX_PATH]; 7 DWORD cbFormatLength = MAX_PATH; 8 hash_map<int, QueueInformation>::const_iterator found = Queues.find(nQueueId); 9 10 if (Queues.size() > 0 || found != Queues.end()) { 11 auto pathName = found->second.Queuepath.c_str(); 12 auto result = MQPathNameToFormatName(pathName, &szFormatName[0], &cbFormatLength); 13 if (SUCCEEDED((ret = MQDeleteQueue(szFormatName)))) { 14 retval.hr = S_OK; 15 retval.isSuccess = true; 16 Queues.erase(found); 17 } else { 18 CreateFailedQueueOpResult(retval, ret); 19 } 20 } else { 21 retval.isSuccess = false; 22 retval.errMessage = L"Queue is empty or Queue Id wasn't found"; 23 } 24 25 return retval; 26 }

Once we run “DeleteQueue” unit test and set a breakpoint inside the method, we can clearly see that our library is interacting with MSMQ effectively

image

So the way all pieces hang together is as follows:

  • Native Visual C++ Library provides an interface which is consumed from Java through JNI (source code here)
  • Java sends messages to Microsoft application using a queue.
  • .NET application provides classes to interact and use MSMQ (e.g.: MessageQueue class).

And once again, Visual C++ has saved the day Smile

Angel

Building a Visual Studio Add-In To Leverage WinDBG and SOS

 

Hi Community,

I am currently building an Visual Studio add-in to leverage SOS and make it more user friendly, thus I have called it Visual SOS. On this blog post I will describe the main debugging interfaces that are required as well as an artefact called SOS Wrapper which encapsulates the functionality as a native DLL that is invoked from the add-in which is written in C# effectively.

Windows has a built-in debugger engine, and like with every other feature in the  operating system, it also provides means for the developers to build code around it. The debugger engine API is COM based, so every interface required has IUnknown as the base interface.

In Visual SOS, we require to use the following interfaces:

  • IDebugOutputCallbacks:  This interface is called by the engine and it allows to send output from the client to the IDebugOutputCallbacks object that is registered with the client.
  • IDebugEventCallbacks: This interface is called by the engine and it allows to subscribe to events fired by the debugging engine.

The way Visual SOS works is as follows:

  • The entire core functionality is encapsulated in C++ library that exposes a few functions as “extern
  • The add-in code is written in its entirety in C#/.NET but it makes calls to the native library, this is the same approach I took when building Memory Inspector a few years back.
  • Notifications are done using a message sink in the form of a callback or delegate that is passed from managed code to native code, similar to the technique I used and described here

The native functionality lives in SOSWrapper, below its Initialize method

1 void SOSWrapper::Initialize() { 2 if (!m_bIsInitialized) { 3 CoInitialize(NULL); 4 5 if (SUCCEEDED(DebugCreate(__uuidof(IDebugClient), (void**)&m_pDbgClient))) { 6 if (SUCCEEDED(m_pDbgClient->QueryInterface(__uuidof(IDebugControl), (void**)&m_pDbgControl))) { 7 8 // Load extensions 9 std::for_each(m_configReader.Extensions_get().begin(), 10 m_configReader.Extensions_get().end(), 11 [&, this](ExtInformation& item) { m_pDbgControl->AddExtension(item.Path.data(), NULL, &item.pHandle); }); 12 13 m_pDbgClient->SetOutputCallbacks(&m_pOutputCallback); 14 m_pDbgClient->SetEventCallbacks(&m_pEventCallback); 15 16 if (SUCCEEDED(LoadSos())) 17 m_bIsInitialized = TRUE; 18 } 19 } 20 CoUninitialize(); 21 } 22 }

Initialize method

 

Visual SOS  implements a XML-based config file which allows to specify what debugger extensions to load. The way it parses this config file is described here. The implementation of the required debugger engine interfaces are:

1 #include "../../dbghelp/inc/dbgeng.h" 2 3 class EventCallback : public IDebugEventCallbacks { 4 5 private: 6 long m_ref; 7 8 public: 9 EventCallback(); 10 11 ~EventCallback(); 12 13 // IUnknown 14 STDMETHOD_(ULONG, AddRef)(); 15 STDMETHOD_(ULONG, Release)(); 16 STDMETHOD(QueryInterface)(__in REFIID InterfaceId, __out PVOID* Interface); 17 18 // IDebugEventCallbacks 19 STDMETHOD(ExitThread)(__in ULONG ExitCode); 20 STDMETHOD(SessionStatus)(__in ULONG Status); 21 STDMETHOD(ExitProcess)(__in ULONG ExitCode); 22 STDMETHOD(GetInterestMask)(__out PULONG Mask); 23 STDMETHOD(Breakpoint)(__in PDEBUG_BREAKPOINT Bp); 24 STDMETHOD(SystemError)(__in ULONG Error, __in ULONG Level); 25 STDMETHOD(ChangeEngineState)(__in ULONG Flags, __in ULONG64 Argument); 26 STDMETHOD(ChangeSymbolState)(__in ULONG Flags, __in ULONG64 Argument); 27 STDMETHOD(ChangeDebuggeeState)(__in ULONG Flags, __in ULONG64 Argument); 28 STDMETHOD(UnloadModule)(__in_opt PCSTR ImageBaseName, __in ULONG64 BaseOffset); 29 STDMETHOD(Exception)(__in PEXCEPTION_RECORD64 Exception, __in ULONG FirstChance); 30 STDMETHOD(CreateThread)(__in ULONG64 Handle, __in ULONG64 DataOffset, __in ULONG64 StartOffset); 31 STDMETHOD(LoadModule)(__in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, 32 __in_opt PCSTR ModuleName, __in_opt PCSTR ImageName, __in ULONG CheckSum, __in ULONG TimeDateStamp); 33 STDMETHOD(CreateProcess)(__in ULONG64 ImageFileHandle, __in ULONG64 Handle, __in ULONG64 BaseOffset, 34 __in ULONG ModuleSize, __in_opt PCSTR ModuleName, __in_opt PCSTR ImageName, __in ULONG CheckSum, 35 __in ULONG TimeDateStamp, __in ULONG64 InitialThreadHandle, __in ULONG64 ThreadDataOffset, 36 __in ULONG64 StartOffset); 37 };

EventCallback.h

1 #include "stdafx.h" 2 #include "EventCallback.h" 3 4 EventCallback::EventCallback() { 5 m_ref = 1; 6 7 } 8 9 EventCallback::~EventCallback() { 10 11 } 12 13 STDMETHODIMP EventCallback::QueryInterface(__in REFIID InterfaceId, __out PVOID* Interface) { 14 *Interface = NULL; 15 if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacks))) { 16 *Interface = (IDebugEventCallbacks *)this; 17 InterlockedIncrement(&m_ref); 18 return S_OK; 19 } else { 20 return E_NOINTERFACE; 21 } 22 } 23 24 STDMETHODIMP_(ULONG) EventCallback::AddRef() { 25 return InterlockedIncrement(&m_ref); 26 } 27 28 STDMETHODIMP_(ULONG) EventCallback::Release() { 29 if (InterlockedDecrement(&m_ref) == 0) { 30 delete this; 31 return 0; 32 } 33 return m_ref; 34 } 35 36 37 38 STDMETHODIMP EventCallback::ExitThread(__in ULONG ExitCode) { 39 return S_OK; 40 } 41 42 STDMETHODIMP EventCallback::SessionStatus(__in ULONG Status) { 43 return S_OK; 44 } 45 46 STDMETHODIMP EventCallback::ExitProcess(__in ULONG ExitCode) { 47 return S_OK; 48 } 49 50 STDMETHODIMP EventCallback::GetInterestMask(__out PULONG Mask) { 51 auto retval = S_OK; 52 53 if (Mask != nullptr) 54 *Mask = DEBUG_EVENT_BREAKPOINT; 55 56 return retval; 57 } 58 59 60 STDMETHODIMP EventCallback::Breakpoint(__in PDEBUG_BREAKPOINT Bp) { 61 return DEBUG_STATUS_BREAK; 62 } 63 64 STDMETHODIMP EventCallback::SystemError(__in ULONG Error, __in ULONG Level) { 65 return S_OK; 66 } 67 68 69 STDMETHODIMP EventCallback::ChangeEngineState(__in ULONG Flags, __in ULONG64 Argument) { 70 return S_OK; 71 } 72 73 STDMETHODIMP EventCallback::ChangeSymbolState(__in ULONG Flags, __in ULONG64 Argument) { 74 return S_OK; 75 } 76 77 STDMETHODIMP EventCallback::ChangeDebuggeeState(__in ULONG Flags, __in ULONG64 Argument) { 78 return S_OK; 79 } 80 81 STDMETHODIMP EventCallback::UnloadModule(__in_opt PCSTR ImageBaseName, __in ULONG64 BaseOffset) { 82 return S_OK; 83 } 84 85 STDMETHODIMP EventCallback::Exception(__in PEXCEPTION_RECORD64 Exception, __in ULONG FirstChance) { 86 return S_OK; 87 } 88 89 90 STDMETHODIMP EventCallback::CreateThread(__in ULONG64 Handle, __in ULONG64 DataOffset, __in ULONG64 StartOffset) { 91 return S_OK; 92 } 93 94 STDMETHODIMP EventCallback::LoadModule(__in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, 95 __in_opt PCSTR ModuleName, __in_opt PCSTR ImageName, __in ULONG CheckSum, 96 __in ULONG TimeDateStamp) { 97 98 return S_OK; 99 } 100 101 102 STDMETHODIMP EventCallback::CreateProcess(__in ULONG64 ImageFileHandle, __in ULONG64 Handle, __in ULONG64 BaseOffset, 103 __in ULONG ModuleSize, __in_opt PCSTR ModuleName, __in_opt PCSTR ImageName, __in ULONG CheckSum, 104 __in ULONG TimeDateStamp, __in ULONG64 InitialThreadHandle, __in ULONG64 ThreadDataOffset, 105 __in ULONG64 StartOffset) { 106 107 return S_OK; 108 }

EventCallback.cpp

 

In summary, the debugger engine is available and it allows building cool stuff around it. I’ll keep you posted on the development/progress of this add-in, so stay tuned Smile

Angel

Microsoft Visual C++ MVP Award for 2015

Hi community,

It gives me great pleasure to announce that Microsoft has awarded me with an MVP recognition once again in the Visual C++ category. This is my tenth consecutive time as an MVP award recipient. Wow!!! Who woulda have imagined, MVP award recipient for a decade :-) I am thrilled and excited about this, because it is hard to be awarded for the first time even harder to continue being awarded the following years. Before, I continue writing this blog post I would like to thank God and his son Jesus in the first place, Microsoft for valuing and appreciating my time, effort and contributions, Developer community because you are the reason for the MVP program to exist, and by helping you I help myself , hence the insatiable appetite for knowledge and ideas sharing is always there, and last but not least, to my family, my wife and two daughters, thanks for your understanding and patience with me whenever I can’t go out to finish writing or presenting something… Thank-you all!!!

Image (13)

In this opportunity, I will not say or mention how awesome or powerful C++ is because you must know it already, what I would love to do though is to reflect on the past 10 years as an MVP, how the program has helped lots of people (including me) and what lies ahead with native code and C++.

The first time I ever gave a talk was back in 2002 for the Visual FoxPro community in Venezuela, then I started to present for the MUG communities all along Venezuela and South America. To me, communities represented my playground, a place where I could talk in “Geek” language, share experiences with peers and learn, at the same time it helped me to get rid of my introvert-self… Yes, I was an introvert but the need to present helped me overcome it and I became an extrovert.

I was awarded the first time back in January 2006 and I have been an MVP recipient since then. I came to Australia in February 2008 because a consultancy firm based in Sydney CBD found my blog, and they were keen enough to sponsor me, seven years later I can proudly say that I’m Venezuelan-Australian and my children our Australian. Why made me any special back then for this Australian company sponsor me here? My status as an MVP.

To me being an MVP is more important than any recognition, because it is about my contributions towards society as an individual, a good example of this is how my inputs and comments are taken into consideration by the evolution working group of the ISO committee (C++ Language). Some people ask me about whether I get paid by Microsoft or why haven’t I joined Microsoft and similar questions. My answer pretty much is the same… I rather be an independent vendor and/or consultant who enjoys doing research, learning and sharing my experiences. It is more fun, trust me.

I used to spend a lot of time in forums and newsgroups (long before Stackoverflow even existed) and NNTP was the only channel available to seek and provide help. In recent years, I have been the kind of “blogging” dude instead of being in forums, reason being is that like everything in life, it was one stage I had to go through, learn from it and move on. I find blogging more interesting… Heaps more.

I must have been 16 when MIcrosoft released Microsoft C/C++ 7.0 (and it introduced MFC 1.0) and I learned every book available about C and C++. In those days, I also experienced a bit of assembler, even when it was a great learning experience it could have been much better if someone like an MVP was available, but the MVP program was introduced a year later in 1993 and CompuServe was not available in Venezuela either.

20 something years have passed, and I am still passionate about C++ and programming in general like I was back in 1992 (to those wondering about my age, I am 38 years old as I type this – 1976 model, and I have dedicated my life to computers since age 12).

Moving on… What lies ahead for native code and C++… Interesting question, and I will try to be nonbiased on this one. C++ has been evolving since its conception and the language has become better and better, but not only the language itself but the tools. Native code has not gone anywhere, it has always been there (if you have a PC, Mac, Mobile device, etc. whatever the OS is running, it is native). Managed languages are great for RAD and common development tasks, but if you want to have full access to hardware, bare metal and be in control of your program and how it behaves, then native code and C++ is the way to go.

C and C++ were one of the first portable languages (long before any VM, CLR or similar) and they will always be in demand. Mobility is considered by many the new computing wave, and as a developer if you want to make the most of your device battery, well, C++ is the right choice which by the way, in Visual Studio 2015 will allow cross-platform development, so as you can all see. C++ and native code are here to stay.

Once again thank-you all and I look forward to contributing to community this year 2015.

Angel