Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

Posted in Uncategorized | 1 Comment

Windows Mobile Heaps from the Large Memory Area

I recently worked on a project porting a Windows desktop application to a Windows Mobile 5 device and a Windows Mobile 6.1 device. Both Windows Mobile 5 and Windows Mobile 6.x are based on Windows CE 5.0. In Windows CE 5.0 and previous versions, the memory management is quite different than Windows XP and Vista. This is documented quite well on MSDN and Doug Boling’s book “Programming Microsoft Windows CE .NET” (3rd Edition). His latest book titled “Programming Windows Embedded CE 6.0” is the 4th edition addresses Windows CE 6.0 where Microsoft changed the memory management. Previous to Windows CE 6.0 applications are limited to 32 MB of usable virtual address space per process. I am not going to describe the memory management of Windows CE/Mobile devices as the following links provide complete information.

Windows CE .NET Advanced Memory Management
http://msdn.microsoft.com/en-us/library/ms836325.aspx

Effective Memory, Storage, and Power Management in Windows Mobile 5.0
http://msdn.microsoft.com/en-us/library/aa454885.aspx

Doug Boling’s Windows CE Blog
Windows Mobile 6.1 Memory Management Changes
http://bolingconsulting.com/blog/?p=25

Most applications are not hindered by the 32 MB virtual address space per process architecture in Windows CE/Mobile devices; however, the application that I was porting required a large amount of memory to hold information and I quickly exhausted the 32 MB address space of my process yet had ample memory on the device (128 MB). Fortunately, Windows CE/Mobile does provide a way for an application to tap into additional address space, and therefore, additional memory. In Windows CE/Mobile requested memory allocations that are greater than 2 MB are allocated from the large memory area (0x40000000-0x7E000000). So for my porting project I created another heap in the large memory area and allocated the memory as needed.

I wanted to improve on the implementation used in the project and develop a set of APIs that would allow the automatic management of heaps that would use the virtual address space of the large memory area. These APIs along with C++ classes would allow an application designer to take advantage of virtual address space of the large memory area without having to handle the maintenance.

I created APIs to allow the user to allocate memory, reallocate memory, free memory and compact the heap(s) located in the large memory area. In an embedded device memory can become fragmented quickly, compacting the heap(s) combine adjacent free blocks of memory and decommiting large free blocks of memory. I also created a CString derived class that allocates memory from the large memory area. The application I was porting also used container template classes in the Standard C++ library (std::vector, std::list, etc.), so I also created an allocator to be used with these templates.

The private class CWMHeap manages the heaps created in the large memory area. A CWMHeap object is created by the global variable ‘g_WMHeap’. When the WMHeapAlloc function is called and no heaps exist a heap is created and the memory is allocated from that heap. If one or more heaps already exist, an allocation request is made in the existing heaps prior to creating a new heap.

Let’s look at some code that uses the WMHeap APIs.

MyBuffers myBuffers; // vector to hold pointers to buffers
// Allocate 35 1MB buffers;
DWORD dwBufferSize = 1024 * 1024;
for (int nIndex = 0; nIndex < 35; ++nIndex)
{
    // Allocate a 1MB buffer from the Large Memory Area.
    char* pBuffer = reinterpret_cast<char*>(WMHeapAlloc(0, dwBufferSize));
    if (NULL != pBuffer)
    {
        // Fill the buffer.
        memset(pBuffer, 'A' + nIndex, dwBufferSize);

        // Save the buffer pointer onto the vector.
        myBuffers.push_back(pBuffer);
    }
    else
    {
        TCHAR szMsg[100];
        _stprintf(szMsg, _T("Error allocating memory at index %d."), nIndex);
        MessageBox(NULL, szMsg, _T("Error"), MB_OK);
    }
}

