需要在 C# 应用中使用C++代码'block'

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

本文关键字:C++ 代码 block 应用      更新时间:2023-10-16

我得到了一个 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);
}

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

谢谢Kevin

我认为你有三个选择:

  1. 创建一个类库类型的托管C++项目,将代码放入其中,从主应用引用此项目。
  2. 创建一个非托管C++ DLL 项目,将代码放入一个或多个函数中,导出函数(使用 .def 文件(,然后生成项目。使用[DllImport]属性使用该 dll 中的函数。(见这里和这里(
  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;
    }
}

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

让我知道它是否有效。

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

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

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

如果您在此处发布代码,可能会有一些好人为您将代码从 C++ 移植到 C#。但是,为了将来在处理从 .NET 应用程序中使用本机C++代码时参考,可以使用 .NET 框架的互操作服务来引用本机 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(。等(。标记为导出并使用调用约定__stdcall extern "C"声明的函数将始终附加@X其中X是函数参数的字节数(因此对于上面的示例,如果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.

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

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

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

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

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