kevp
kevp

Reputation: 377

Need to use a 'block' of C++ code in C# app

I was given a block of c++ code that looks like it was from a c++ app that makes use of Shared Memory for sending messages to other programs.

The c++ code has no #include or anything yet. I was given the code to use in my C# application and I am pretty stuck. I somewhat understand what the code does, but I don't know it well enough to translate it to C# as I am pretty new to coding.

My question is, what is the easiest way to be able to use the functionality of the code in my project? The end result is to send messages to another program, that will in turn do something that I'm not worried about.

I have tried creating different c++ projects and file types in my solution to link them using a reference later on, but I can never get it to compile properly.

Please let me know if you have some advice or a good place to look. I can always provide more information.

Code (I had to remove comments, sorry):

UINT WM_HELO_ZOOM_XYZ = RegisterWindowMessage("WM_HELO_ZOOM_XYZ");


int HELO_Broadcast_Zoom_Message(
  double dbX,
  double dbY,
  double dbZ,
  UINT uMessage=WM_HELO_ZOOM_XYZ) {

  #ifndef HELO_ 
    typedef struct { 
      UINT uMajVersion; 
      UINT uMinVersion; 
      DWORD dwReserved;
      double dbX;  
      double dbY;
      double dbZ;
    } HELOCoordsStruct;
  #endif


  char *szSharedMemory = "HELO-_Coords"; 
  char szErr[_MAX_PATH*3];
  HANDLE hMem = OpenFileMapping(FILE_MAP_WRITE, FALSE, szSharedMemory); 
  if (NULL == hMem) {
    return(0);
  }

  void *pvHead = MapViewOfFile(hMem, FILE_MAP_WRITE, 0,0,0);
  if (NULL == pvHead)  {
    CloseHandle(hMem);
    sprintf(szErr, "Unable to view", szSharedMemory);
    AfxMessageBox(szErr, MB_OK|MB_ICONSTOP);
    return(0);
  }

  HELOCoordsStruct *pHELOCoords = (HELOCoordsStruct *)pvHead;

  BOOL bVersionOk=FALSE; 

  if (1 == pHELOCoords->uMajorVersion) {

    if (WM_HELO_ZOOM_XYZ==uMessage) { 
      pHELOCoords->dbX = dbX;
      pHELOCoords->dbY = dbY;
      pHELOCoords->dbZ = dbZ;
    }
    bVersionOk=TRUE;
  }
  else {

    sprintf(szErr, "Unrecognized HELO- shared memory version: %d.%d", pHELOCoords->uMajVersion, pHELOCoords->uMinVersion);
    AfxMessageBox(szErr, MB_OK);
  }

  if (NULL != hMem) CloseHandle(hMem);
  UnmapViewOfFile(pvHead);

  if (bVersionOk) {
    PostMessage(HWND_BROADCAST,uMessage,0,0); 
    return(1); 
  }
  else return(0);
}

EDIT: The feedback has been completely unreal. I must say that the community sure spoils folks around here.

Thanks, Kevin

Upvotes: 2

Views: 1764

Answers (4)

Moha Dehghan
Moha Dehghan

Reputation: 18443

I think you have three options:

  1. Create a Managed C++ project of type Class Library, put the code in it, make a reference from your main app to this project.
  2. Create an unmanaged C++ DLL project, put the code in a function (or functions), export the function(s) (using .def file), and build the project. Use the functions from that dll using [DllImport] attribute. (See here and here)
  3. Convert the code to C#. This will require some knowledge of unmanaged code, Win32 and P/Invoke (See here and here). And as I see your code, it is takes a little time!

