Necesita usar un 'bloque' de código C++ en la aplicación C#

Me dieron un bloque de código C++ que parece que era de una aplicación C++ que utiliza memoria compartida para enviar mensajes a otros programas.

El código C++ no tiene #include ni nada todavía. Me dieron el código para usar en mi aplicación C# y estoy bastante atascado. Entiendo un poco lo que hace el código, pero no lo sé lo suficientemente bien como para traducirlo a C#, ya que soy bastante nuevo en la codificación.

Mi pregunta es, ¿cuál es la forma más fácil de poder usar la funcionalidad del código en mi proyecto? El resultado final es enviar mensajes a otro programa, que a su vez hará algo que no me preocupa.

Intenté crear diferentes proyectos y tipos de archivos de C++ en mi solución para vincularlos usando una referencia más adelante, pero nunca logré que se compilara correctamente.

Por favor, hágamelo saber si tiene algún consejo o un buen lugar para buscar. Siempre puedo proporcionar más información.

código (yo tenido para eliminar los comentarios, lo siento):

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);
}

EDITAR: Los comentarios han sido completamente irreales. Debo decir que la comunidad mima a la gente por aquí.

Gracias kevin

preguntado el 22 de mayo de 12 a las 16:05

Esta es una q muy amplia y es probable que se cierre sin más detalles, pero en términos generales, podría portar (convertir) el código a la sintaxis de C# o envolverlo para su uso. DE C# -

¡Gracias por todas las excelentes respuestas! Estoy leyendo y tomando tiempo para tratar de entender todas sus sugerencias. -

Vuelva a colocar el código en su pregunta para referencia futura. Ayuda a personas con problemas similares. -

4 Respuestas

Creo que tienes tres opciones:

  1. Cree un proyecto de C++ administrado de tipo Biblioteca de clases, coloque el código en él, haga una referencia desde su aplicación principal a este proyecto.
  2. Cree un proyecto DLL de C++ no administrado, coloque el código en una función (o funciones), exporte la(s) función(es) (usando el archivo .def) y compile el proyecto. Use las funciones de ese dll usando [DllImport] atributo. (Ver aquí y aquí)
  3. Convierte el código a C#. Esto requerirá algún conocimiento de código no administrado, Win32 y P/Invoke (Ver aquí y aquí). Y como veo su código, ¡toma un poco de tiempo!

Aquí está su código convertido (opción 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;
    }
}

Hice una conversión exacta y creo que debería funcionar bien, sin embargo, no la he probado.

Déjame saber si funciona.

contestado el 24 de mayo de 12 a las 05:05

El único problema fue el argumento 'ulong dwNumberOfBytesToMap' en la declaración MapViewOfFile. En este momento, parece que el mensaje pasado incluye todos los valores 0, por lo que ahora estoy trabajando en eso. - mantener

@kevp: Lo siento, mi error. Debe ser uint (el equivalente de SIZE_T) no ulong. Lo arreglé. - mohammad dehghan

hola y gracias por la ayuda Tengo un ultimo problema. El mensaje se pasa al programa externo, excepto que no obtiene datos de la estructura. El programa solo recibe WM_HELO_ZOOM_XYZ(0,0,0) cuando veo el registro de depuración. Tengo que volver a escribir el código C++ e intentaré recuperarlo pronto. Perdón por quitarlo, pensé que estaba bien (nuevo aquí) - mantener

@kevp: ¿Quiere decir que el programa externo recibe todos los ceros del mensaje que se envía con PostMessage? No le hemos pasado nada al mensaje, por lo que es obvio. ¿O quieres decir pHELOCoords ¿La estructura que se llena desde pvHead (que a su vez se llena con MapViewOfFile) está completamente vacía? - mohammad dehghan

@kevp: ¡Creo que fue mi error otra vez! ¡Me perdí el hecho de que el objeto modificado debe volver a escribirse! Lo corregí y también respondí tu otra pregunta al respecto. - mohammad dehghan

Puede volcar el código de C++ en un proyecto de Visual C++ y compilarlo. Cuando lo construya, vaya a la configuración del proyecto y seleccione la opción que genera archivos tlb (es una clase de proxy para interoperabilidad c ++/.net, no recuerdo el nombre de la opción).

Una vez que tenga esto, puede agregar una referencia al ensamblaje de interoperabilidad tlb desde un proyecto de C#.

Además, busque aquí un ejemplo de Microsoft http://msdn.microsoft.com/en-us/library/fx82zhxa.aspx

contestado el 22 de mayo de 12 a las 17:05

Lo más probable es que si publica el código aquí, alguna buena persona transferirá el código de C++ a C# por usted. Sin embargo, para futuras referencias cuando se trate del uso de código C++ nativo desde una aplicación .NET, puede usar InteropServices del marco .NET para hacer referencia a funciones nativas en dll nativos.

Para hacer esto, se requieren algunos pasos tanto en el lado de C++ como en el de C#. En primer lugar, debe crear su punto de entrada como una función exportada en C++.

Por ejemplo, digamos que quería escribir una función trivial de C++ para sumar 2 números y luego llamarla desde una aplicación de C#, tendría que hacer lo siguiente:

Paso 1: escribir la función C++. Para que las fuentes externas localicen sus funciones, debe informar al compilador que la función se va a 'exportar'. Un punto a tener en cuenta es que si estás llamando otros funciones desde dentro de su función exportada, no necesita marcarlas todas como exportadas.

Así que escribamos la función "agregar" en C++:

#define DLLEXPORT extern "C" declspec(dllexport) 

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

La primera línea define una macro que usaremos para marcar los métodos exportados. El extern "C" parte le dice al compilador que evite manipular el nombre exportado de la función (por lo que siempre será 'agregar', y no algo como @YZadd_), luego viene la definición de la función marcada como DLLEXPORT. Antes de continuar, hay un punto más sobre la 'manipulación de nombres' en funciones exportadas, y eso es para funciones declaradas __stdcall o cualquiera de sus variantes (WINAPI..etc). Funciones que están marcadas para exportar y declaradas con extern "C" con la convención de llamada __stdcall siempre se agregará con @X sin que importe X es el número de bytes de los parámetros de la función (así que para el ejemplo anterior si add fue declarado __stdcall entonces el nombre de la función exportada sería add@8. Solo tenga esto en cuenta si C# alguna vez tiene problemas para localizar sus funciones.

Ahora, el lado C++ de las cosas está hecho, compílelo como un archivo DLL y muévase a C#.

En C# es bastante sencillo importar funciones externas. Primero deberá hacer referencia a la InteropServices espacio de nombres:

using System.Runtime.InteropServices;

Y luego tendrás que hacer un [DllImport] declaración:

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

Siempre que los nombres de las funciones coincidan, eso debería ser todo lo que se requiere para importar la función. Ahora puedes llamar add tal como lo haría con cualquier función normal en C#

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

Eso concluye cómo simplemente exportar funciones de C++ y llamarlas desde una aplicación de C#.

contestado el 22 de mayo de 12 a las 17:05

Bien, logré que este ejemplo funcionara. Ahora vamos a la memoria compartida! Gracias - mantener

Si no puede hacer que el código de C++ funcione como está, entonces no tiene sentido intentar injertarlo en su aplicación de C#.

Primero averigüe el código C++ (lea la documentación de MSDN para las API utilizadas, pregúntele a la persona que le dio el código, publique preguntas específicas). Una vez que lo comprenda mejor y pueda hacerlo funcionar, tendrá una mejor oportunidad de descubrir la mejor manera de hacer lo que se necesita en C#.

contestado el 22 de mayo de 12 a las 17:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.