// Reallocate the 34th buffer, increasing its size by 256 bytes.
dwBufferSize += 256;
char* pBuffer = reinterpret_cast<char*>(WMHeapReAlloc(0, myBuffers.at(33), dwBufferSize));
if (NULL != pBuffer)
{
    // Save the new pointer (as it may have changed) into the vector.
    myBuffers.at(33) = pBuffer;

    // Fill the memory.
    char ch = 'A';
    for (DWORD dwIndex = 0; dwIndex < dwBufferSize; ++dwIndex)
    {
        pBuffer[dwIndex] = ch;
        if (++ch > 'Z')
            ch = 'A';
    }
}
else
{
    // Note: The original buffer is still valid.
    MessageBox(NULL, _T("Unable to reallocate memory."), _T("Error"), MB_OK);
}

// Free all memory allocated from the Large Memory Area.
for (MyBuffersIter iBuffer = myBuffers.begin();
(iBuffer != myBuffers.end());
++iBuffer)
{
    WMHeapFree(*iBuffer);
}
myBuffers.clear(); // erase all entries in the vector.

// Compact the heap(s).
WMHeapCompact();

The first part allocates 35 1MB buffers. This causes two additional heaps to be created since an individual heap can have a maximum size of 32 MB. You can see this by using the Visual Studio Remote Heap Walker tool. In the image below you can see the local heap at 0x18050000 and two additional heaps at 0x50000000 and 0x52000000 for the process.

The 34th buffer is increased by 256 bytes. This causes a new allocation to be made, the contents of the first buffer copied to the new buffer and then the original allocation is freed. This causes memory fragmentation and can be seen using the Remote Heap Walker tool that comes with Visual Studio. In the image below the right window contains a listing of the allocations in the heap at 0x52000000 (pre-realloc) and the left window contains a listing of the allocations in the heap (post-realloc). As you can see the allocation at 0x52200080 (the 34th buffer) has been freed and a new allocation at 0x524000A0 has been made.

After all of the buffers have been freed and the heaps are compacted, the heaps are destroyed since there are no allocations. This is shown in the image below; note that only the local heap (0x18050000) exists for the process.

The next snip of code demonstrates the use of the WMHeapAllocator template class. As elements are pushed onto the vector memory is allocated from one of the managed heaps using the WMHeap APIs.

// Define a vector type to hold a series of values of type 'short'.
// The vector uses memory from the Large Memory Area.
typedef std::vector< short, WMHeapAllocator< short > > MyShorts;
MyShorts myShorts1;     // vector to store values

myShorts1.push_back(1);
myShorts1.push_back(2);
myShorts1.push_back(3);
myShorts1.push_back(4);

MyShorts myShorts2;     // vector to store values

myShorts2.reserve(5);   // allocate memory for five entries
myShorts2.push_back(5);
myShorts2.push_back(6);
myShorts2.push_back(7);
myShorts2.push_back(8);
myShorts2.push_back(9);

Vectors in a Windows CE/Mobile device should be used with the knowledge that unless the memory is reserved prior to pushing elements onto the vector, memory fragmentation will occur. This is demonstrated in the code snip above. The myShorts1 vector will allocate memory for the first element when it is pushed onto the vector. When the second element is pushed onto the vector, the vector allocates a new block of memory to hold two elements, the first element is then copied to the new allocation and the original allocation is freed (allocation at 0x50000060 in the image below). This continues as elements are pushed onto the vector. By reserving the vector memory up front only one allocation is made (0x50000080 in the image below) and memory is not fragmented and the operation is quicker.

The next snip of code demonstrates the use of the CWMHeapString class. The CWMHeapString class is derived from CString and allocates its memory using the WMHeap APIs.

CWMHeapString myEmptyString;                // an empty string
CWMHeapString myHelloString(_T("HELLO"));   // from a C string literal
CWMHeapString myCopyString = myHelloString; // copy constructor
CWMHeapString myExpString(myHelloString     // string expression
    + _T(" ") + myCopyString);
CWMHeapString myCharString(_T('x'));        // = "x"
CWMHeapString myCharRepString(_T('x'), 6);  // = "xxxxxx"
VARIANT var;
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = ::SysAllocString(L"Coding is fun.");
CWMHeapString myVarString(var);             // = "Coding is fun."

// The following statement does not call the assignment operator.
// The compiler considers the following statement equivalent to
// CWMHeapString myString("Hello World")
CWMHeapString myString = _T("Hello World");

