2012-09-19 16 views
0

我有一個本地的dll,其功能看起來像這樣如何編組與C#(的char * PARAM [])的函數

int GetActiveNames(char* names[]); 

如何在C#中創建我的PInvoke包裝方法的功能上面的函數返回給我一個字符串/字符數組?


更描述性..

我嘗試了P /調用方式和其示出試圖讀取或寫入受保護的存儲器中的下面的錯誤。這通常表明其他內存已損壞。

我剛剛剝離了實際的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將存儲實際總數的指針數 所有活動項目的模型數。 maxProjectPathLen:指向API將存儲最長項目路徑長度的指針。 maxModelPathLen:指向API將存儲最長模型路徑長度的指針。 maxMachineNameLen:指向API將存儲最長機器名稱長度的指針。 maxIpLen:指向API將存儲 最長IP地址的長度的指針。 projectPaths:字符串數組,其中API將存儲活動項目的配置文件的路徑。 instanceID:其中API將存儲活動項目的實例ID的數組。 machineNames:其中API將存儲機器名稱的數組。 controllerIPs:其中API將存儲IP地址的數組。 modelPaths:其中API將存儲所有活動項目的模型路徑。 numModelsPerProject:其中API將存儲每個項目的模型數量。使用這些值來確定哪些模型屬於哪個項目。 modelIDs:其中API將存儲所有 活動項目的模型ID。 modelStates:其中API將存儲所有活動項目的模型狀態的數組。

還有它使用OpalGetActiveProjects()函數來獲取projectPaths,machineNames等C程序..

#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); 
    } 
+1

你不能,沒辦法正確銷燬返回的字符串後使用它們。這個函數在本地代碼中使用並不安全,因此使用C++/CLI也是一個垃圾短片。 –

回答

0

哇,這真是一個可怕的API。沒有任何東西甚至是遠程的......哦,我不知道...... DECENT,你可以使用?但如果你真的想要它...

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

簽名太詭異了被翻譯成C#。我想看到的TLBIMP.EXE將它翻譯成,並說:

TLBIMP:警告TI3015:爲「Marshal2.IMarshal3.GetActiveNames」的論據至少有一個不能被封送運行時進行封送處理。因此這些參數將作爲指針傳遞,可能需要不安全的代碼來操作。

如果可以,我建議改變函數返回一個包含BSTR的SAFEARRAY。在C#端,您會看到一個System.Array,其元素(類型爲Object)可以轉換爲String。

我也建議讓GetActiveNames成爲COM類的一個方法。 P/Invoke太原始,不安全。

+0

感謝您的建議。但是,這個函數在第三方dll中。所以,我無法控制本地功能。如果我們必須在C#端玩弄指針,那我們該怎麼做? – user1683000

+0

你可以聲明它爲'int GetActiveNames(IntPtr names)'或者'unsafe int GetActiveNames(byte ** names)'並開始玩指針。或者使用C++/CLI作爲橋樑來調用非託管的GetActiveNames,併爲C#端提供適當的API。如果調用者負責解除分配結果,我可能會推薦使用C++/CLI,這可能需要使用'delete'操作符(無法在C#中使用)完成。 – user1610015

+0

我很新這個..你能否提供一些代碼片段顯示如何做到這一點? – user1683000

0

用C#P /調用功能:

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如何工作(名字分配和釋放的內存如何?你怎麼知道有多少名字被返回?)。如果您需要更多幫助,您必須更具體。

+0

我嘗試了P/Invoke方式,並顯示以下錯誤試圖讀取或寫入受保護的內存。這通常表明其他內存已損壞。。 – user1683000

+0

@ user1683000我編輯了我原來的問題 – user1683000