2

我得到了一段 c++ 代码,它看起来像是来自一个 c++ 应用程序,它利用共享内存向其他程序发送消息。

c++ 代码还没有#include 或任何东西。我获得了在我的 C# 应用程序中使用的代码,但我被卡住了。我有点理解代码的作用,但我不太了解将它翻译成 C#,因为我对编码很陌生。

我的问题是,能够在我的项目中使用代码功能的最简单方法是什么?最终结果是将消息发送到另一个程序,这反过来会做一些我不担心的事情。

我曾尝试在我的解决方案中创建不同的 c++ 项目和文件类型,以便稍后使用参考链接它们,但我永远无法正确编译它。

如果您有什么建议或有什么好去处,请告诉我。我总是可以提供更多信息。

代码(我不得不删除评论,对不起):

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

编辑:反馈完全不真实。我必须说社区肯定会宠坏这里的人。

谢谢,凯文

4

4 回答 4

4

我认为你有三个选择:

  1. 创建一个类库类型的托管 C++ 项目,将代码放入其中,从您的主应用程序中引用该项目。
  2. 创建一个非托管 C++ DLL 项目,将代码放入一个(或多个函数)中,导出函数(使用 .def 文件),然后构建项目。使用该 dll 中的函数使用[DllImport]属性。(见这里这里
  3. 将代码转换为 C#。这将需要一些非托管代码、Win32 和 P/Invoke 的知识(参见此处此处)。当我看到你的代码时,这需要一点时间!

这是您转换后的代码(选项 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;
    }
}

我已经进行了精确的转换,我认为它应该可以正常工作,但是我还没有测试过。

让我知道它是否有效。

于 2012-05-22T16:22:46.683 回答
1

您可以将 C++ 代码转储到 Visual C++ 项目中并构建它。构建时,进入项目设置并选择生成 tlb 文件的选项(它是 c++/.net 互操作的代理类,我不记得选项的名称)。

完成此操作后,您可以从 C# 项目添加对 tlb 互操作程序集的引用。

此外,请在此处查找 Microsoft 示例http://msdn.microsoft.com/en-us/library/fx82zhxa.aspx

于 2012-05-22T16:05:47.547 回答
1

很有可能,如果您在此处发布代码,一些好心人会为您将代码从 C++ 移植到 C#。但是,为了将来在处理在 .NET 应用程序中使用本机 C++ 代码时参考,您可以使用 .NET 框架的 InteropServices 来引用本机 dll 中的本机函数。

为此,需要在 C++ 和 C# 方面执行一些步骤。首先,您需要将入口点构建为 C++ 中的导出函数。

例如,假设我想编写一个简单的 C++ 函数来将 2 个数字相加,然后从 C# 应用程序中调用它,我必须执行以下操作:

第 1 步:编写 C++ 函数。为了让外部资源找到您的函数,您需要让编译器知道该函数将被“导出”。需要注意的一点是,如果您从导出的函数中调用其他函数,则不需要将它们全部标记为已导出。

所以让我们用 C++ 编写“add”函数:

#define DLLEXPORT extern "C" declspec(dllexport) 

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

第一行定义了一个宏,我们将使用它来标记导出的方法。该extern "C"部分告诉编译器避免破坏函数的导出名称(因此它总是'add',而不是@YZadd_之类的东西),接下来是标记为DLLEXPORT的函数定义。在我继续之前,还有一点关于导出函数中的“名称修改”,即声明的函数__stdcall或其任何变体(WINAPI..etc)。标记为导出并extern "C"使用调用约定 __stdcall 声明的函数将始终附加@XwhereX是函数参数的字节数(因此对于上面的示例,如果add已声明__stdcall,则导出的函数名称将是add@8. 如果 C# 在定位您的函数时遇到问题,请记住这一点。

现在,C++ 方面已经完成,将其编译为 DLL 并转移到 C#。

在 C# 中,导入外部函数相当简单。首先,您需要引用InteropServices命名空间:

using System.Runtime.InteropServices;

然后你需要做一个[DllImport]声明:

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

如果函数名称匹配,这应该是导入函数所需的全部内容。现在您可以add像调用 C# 中的任何普通函数一样调用

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

关于如何简单地导出 C++ 函数并从 C# 应用程序调用它们的总结。

于 2012-05-22T16:22:08.277 回答
0

如果您无法让 C++ 代码按原样工作,那么尝试将其移植到您的 C# 应用程序中是没有意义的。

首先找出 C++ 代码(阅读 MSDN 文档以了解所使用的 API,询问提供代码的人,发布具体问题)。一旦您更好地理解它并使其工作,那么您将有更好的机会找出在 C# 中执行所需操作的最佳方法。

于 2012-05-22T16:07:25.453 回答