The private CWMHeap will manage a seperate heap for strings by creating a heap and attaching the heap to a CWin32Heap object and allowing an CAtlStringMgr object to manage the heap.

DWORD CWMHeap::CreateStringHeap()
{
    DWORD dwError = ERROR_SUCCESS;
    // In Windows Mobile 5.0 the maximum amount that can be committed at a
    // time is 32MB. If more than 32MB is needed multiple heaps will need to
    // be created.
    // http://msdn.microsoft.com/en-us/library/aa908768.aspx
    //
    // Create a heap with an initial size of 4K and a maximum size of 32MB.
    m_hStringHeap = ::CeHeapCreate(0, 0x00001000 /* 4K */, 0x02000000 /* 32MB */,
    CWMHeap::AllocMemory, CWMHeap::FreeMemory);
    if (NULL == m_hStringHeap)
    {
        dwError = GetLastError();
    }
    else
    {
        // Attach to the heap and allow CWin32Heap to take ownership.
        m_StringHeap.Attach(m_hStringHeap, TRUE);

        // Set the CString memory manager to use the heap.
        m_StringMgr.SetMemoryManager(&m_StringHeap);
    }

    return dwError;
}

For most applications the local heap of the process will suffice; however, for those applications that require allocations that would exceed the 32 MB virtual address space these APIs will allow your applications to create additional heaps that utilize the large memory area of the Windows CE virtual address space.

The complete source (Visual Studio 2008 solution) can be downloaded using the link below.

http://cid-e7e4366eac188131.office.live.com/self.aspx/Live Writer folder/WinMobileHeap.zip

Posted in Computers and Internet | 6 Comments

Windows Live Writer – Link to SkyDrive

When I decided to start my blog I decided to use Windows Live Spaces and Windows Live Writer. I knew I wanted to provide my zipped up source so I decided to use Windows Live SkyDrive. I discovered that I did not need to use a Windows Live Writer (WLW) plug-in to provide a link to my zip file residing in my SkyDrive account.

To embed a link to a file in your SkyDrive account into your blog entry follow these instructions.

  1. The file that you intend to link to in your Windows Live Space must reside in a public folder.
    I created a new folder named ‘Live Writer folder’, marked it public and then added my file to the folder.
  2. Using a browser, such as Windows Internet Explorer, navigate to the folder in your SkyDrive account.
    You should see your file.
  3. Click on the file you intend to link to in your blog entry.
    In the lower right corner of the window you will see information about the file.
  4. Select the text in the ‘Embed’ text box.
  5. Copy the text into the clipboard.
  6. Open your blog entry with Windows Live Writer.
  7. From the ‘Edit’ menu select ‘Paste special…’.
  8. On the ‘Paste Special’ form select ‘HTML’ and click ‘OK’.

A link to your SkyDrive file will now be embedded in your blog entry.

Using a WLW plug-in to embed links to your SkyDrive files in your blog entry allows you to link to files in your SkyDrive account without the need of using an Internet browser; everything can be accomplished from within Windows Live Writer; however, I have found that the plug-ins that are available do not work with the latest version of Windows Live Writer. More about that in another post.

Posted in Computers and Internet | Leave a comment

Accessing a Native Code Object from Managed Code

I was working on a C# application to load and validate embedded devices with multiple firmware configurations. The code to actually load the firmware onto the devices is native C++ code and is object oriented; which means classes are involved. Platform Invoke, also known as P/Invoke, is a .NET feature to call functions in native DLLs; however, interacting with native code objects from managed code is outside the realm of Platform Invoke. So I began to investigate how to interact with native objects from managed code. I began reading about C++/CLI (which replaced Managed C++). I picked up two great books on the subject: C++/CLI In Action by Nishant Sivakumar and Expert C++/CLI: .NET for Visual C++ Programmers by Marcus Heege and began to develop a sample application to test my theories on how to accomplish my end goal: using a managed code user interface that interacts with exported naive C++ objects contained in DLLs.

