【问题标题】:How to marshalling a function with (char* param[]) in C#如何在 C# 中使用 (char* param[]) 编组函数
【发布时间】:2012-09-19 12:22:34
【问题描述】:

我在本机 dll 中有一个函数,其函数如下所示

int GetActiveNames(char* names[]);

如何在 C# 中为返回字符串/字符数组的上述函数创建我的 PInvoke 包装器方法?


为了更具描述性..

我尝试了 P/Invoke 方式并显示以下错误 尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

我只是剥离了实际的 C 函数签名以使其简单。但是这里是完整的C函数签名和函数参数的描述

 int OpalGetActiveProjects(unsigned short allocatedProjects,
unsigned short *numProjects,
unsigned short allocatedModels,
unsigned short *numModels,
unsigned short allocatedProjectsPathLen,
unsigned short *maxProjectPathLen,
unsigned short allocatedModelPathLen,
unsigned short *maxModelPathLen,
unsigned short allocatedMachineNameLen,
unsigned short *maxMachineNameLen,
unsigned short allocatedIpLen,
unsigned short *maxIpLen,
char *projectPaths[],
unsigned short instanceIDs[],
char *machineNames[],
char *controllerIPs[],
char *modelPaths[],
unsigned short numModelsPerProject[]
unsigned short modelIDs[],
unsigned short modelStates[]);

该函数返回活动项目的列表。对于每个项目,此函数返回配置文件的完整路径、打开该项目的机器的名称和 IP 地址、当前项目会话的实例 ID 以及属于该项目的模型列表。 对于每个模型,返回路径、ID 和状态。如果分配的存储对于任何参数来说太小,则不返回任何列表,但会设置 numProjects、numModels、maxProjectPathLen、maxModelPathLen、maxMachineNameLen 和 maxIpLen 值,并且返回值为 E2BIG。应用程序可以使用此事实在第一次调用时将长度指定为 0,分配所需的存储空间并发出第二次调用以获取信息。


输入参数


allocatedProjects:分配存储空间的项目数 路径、机器名、IP 地址的调用者 和实例 ID。 allocatedModels:分配存储空间的模型数量 名称、路径和实例 ID 的调用者。 allocatedProjectPathLen:为接收项目路径分配的存储长度 allocatedModelPathLen:为接收模型路径分配的存储长度 allocatedMachineNameLen:分配的存储长度以接收机器名称 allocatedIpLen:为接收 IP 地址分配的存储长度


输出参数


numProjects:指向 API 将存储实际活动项目数的位置的指针。 numModels: 指向 API 将存储实际总数的位置的指针 所有活动项目的模型数量。 ma​​xProjectPathLen: 指向 API 将存储最长项目路径长度的位置的指针。 ma​​xModelPathLen: 指向 API 将存储最长模型路径长度的位置的指针。 ma​​xMachineNameLen: 指向 API 将存储最长机器名称长度的位置的指针。 ma​​xIpLen: 指向 API 存储长度的指针 最长的IP地址。 projectPaths: 字符串数组,API 将在其中存储活动项目配置文件的路径。 instanceIDs: 数组,API 将在其中存储活动项目的实例 ID。 ma​​chineNames: API 将存储机器名称的数组。 controllerIPs: API 将存储 IP 地址的数组。 modelPaths: 数组,API 将存储所有活动项目的模型路径。 numModelsPerProject: 数组,API 将在其中存储每个项目的模型数量。使用这些值来确定什么 型号属于什么项目。 modelIDs: 数组,API 将在其中存储所有模型的 ID 活跃的项目。 modelStates: API 将存储所有活动项目的模型状态的数组。

还有一个 C 程序使用 OpalGetActiveProjects() 函数来获取 projectPaths、machineNames 等。

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

// Header for getcwd() is platform-dependent
#if defined(WIN32)
#include <direct.h>
#include <windows.h>
#elif defined(__linux__)
#include <unistd.h>
#define _MAX_PATH 256
 #endif
 #include "OpalApi.h"