Here is your converted code (option 3):

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace UnmanagedBlock
{
    public class ConvertedClass
    {
        public uint WM_HELO_ZOOM_XYZ = RegisterWindowMessageA("WM_HELO_ZOOM_XYZ"); // Your code uses the ANSI string

        int HELO_Broadcast_Zoom_Message(
            double dbX, double dbY, double dbZ, uint uMessage) // Porting the default value for 'uMessage' is not possible
        {
            string szSharedMemory = "HELO-_Coords";
            IntPtr hMem = OpenFileMapping(FileMapAccessRights.Write, FALSE, szSharedMemory);
            if (IntPtr.Zero == hMem)
                return 0;
            IntPtr pvHead = MapViewOfFile(hMem, FileMapAccessRights.Write, 0, 0, UIntPtr.Zero);
            if (IntPtr.Zero == pvHead)
            {
                CloseHandle(hMem);
                MessageBox.Show(
                    "Unable to view " + szSharedMemory, // Your code does not concat these two strings.
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return 0;
            }

            HELOCoordsStruct pHELOCoords = new HELOCoordsStruct();
            Marshal.PtrToStructure(pvHead, pHELOCoords);

            int bVersionOk = FALSE;

            if (1 == pHELOCoords.uMajVersion) // I think it had a typo (it was uMajorVersion)
            {
                if (WM_HELO_ZOOM_XYZ == uMessage)
                {
                    pHELOCoords.dbX = dbX;
                    pHELOCoords.dbY = dbY;
                    pHELOCoords.dbZ = dbZ;
                }
                Marshal.StructureToPtr(pHELOCoords, pvHead, false);
                bVersionOk = TRUE;
            }
            else
            {
                MessageBox.Show(
                    "Unrecognized HELO- shared memory version: " +
                    pHELOCoords.uMajVersion.ToString() + "." + pHELOCoords.uMinVersion.ToString());
            }

            if (IntPtr.Zero != hMem)
                CloseHandle(hMem);
            UnmapViewOfFile(pvHead);

            if (bVersionOk == TRUE)
            {
                PostMessage(HWND_BROADCAST, uMessage, 0, 0);
                return 1;
            }
            else
                return 0;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class HELOCoordsStruct
        {
            public uint uMajVersion;
            public uint uMinVersion;
            public uint dwReserved;
            public double dbX;
            public double dbY;
            public double dbZ;
        }

        [DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        public static extern uint RegisterWindowMessageW([In]string lpString);

        [DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        public static extern uint RegisterWindowMessageA([In]string lpString);

        [DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
        public static extern IntPtr OpenFileMapping(FileMapAccessRights dwDesiredAccess, int bInheritHandle, [In]String lpName);

        [DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
        public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccessRights dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);

        [DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
        public static extern int UnmapViewOfFile(IntPtr lpBaseAddress);

        [DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
        public static extern int CloseHandle(IntPtr hObject);

        [DllImport("user32.dll")]
        public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

        public const int FALSE = 0, TRUE = 1;

        public enum FileMapAccessRights : uint
        {
            Write = 0x2,
            Read = 0x4,
            Execute = 0x20,
        }

        public const IntPtr HWND_BROADCAST = (IntPtr)0xffff;
    }
}

I've done an exact conversion and I think that it should work fine, however I have not tested it.

Let me know if it works.

Upvotes: 4

Jason Larke
Jason Larke

Reputation: 5599

Chances are that if you post the code here, some nice person will port the code from C++ to C# for you. However, for future reference when dealing with using native C++ code from within a .NET application, you can use the InteropServices of the .NET framework to reference native functions in native dlls.

To do this requires a few steps on both the C++ and C# side of things. Firstly you need to build your entry point as an exported function in C++.

For example, say I wanted to write a trivial C++ function to add 2 numbers together and then call it from a C# app, I would have to do the following:

Step 1: Writing the C++ function. In order for external sources to locate your functions, you need to let the compiler know that the function is to be 'exported'. A point to note is that if you're calling other functions from within your exported function, you do not need to mark them all as exported.

So let's write the "add" function in C++:

#define DLLEXPORT extern "C" declspec(dllexport) 

DLLEXPORT int __cdecl add(int x, int y)
{
    return (x + y);
}

The first line defines a macro we'll use to mark exported methods. The extern "C" part tells the compiler to avoid mangling the exported name of the function (so it will always be 'add', and not something like @YZadd_), next comes the function definition marked as a DLLEXPORT. Before I continue there's one more point on the 'name mangling' in exported functions, and that is for functions declared __stdcall or any of its variations (WINAPI..etc). Functions that are marked for exporting and declared with extern "C" with the calling convention __stdcall will always be appended with @X where X is the number of bytes of the function paramaters (so for the above example if add was declared __stdcall then the exported function name would be add@8. Just keep this in mind if C# ever has trouble locating your functions.

Now, the C++ side of things is done, compile that as a DLL and move over to C#.

In C# it's rather straightforward to import external functions. First you'll need to reference the InteropServices namespace:

using System.Runtime.InteropServices;

And then you will need to make a [DllImport] declaration:

[DllImport(@"path to your C++ dll here")]
public static extern int add(int x, int y) //make sure the function definition matches.

Provided the function names match, that should be all that's required to import the function. Now you can call add just as you would any normal function in C#

int x = 5;
int y = 10;
int z = add(x, y); //z should be 10

That about concludes how to simply export C++ functions and call them from a C# application.

Upvotes: 1

bames53
bames53

Reputation: 88155

If you can't get the C++ code to work as-is then there's no point trying to graft it into your C# app.

Figure out the C++ code first (read MSDN documentation for the APIs used, ask the person that gave you the code, post specific questions). Once you understand it better and can make it work then you'll have a better chance of figuring out the best way to do what's needed in C#.

Upvotes: 0

Jaimal Chohan
Jaimal Chohan

Reputation: 8645

You can dump the C++ code into a Visual C++ project and build that. When you build it, go into the project settings and select the option that generates tlb files (It's a proxy class for c++/.net interop, I can't remember the name of the option).

Once you have this, you can add a reference to the tlb interop assembly from a C# project.

Also, look here for a Microsoft example http://msdn.microsoft.com/en-us/library/fx82zhxa.aspx

Upvotes: 1

Related Questions