I started by creating a normal C++ class (CNativeClass) that was structured the same as the class that I would ultimately use in my final project. This was so that I could not only limit the amount of code I would need to develop for the sample, but also to minimize the complexity of my sample to test my theories. The CNativeClass exposes methods to add two values and return the result and to multiply two values and return the result. You can’t get any simpler than that. I structured the callback implementation exactly as it was implemented in the real class. Since I cannot change the C++ code in the native DLL, I must ensure that my managed code will interface to the callback so that the managed code can be called from the native code during the operation. You can think of this as while the firmware is being downloaded to the device (native code) it would be nice to update a progress bar in the user interface (managed code) to indicate the amount of time left to complete the operation.

#pragma once

#include "tchar.h"
#include "StringX.h"

// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the NATIVECLASS_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// NATIVECLASS_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef NATIVECLASS_EXPORTS
#define NATIVECLASS_API __declspec(dllexport)
#else
#define NATIVECLASS_API __declspec(dllimport)
#endif

// This class is exported from the NativeClass.dll
class NATIVECLASS_API CNativeClass
{
public:

    enum Operation
    {
        none,
        addition,
        multiplication
    };

    enum ResultCode
    {
        success,
        aborted,
        fail_unknown,
        fail_os
    };

    // Result object
    class NATIVECLASS_API Result
    {
    public:
        Result(const Result &result);
        Result(ResultCode code = success, const TCHAR *detail = _T(""));

        // Assignment operators
        Result& operator = (ResultCode code);
        Result& operator = (const Result &result);

        // Accessor functions
        ResultCode GetCode() const { return code; }
        CStringX GetDetail() const { return detail; }

    private:

        // The status data
        ResultCode code;
        CStringX detail;

        // Obtain textual version of an operating system error
        static CStringX GetOSError();
    };

    struct NATIVECLASS_API Status
    {
        Status();

        Result result;
    };

    // Callback object
    class Callback
    {
    public:

        Callback() {}
        virtual ~Callback() {}

        // Callback function
        virtual void Progress(CNativeClass* pNativeClass, const Status& status) {}
    };

private:
    // Implementation
    Callback*	m_pCallback;	// Pointer to a Callback object.

    Operation	m_operation;	// Operation processed.
    int	m_nValue1;				// Value 1 of the operation.
    int m_nValue2;				// Value 2 of the operation.

public:
    // Normally I would recommend that all variables be private or protected
    // and that member access functions be created; however, in an effort to
    // show how to access public member variables this variable is public.
    int m_nResult;

public:
    CNativeClass();

    // Add two values and return the result.
    int AddValues(int nValue1, int nValue2);

    // Multiple two values and return the result.
    int MultiplyValues(int nValue1, int nValue2);

    // Callback control
    void SetCallback(Callback* pCallback = 0) { m_pCallback = pCallback; }

    // Member variable access methods.
    Operation GetOperation() { return m_operation; }
    int GetValue1() { return m_nValue1; }
    int GetValue2() { return m_nValue2; }
};

Now that the DLL was complete I wanted to start at a point which I knew would work to verify my native DLL. So I created a regular C++/MFC dialog based application (MFCAccess) to interface to my native DLL. As you would expect, this went effortlessly.

So on to the challenge. The key about C++/CLI is that C++/CLI provides the most powerful and convenient interop functionality of any CLI language. To bridge the gap between managed code and native code I developed a wrapper using C++/CLI that contains both managed and native code intermixed within the class (NativeClassWrapper). The wrapper is defined as its own C++ CLR Class Library project within the solution. As you can see the NativeClassWrapper defines a pointer to a CNativeClass object (pNativeClass) and contains managed code elements as well.

// NativeClassWrapper.h

#pragma once

#include <vcclr.h>
#include <msclr\auto_gcroot.h>
#include "..\NativeClass\NativeClass.h"

using namespace System;
using namespace System::Runtime::InteropServices;

// CLR Class Library project.

namespace NativeClassWrapper_CLR
{
    // Forward class declaration.
    class CNativeClassCallback;