void    PrintError(int rc, char *funcName);

 #define FALSE 0

 #define TRUE 1

 int main (int argc, char* argv[])
 {
     char                                **projectPath, **modelPath;
     char                                *projectPaths, *modelPaths;
     char                                **loaderMachineName, **controllerIp;
     char                                *loaderMachineNames, *controllerIps;
     int                                         i, rc, projectIdx;
     unsigned short                  *instId;
     int                             *instId2;
     int                                 * modelIds;
     unsigned short             *numModelsPerProject;
     unsigned short             *modelId;
     unsigned short             *modelState;
     unsigned short             allocProjects, numProjects;
     unsigned short             allocModels, numModels;
     unsigned short             allocProjectPathLen, maxProjectPathLen;
     unsigned short             allocModelPathLen, maxModelPathLen;
     unsigned short             allocMachineLen, maxMachineLen;
     unsigned short             allocIpLen, maxIpLen;

     // Obtenir la taille des données.
     allocProjects              =allocModels                 = 0;
     allocProjectPathLen = allocModelPathLen = 0;
     allocMachineLen         = allocIpLen            = 0;

     rc = OpalGetActiveProjects( 0, &allocProjects,
                                                             0, &allocModels,
                                                             0, &allocProjectPathLen,
                                                             0, &allocModelPathLen,
                                                             0, &allocMachineLen,
                                                             0, &allocIpLen,
                                                             NULL, NULL, NULL, NULL,
                                                             NULL, NULL, NULL, NULL );

     if ((rc != 0) && (rc != E2BIG))
     {
         PrintError(rc, "OpalGetActiveProject");
            printf(" -        alGetActiveProjects error output !!!\n");
            printf("\t numProjects - %i \n", allocProjects);
            printf("\t numModels - %i \n", allocModels);
            printf("\t maxProjectPathLen - %i \n", allocProjectPathLen);
            printf("\t maxModelPathLen - %i \n", allocModelPathLen);
            //printf("\t maxMachineNameLen - %i \n", maxMachineNameLen);
            printf("\t maxIpLen - %i \n", allocIpLen);
            return (rc);
             // Erreur
             //return returnInstance;
     }

     projectPath = (char **)_alloca(allocProjects * sizeof(*projectPath));
     projectPaths = (char *)_alloca(allocProjects * (allocProjectPathLen + 1) * sizeof(*projectPaths));

     loaderMachineName = (char **)_alloca(allocProjects * sizeof(*loaderMachineName));
     loaderMachineNames = (char *)_alloca(allocProjects * (allocMachineLen + 1) * sizeof(*loaderMachineNames));

     controllerIp = (char **)_alloca(allocProjects * sizeof(*controllerIp));
     controllerIps = (char *)_alloca(allocProjects * (allocIpLen + 1) * sizeof(*controllerIps));

     numModelsPerProject = (unsigned short*)_alloca(allocProjects * sizeof(*numModelsPerProject));

     modelPath = (char **)_alloca(allocModels * sizeof(*modelPath));
     modelPaths = (char *)_alloca(allocModels * (allocModelPathLen + 1) * sizeof(*modelPaths));

     for (i = 0; i < allocProjects; ++i)
     {
             projectPath[i]                          = &projectPaths[i * (allocProjectPathLen + 1)];
             loaderMachineName[i]        = &loaderMachineNames[i * (allocMachineLen + 1)];
             controllerIp[i]                 = &controllerIps[i * (allocIpLen + 1)];
     }

     for (i = 0; i < allocModels; ++i)
     {
             modelPath[i] = &modelPaths[i * (allocModelPathLen + 1)];
     }

     instId = (unsigned short *)_alloca(allocProjects * sizeof(*instId));
     instId2 = (int *)_alloca(allocProjects * sizeof(*instId2));
     modelId = (unsigned short*)_alloca(allocModels * sizeof(*modelId));
     modelState = (unsigned short *)_alloca(allocModels * sizeof(*modelState));

     rc = OpalGetActiveProjects( allocProjects, &numProjects,
                                                             allocModels, &numModels,
                                                             allocProjectPathLen, &maxProjectPathLen,
                                                             allocModelPathLen, &maxModelPathLen,
                                                             allocMachineLen, &maxMachineLen,
                                                             allocIpLen, &maxIpLen,
                                                             projectPath, instId,
                                                             loaderMachineName, controllerIp, modelPath,
                                                             numModelsPerProject, modelId, modelState);
     printf(" - OpalGetActiveProjects output !!!\n");
            printf("\t numProjects - %i \n", allocProjects);
            printf("\t numModels - %i \n", allocModels);
            printf("\t maxProjectPathLen - %i \n", allocProjectPathLen);
            printf("\t maxModelPathLen - %i \n", allocModelPathLen);
            //printf("\t maxMachineNameLen - %i \n", maxMachineNameLen);
            printf("\t maxIpLen - %i \n", allocIpLen);

    for(i=0; i<numProjects; i++)
    {
        printf("\t \t projectPath[%d] = %s \n", i, *(projectPath +i));
        printf("\t \t instId[%d] = %d \n", i, *(instId + i));
        printf("\t \t loaderMachineName[%d] = %s \n", i, *(loaderMachineName + i));
        printf("\t \t controllerIp[%d] = %s \n", i, *(controllerIp + i));
        printf("\t \t numModelsPerProject[%d] = %d \n", i, *  (numModelsPerProject +i));
    }

    for(i=0; i<numModels; i++)
    {
        printf("\t \t modelPath[%d] = %s \n", i, *(modelPath+i));
        printf("\t \t modelId[%d] = %d \n", i, *(modelId +i));
        printf("\t \t modelState[%d] = %d \n", i, *(modelState+i));
    }
    OpalDisconnect();
    getchar();
 }


 void   PrintError(int rc, char *funcName)
  {
char            buf[512];
unsigned short  len;

OpalGetLastErrMsg(buf, sizeof(buf), &len);
printf("Error !!! \n %s (code %d) from %s\n", buf, rc, funcName);
  }

