2012-09-19 1 views
0

나는 기능 난에 C#으로 내 PInvoke를 래퍼 방법을 작성하는 방법이C#에서 (숯불 *의 PARAM [])로 함수를 마샬링하는 방법

int GetActiveNames(char* names[]); 

과 같은 기본 DLL에서 함수를 가지고 날 문자열/문자의 배열을 반환 위의 함수?


는 I는 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입니다. 응용 프로그램은이 사실을 사용하여 첫 x 째 호출에서 길이를 0으로 지정하고 필요한 기 o 장치를 할당하고 정보를 얻기 위해 두 x 째 호출을 _ 행할 수 있습니다. 경로에 대한 호출은 컴퓨터 이름, IP는 및 인스턴스 식별자를 해결 저장 에 의해 할당 된 공용 프로젝트 번호 :


입력


allocatedProjects 파라미터. allocatedModels : 저장 장치가 이름, 경로 및 인스턴스 ID에 대한 호출자 에 의해 할당 된 모델 수입니다. allocatedProjectPathLen :의 할당 된 길이 : 컴퓨터 이름 allocatedIpLen를받을 스토리지 할당 된 길이 : 모델 경로 allocatedMachineNameLen를받을 스토리지 할당 된 길이 : 프로젝트 경로 allocatedModelPathLen을받을 스토리지 할당 된 길이 는 IP를받을 저장


출력 매개 변수

를 해결

numProjects : API가 실제 활성 프로젝트 수를 저장할 위치의 포인터. numModels : API가 실제 총 프로젝트 수에 대한 개의 모델 수를 저장할 위치의 포인터. maxProjectPathLen : API가 가장 긴 프로젝트 경로의 길이를 저장할 위치의 포인터. maxModelPathLen : API가 가장 긴 모델 경로의 길이를 저장할 위치의 포인터. maxMachineNameLen : API가 가장 긴 시스템 이름의 길이를 저장할 위치의 포인터. maxIpLen : API가 가장 긴 IP 주소의 길이를 저장할 위치의 포인터. projectPaths : API가 활성 프로젝트의 구성 파일 경로를 저장하는 문자열 배열입니다. instanceIDs : API가 활성 프로젝트의 인스턴스 ID를 저장하는 배열. machineNames : API가 컴퓨터 이름을 저장하는 배열. 컨트롤러 IP : API가 IP 주소를 저장할 어레이. modelPaths : API가 모든 활성 프로젝트의 모델 경로를 저장할 배열. numModelsPerProject : API가 각 프로젝트의 모델 수를 저장하는 배열. 이 값을 사용하여 모델이 어떤 프로젝트에 속하는지 결정합니다. modelIDs : API가 모든 활성 프로젝트의 모델 ID를 저장하는 배열. modelStates : API가 모든 활성 프로젝트의 모델 상태를 저장하는 배열. projectPaths, machineNames 등을 얻을 OpalGetActiveProjects() 함수를 사용하는 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입니다. 원격으로도 다른 게 없니? 모르겠어 ... 십센트, 너 쓸 수있어? 하지만 네가 정말로 원한다면 ...

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'의 인수 하나 이상의 런타임 마샬에 의해 마샬링 할 수 없습니다. 따라서 이러한 인수는 포인터로 전달되므로 안전하지 않은 코드가 조작되어야 할 수도 있습니다.

가능한 경우 BSTRs가 포함 된 SAFEARRAY를 반환하는 함수를 변경하는 것이 좋습니다. C# 측에서 System.Array를 볼 수 있습니다. System.Array의 요소 (Object 유형)는 String으로 캐스팅 할 수 있습니다.

또한 GetActiveNames를 COM 클래스의 메서드로 만드는 것이 좋습니다. P/Invoke가 너무 원시적이고 안전하지 않습니다. P에 C#을 사용

+0

제안 해 주셔서 감사합니다. 그러나이 기능은 타사 DLL에 있습니다. 그래서, 나는 네이티브 함수를 제어 할 수 없습니다. 우리가 C#에서 포인터로 놀아야 만한다면 어떻게 할 것인가? – user1683000

+0

당신은'int GetActiveNames (IntPtr names)'또는'unsafe int GetActiveNames (byte ** names)'로 선언하고 포인터로 재생하기 시작할 것입니다. 또는 C++/CLI를 사용하여 관리되지 않는 GetActiveNames를 호출하고 적절한 API를 C# 측에 제공하십시오. C++/CLI는 호출자가 결과를 할당 해제 할 책임이있는 경우에 특히 좋습니다.이 연산자는 결과를 'delete'연산자 (C#에서는 사용할 수 없음)로 처리해야 할 수도 있습니다. – user1610015

+0

나는 이것에 아주 새롭다. pls는 그것을하는 방법을 보여주는 몇몇 부호 단서를 제공 할 수 있 었는가? – user1683000

0

/호출 기능 :

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/호출이 필요) : 당신이 볼 수 있듯이

#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