    public ref class NativeClassWrapper
    {
    public:
        enum class Operation
        {
            none,
            addition,
            multiplication
        };

        enum class ResultCode
        {
            success,
            aborted,
            fail_unknown,
            fail_os
        };

        ref class Result
        {
        public:
            // Constructors
            Result();
            Result(const Result% result);
            Result(ResultCode code, const TCHAR* detail);

            // Assignment operators
            Result^ operator = (ResultCode code);
            Result^ operator = (const Result^ result);

            // Accessor functions
            ResultCode GetCode() { return NativeClassWrapper::Result::code; }
            String^ GetDetail() { return NativeClassWrapper::Result::detail; }

        private:
            // The status data
            ResultCode code;
            String^ detail;
        };

        ref struct Status
        {
            // Constructor
            Status();
            Status(Result^ result);

            // Data
            Result^ result;
        };

        // Callback object
        interface class ICallback
        {
            // Callback function
            void Progress(Status^% status);
        };

    public:
        NativeClassWrapper();
        ~NativeClassWrapper();

    protected:
        // Finalizer
        !NativeClassWrapper();

    public:
        int AddValues(int nValue1, int nValue2)
        { return pNativeClass->AddValues(nValue1, nValue2); }

        int MuliplyValues(int nValue1, int nValue2)
        { return pNativeClass->MultiplyValues(nValue1, nValue2); }

        // Callback control
        void SetCallback();
        void SetCallback(ICallback^ callback);

        Operation GetOperation();
        int GetValue1() { return pNativeClass->GetValue1(); }
        int GetValue2() { return pNativeClass->GetValue2(); }
        int GetResult() { return pNativeClass->m_nResult; }

    private:
        // Points to an instance of the unmanaged CNativeClass class.
        CNativeClass* pNativeClass;

        // Points to an instance of the unmanged CNativeClassCallback class.
        CNativeClassCallback* pNativeClassCallback;
    };

    public class CNativeClassCallback : public CNativeClass::Callback
    {
    private:
        // Handle to a manged interface.
        msclr::auto_gcroot<NativeClassWrapper::ICallback^> managedCallbackInterface;

    public:
        CNativeClassCallback(NativeClassWrapper::ICallback^ managedCallbackInterface);

        // NativeClass callback functions
        virtual void Progress(CNativeClass* pNativeClass,
            const CNativeClass::Status& nativeStatus);
    };
}

Of course the development of the native code wrapper needed to be developed and debugged using a managed code interface – out goes the comfortable MFC dialog based application. I chose to start with developing a C++ CLR Windows Forms Application (CLRAccess). This is C++/CLI code, just like the wrapper code, but the key difference is that the Windows Forms application is strictly a managed application that interfaces with the wrapper which interfaces with the native code, thereby bridging the gap from managed code to native code. This allowed me to test the wrapper before jumping to the final destination, a managed C# application that uses the C++/CLI wrapper to interface with the native code.

I am not going to explain the C++ CLR Windows Forms Application (CLRAccess) since once you see how all this works by reviewing the C# application you should be able to understand the CLR version easily.

Below is the C# application (CSharpAccess) form. Like I said, I wanted to keep it simple. The ‘Add’ button will take the two supplied values and output the result in the edit box, provide the textual formula and provide the status; which in the case of the ‘Add’ function will always be ‘Success: Values were calculated.’ The ‘Multiply’ button will take the two supplied values and output the result in the edit box, provide the textual formula and provide the status; which in the case of the ‘Multiply’ function will always be ‘OS Failure: There was an operating system error.’; however, this message can be ignored. The values are calculated correctly, I just wanted to verify that the NativeClassWrapper is functioning properly when there is an error condition.

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

// Add reference
// Solution Explorer -> References
using NativeClassWrapper_CLR;

// C# Windows Application project.

// Change 'Output path' from bin\Debug\ to ..\Debug
// This is so that the manifest for the NativeClassWrapper DLL can be found
// so that the MSVCR80.DLL and MSVCM80.DLL files can be located.
// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=98744
// http://www.ddj.com/windows/184406482

// Delegates
// http://msdn.microsoft.com/en-us/magazine/cc301810.aspx

namespace CSharpAccess
{
    public partial class CSharpAccessForm : Form, NativeClassWrapper.ICallback
    {
        private NativeClassWrapper nativeClassWrapper;

        public CSharpAccessForm()
        {
            InitializeComponent();
        }