【问题讨论】:

  • 你不能,也没有办法在使用后正确销毁返回的字符串。此函数在本机代码中使用也不安全,因此使用 C++/CLI 也是一种废话。

标签: c# char marshalling param


【解决方案1】:

签名太奇怪了,无法翻译成 C#。我试着看看 TlbImp.exe 会将它翻译成什么,它说:

TlbImp:警告 TI3015:“Marshal2.IMarshal3.GetActiveNames”的至少一个参数不能被运行时封送处理程序封送。因此,此类参数将作为指针传递,并且可能需要不安全的代码来操作。

如果可以,我建议更改函数以返回包含 BSTR 的 SAFEARRAY。在 C# 端,您会看到一个 System.Array,其元素(Object 类型)可以转换为 String。

我还建议将 GetActiveNames 设为 COM 类的方法。 P/Invoke 太原始且不安全。

【讨论】:

  • 感谢您的建议。但是,此功能位于第 3 方 dll 中。所以,我无法控制本机功能。如果我们必须在 C# 端玩指针,我们会怎么做?
  • 您可以将其声明为int GetActiveNames(IntPtr names)unsafe int GetActiveNames(byte** names) 并开始使用指针。或者使用 C++/CLI 作为桥梁来调用非托管的 GetActiveNames 并为 C# 端提供适当的 API。我建议使用 C++/CLI,尤其是在调用者负责释放结果的情况下,这可能需要使用 delete 运算符(不能在 C# 中使用)来完成。
  • 我对此很陌生..您能否提供一些代码 sn-ps 来展示如何做到这一点?
【解决方案2】:

哇,这真是一个可怕的 API。是否还有其他任何东西甚至远程......哦,我不知道......体面,你可以使用?但如果你真的想要...

using System;
using System.Runtime.InteropServices;

public class ActiveProjects
{
    public string[] ProjectPaths { get; set; }
    public ushort[] InstanceIds { get; set; }
    public string[] MachineNames { get; set; }
    public string[] ControllerIps { get; set; }
    public string[] ModelPaths { get; set; }
    public ushort[] NumberOfModelsPerProject { get; set; }
    public ushort[] ModelIds { get; set; }
    public ushort[] ModelStates { get; set; }

    [DllImport("<the DLL>")]
    unsafe private static int OpalGetActiveProjects(
        ushort allocatedProjects,
        ushort* numProjects,
        ushort allocatedModels,
        ushort* numModels,
        ushort allocatedProjectsPathLen,
        ushort* maxProjectPathLen,
        ushort allocatedModelPathLen,
        ushort* maxModelPathLen,
        ushort allocatedMachineNameLen,
        ushort* maxMachineNameLen,
        ushort allocatedIpLen,
        ushort* maxIpLen,
        sbyte** projectPaths,
        ushort* instanceIDs,
        sbyte** machineNames,
        sbyte** controllerIPs,
        sbyte** modelPaths,
        ushort* numModelsPerProject,
        ushort* modelIDs,
        ushort* modelStates
        );