        // http://msdn.microsoft.com/en-us/library/8903062a.aspx
        // http://msdn.microsoft.com/en-us/library/6w96b5h7(VS.80).aspx

        public void Progress(ref NativeClassWrapper.Status status)
        {
            string operationString = "";
            switch (nativeClassWrapper.GetOperation())
            {
                case NativeClassWrapper.Operation.addition:
                    operationString = string.Format("{0} + {1} = {2}",
                        nativeClassWrapper.GetValue1(),
                        nativeClassWrapper.GetValue2(),
                        nativeClassWrapper.GetResult());
                    break;

                case NativeClassWrapper.Operation.multiplication:
                    operationString = string.Format("{0} * {1} = {2}",
                        nativeClassWrapper.GetValue1(),
                        nativeClassWrapper.GetValue2(),
                        nativeClassWrapper.GetResult());
                    break;
            }
            operationLabel.Text = operationString;

            switch (status.result.GetCode())
            {
                case NativeClassWrapper.ResultCode.success:
                    callbackLabel.Text = "Success";
                    break;

                case NativeClassWrapper.ResultCode.aborted:
                    callbackLabel.Text = "Aborted";
                    break;

                case NativeClassWrapper.ResultCode.fail_unknown:
                    callbackLabel.Text = "Unknown Failure";
                    break;

                case NativeClassWrapper.ResultCode.fail_os:
                    callbackLabel.Text = "OS Failure";
                    break;
            }
            messageLabel.Text = status.result.GetDetail();
        }

        private void CSharpAccessForm_Load(object sender, EventArgs e)
        {
            operationLabel.Text = "";
            callbackLabel.Text = "";
            messageLabel.Text = "";

            // Create handle to native class wrapper.
            nativeClassWrapper = new NativeClassWrapper();
            nativeClassWrapper.SetCallback(this);
        }

        private void addButton_Click(object sender, EventArgs e)
        {
            int nValue1 = Convert.ToInt32(addValue1TextBox.Text);
            int nValue2 = Convert.ToInt32(addValue2TextBox.Text);

            int result = nativeClassWrapper.AddValues(nValue1, nValue2);
            addResultTextBox.Text = result.ToString();
        }

        private void multiplyButton_Click(object sender, EventArgs e)
        {
            int nValue1 = Convert.ToInt32(multiplyValue1TextBox.Text);
            int nValue2 = Convert.ToInt32(multiplyValue2TextBox.Text);

            int result = nativeClassWrapper.MuliplyValues(nValue1, nValue2);
            multiplyResultTextBox.Text = result.ToString();
        }

        private void closeButton_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}

Now let’s take a look at the C# code and see how it interfaces to the wrapper class and the native code. When the form is loaded (CSharpAccessForm_Load) a NativeClassWrapper object is created. The NativeClassWrapper constructor is called which creates a CNativeClass object at which time the CNativeClass constructor is called. The NativeClassWrapper is responsible for maintaining the pointer to the native object.

        private void CSharpAccessForm_Load(object sender, EventArgs e)
        {
            operationLabel.Text = "";
            callbackLabel.Text = "";
            messageLabel.Text = "";

            // Create handle to native class wrapper.
            nativeClassWrapper = new NativeClassWrapper();
            nativeClassWrapper.SetCallback(this);
        }

Next the callback is set by calling the SetCallback method of the NativeClassWrapper class. The NativeClassWrapper::SetCallback method takes as a parameter a handle to an ICallback interface. Since the CSharpAccessForm is defined to support the ICallback interface of the NativeClassWrapper we can use the ‘this’ keyword in the call to reference the current object. In the NativeClassWrapper::SetCallback method a CNativeClassCallback object is created which holds a handle to the CSharpAccessForm object (managedCallbackInterface). A callback is then set in the CNativeClass object to call back to the CNativeClassCallback object which is derived from CNativeClass::Callback. As you can see, the NativeClassWrapper is the middle man in the callback operation.

    void NativeClassWrapper::SetCallback(ICallback^ callbackInterface)
    {
        pNativeClassCallback = new CNativeClassCallback(callbackInterface);
        pNativeClass->SetCallback(pNativeClassCallback);
    }

When the ‘Add’ button is clicked the AddValues method of the NativeClassWrapper class is called, which in turn calls the AddValues method of the CNativeClass class; where the computation is made, a status is set, and the callback is triggered.

int CNativeClass::AddValues(int nValue1, int nValue2)
{
    // Set the operation.
    m_operation = addition;

    // Remember the passed values.
    m_nValue1 = nValue1;
    m_nValue2 = nValue2;

    // Calculate the result.
    m_nResult = nValue1 + nValue2;

    // Set the status
    CNativeClass::Status status;
    status.result = Result(success, _T("Values were calculated."));

    // Trigger a callback
    if (m_pCallback)
        m_pCallback->Progress(this, status);

    return m_nResult;
}

The member variable m_pCallback points to a Callback object. Therefore, the call to Progress calls back to the CNativeClassCallback::Progress method which then calls the Progress method in CSharpAccessForm. This little member function here demonstrates the power of C++/CLI. The first parameter is a pointer to the native CNativeClass object and the second parameter is a reference to a CNativeClass::Status object. The method then uses the reference to the CNativeClass::Status object to create a managed version (NativeClassWrapper::Status) to pass into the managed code callback. Notice that a pointer to the native CNativeClass object is passed into the callback but never used. The parameter must exist due to the callback definition in the original non-modifiable native code, but since the pointer to the CNativeClass object is being managed by the wrapper class, the parameter can be ignored. Furthermore, the pointer is irrelevant when calling back into the managed code object.

    // Callback function
    void CNativeClassCallback::Progress(CNativeClass* pNativeClass,
        const CNativeClass::Status& nativeStatus)
    {
        managedCallbackInterface->Progress(
            gcnew NativeClassWrapper::Status(gcnew NativeClassWrapper::Result(
            (NativeClassWrapper::ResultCode) nativeStatus.result.GetCode(),
            nativeStatus.result.GetDetail())));
    }
}

In the managed code (CSharpAccessForm) I wanted to demonstrate the ability to retrieve data from the native object; hence the calls to GetOperaton, GetValue1, GetValue2, and GetResult.

        public void Progress(ref NativeClassWrapper.Status status)
        {
            string operationString = "";
            switch (nativeClassWrapper.GetOperation())
            {
                case NativeClassWrapper.Operation.addition:
                    operationString = string.Format("{0} + {1} = {2}",
                        nativeClassWrapper.GetValue1(),
                        nativeClassWrapper.GetValue2(),
                        nativeClassWrapper.GetResult());
                    break;

                case NativeClassWrapper.Operation.multiplication:
                    operationString = string.Format("{0} * {1} = {2}",
                        nativeClassWrapper.GetValue1(),
                        nativeClassWrapper.GetValue2(),
                        nativeClassWrapper.GetResult());
                    break;
            }
            operationLabel.Text = operationString;

            switch (status.result.GetCode())
            {
                case NativeClassWrapper.ResultCode.success:
                    callbackLabel.Text = "Success";
                    break;

                case NativeClassWrapper.ResultCode.aborted:
                    callbackLabel.Text = "Aborted";
                    break;

                case NativeClassWrapper.ResultCode.fail_unknown:
                    callbackLabel.Text = "Unknown Failure";
                    break;

                case NativeClassWrapper.ResultCode.fail_os:
                    callbackLabel.Text = "OS Failure";
                    break;
            }
            messageLabel.Text = status.result.GetDetail();
        }

So there you have it. A managed application using C++/CLI to interface to a native object developed in C++. This application provided the proof-of-concept that I needed to design and develop the real managed application that would interact with an existing native DLL. Hopefully, my example will help others understand how they can interact with existing native DLLs that exports C++ classes.

In addition to the two books I previously mentioned, I also consulted the following articles on the Internet.

Quick C++/CLI – Learn C++/CLI in less than 10 minutes

.NET An Introduction to Delegates – Jeffrey Richter

The complete source (Visual Studio 2005 solution) can be downloaded using the link below.

http://cid-e7e4366eac188131.office.live.com/self.aspx/Live Writer folder/CSharpCLINativeTest.zip

Posted in Computers and Internet | Leave a comment