    public void GetActiveProjects()
    {
        unsafe
        {
            ushort numberOfProjects = 0;
            ushort numberOfModels = 0;
            ushort maxProjectPathLength = 0;
            ushort maxModelPathLength = 0;
            ushort maxMachineNameLength = 0;
            ushort maxIpLength = 0;

            int result = OpalGetActiveProjects(
                0,
                &numberOfProjects,
                0,
                &numberOfModels,
                0,
                &maxProjectPathLength,
                0,
                &maxModelPathLength,
                0,
                &maxMachineNameLength,
                0,
                &maxIpLength,
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                null
                );

            if (result != 0 && result != 123)
                throw new Exception("Error getting active projects");

            sbyte** projectPaths = null;
            ushort* instanceIds = null;
            sbyte** machineNames = null;
            sbyte** controllerIps = null;
            sbyte** modelPaths = null;
            ushort* numberOfModelsPerProject = null;
            ushort* modelIds = null;
            ushort* modelStates = null;

            try
            {
                projectPaths = (sbyte**)Marshal.AllocHGlobal(numberOfProjects * IntPtr.Size).ToPointer();
                for (int i = 0; i < numberOfProjects; ++i)
                    projectPaths[i] = null;
                for (int i = 0; i < numberOfProjects; ++i)
                    projectPaths[i] = (sbyte*)Marshal.AllocHGlobal(maxProjectPathLength);

                instanceIds = (ushort*)Marshal.AllocHGlobal(numberOfProjects * 2).ToPointer();

                machineNames = (sbyte**)Marshal.AllocHGlobal(numberOfProjects * IntPtr.Size).ToPointer();
                for (int i = 0; i < numberOfProjects; ++i)
                    machineNames[i] = null;
                for (int i = 0; i < numberOfProjects; ++i)
                    machineNames[i] = (sbyte*)Marshal.AllocHGlobal(maxMachineNameLength).ToPointer();

                controllerIps = (sbyte**)Marshal.AllocHGlobal(numberOfProjects * IntPtr.Size).ToPointer();
                for (int i = 0; i < numberOfProjects; ++i)
                    controllerIps[i] = null;
                for (int i = 0; i < numberOfProjects; ++i)
                    controllerIps[i] = (sbyte*)Marshal.AlloHGlobal(maxIpLength).ToPointer();

                modelPaths = (sbyte**)Marshal.AlloHGlobal(numberOfModels * IntPtr.Size).ToPointer();
                for (int i = 0; i < numberOfProjects; ++i)
                    modelPaths = null;
                for (int i = 0; i < numberOfProjects; ++i)
                    modelPaths = (sbyte*)Marshal.AllocHGlobal(maxModelPathLength).ToPointer();

                numberOfModelsPerProject = (ushort*)Marshal.AlloHGlobal(numberOfProjects * 2).ToPointer();
                modelIds = (ushort*)Marshal.AllocHGlobal(numberOfModels * 2).ToPointer();
                modelStates = (ushort*)Marshal.AllocHGlobal(numberOfModels * 2).ToPointer();

                ushort numberOfProjects2 = 0;
                ushort numberOfModels2 = 0;
                ushort maxProjectPathLength2 = 0;
                ushort maxModelPathLength2 = 0;
                ushort maxMachineNameLength2 = 0;
                ushort maxIpLength2 = 0;

                OpalGetActiveProjects(
                    numberOfProjects,
                    &numberOfProjects2,
                    numberOfModels,
                    &numberOfModels2,
                    maxProjectPathLength,
                    &maxProjectPathLength2,
                    maxModelPathLength,
                    &maxModelPathLength2,
                    maxMachineNameLength,
                    &maxMachineNameLength2,
                    maxIpLength,
                    &maxIpLength2,
                    projectPaths,
                    instanceIds,
                    machineNames,
                    controllerIps,
                    modelPaths,
                    numberOfModelsPerProject,
                    modelIds,
                    modelStates
                    );

                ProjectPaths = new string[numberOfProjects2];
                for (int i = 0; i < numberOfProjects2; ++i)
                    ProjectPaths[i] = new string(projectPaths[i]);

                InstanceIds = new ushort[numberOfProjects2];
                for (int i = 0; i < numberOfProjects2; ++i)
                    InstanceIds[i] = instanceIds[i];

                MachineNames = new string[numberOfProjects2];
                for (int i = 0; i < numberOfProjects2; ++i)
                    MachineNames[i] = new string(machineNames[i]);

                ControllerIps = new string[numberOfProjects2];
                for (int i = 0; i < numberOfProjects2; ++i)
                    ControllerIps[i] = new string(controllerIps[i]);

                ModelPaths = new string[numberOfModels2];
                for (int i = 0; i < numberOfModels2; ++i)
                    ModelPaths[i] = new string(modelPaths[i]);

                NumberOfModelsPerProject = new ushort[numberOfProjects2];
                for (int i = 0; i < numberOfProjects2; ++i)
                    NumberOfModelsPerProject[i] = numberOfModelsPerProject[i];

                ModelIds = new ushort[numberOfModels2];
                for (int i = 0; i < numberOfModels2; ++i)
                    ModelIds[i] = modelIds[i];

                ModelStates = new ushort[numberOfModels2];
                for (int i = 0; i < numberOfModels2; ++i)
                    ModelStates[i] = modelStates[i];
            }
            finally
            {
                if (projectPaths != null)
                {
                    for (int i = 0; i < numberOfProjects && projectPaths[i] != null; ++i)
                        Marshal.FreeHGlobal(IntPtr((void*)projectPaths[i]));

                    Marshal.FreeHGlobal(IntPtr((void*)projectPaths));
                }

                if (instanceIds != null)
                    Marshal.FreeHGlobal(IntPtr((void*)instanceIds));

                if (machineNames != null)
                {
                    for (int i = 0; i < numberOfProjects && machineNames[i] != null; ++i)
                        Marshal.FreeHGlobal(IntPtr((void*)machineNames[i]));

                    Marshal.FreeHGlobal(IntPtr((void*)machineIds));
                }

                if (controllerIps != null)
                {
                    for (int i = 0; i < numberOfProjects && controllerIps[i] != null; ++i)
                        Marshal.FreeHGlobal(IntPtr((void*)controllerIps[i]));

                    Marshal.FreeHGlobal(IntPtr((void*)controllerIps));
                }

                if (modelPaths != null)
                {
                    for (int i = 0; i < numberOfModels && modelPaths[i] != null; ++i)
                        Marshal.FreeHGlobal(IntPtr((void*)modelPaths[i]));

                    Marshal.FreeHGlobal(IntPtr((void*)modelPaths));
                }

                if (numberOfModelsPerProject != null)
                    Marshal.FreeHGlobal(IntPtr((void*)numberOfModelsPerProject));

                if (modelIds != null)
                    Marshal.FreeHGlobal(IntPtr((void*)modelIds));

                if (modelStates != null)
                    Marshal.FreeHGlobal(IntPtr((void*)modelStates));
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    使用 C# P/Invoke 函数:

    using System.Runtime.InteropServices;
    
    public class ActiveNames
    {
        public string[] ActiveNames { get; set; }
    
        [DllImport("GetActiveNames.dll")] // replace with the actual name of the DLL
        unsafe private static int GetActiveNames(sbyte** names):
    
        public void GetActiveNames()
        {
            unsafe
            {
                // I use 100 here as an artificial number. This may not be a reasonable
                // assumption, but I don't know how the actual GetActiveNames works
                sbyte** names = (sbyte**)Marshal.AllocHGlobal(100).ToPointer();
    
                try
                {
                    GetActiveNames(names);
    
                    // fill the ActiveNames property
                    ActiveNames = new string[100];
    
                    for (int i = 0; i < 100; ++i)
                        ActiveNames[i] = new string(names[i]);
                }
                finally
                {
                    // deallocate the unmanaged names memory
                    Marshal.FreeHGlobal(IntPtr((void*)names));
                }
            }
        }
    }
    

    并且使用 C++/CLI(不需要 P/Invoke):

    #include "GetActiveNames.h" // replace with the actual GetActiveNames's header
    
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    public ref class ActiveNames
    {
    private:
        array<String^>^ m_activeNames;
    
    public:
        property array<String^>^ ActiveNames
        {
            array<String^>^ get()
            {
                return m_activeNames;
            }
    
            void set(array<String^>^ names)
            {
                m_activeNames = names;
            }
        }
    
        void GetActiveNames()
        {
            signed char** names = new signed char*[100];
    
            try
            {
                ::GetActiveNames(reinterpret_cast<char**>(names));
    
                ActiveNames = gcnew array<String^>(100);
    
                for (int i = 0; i < 100; ++i)
                    ActiveNames[i] = gcnew String(names[i]);
            catch (...)
            {
                delete[] names;
                throw;
            }
    
            delete[] names;
        }
    };
    

    如您所见,我做了一些不安全的假设,因为我不知道 GetActiveNames 是如何工作的(名称的内存是如何分配和解除分配的?您怎么知道返回了多少名称?)。如果您需要更多帮助,则必须更具体。

    【讨论】:

    • 我尝试了 P/Invoke 方式并显示以下错误 尝试读取或写入受保护的内存。这通常表明其他内存已损坏。.
    猜你喜欢
    • 2011-05-02
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    相关资源
    最近更新 更多