2017-12-04 26 views
0

내 C# 응용 프로그램에서 pinvoke를 통해 advapi32.dll을 사용하여 서비스에서 사용하는 실행 파일의 경로를 가져 오려고합니다. 유니 코드를 지정하고 경로로 보이는 부분을 가져 오는 중이지만 일부 문자는 분명히 번역에 휘 말리기 시작했습니다.PtrToStringUni가 잘못된 문자를 반환하는 이유

private static string GetServicePath(IntPtr service) 
{ 
    SERVICE_CONFIG status = new SERVICE_CONFIG(); 

    uint bytesNeeded = 1; 
    var queryResult = QueryServiceConfig(service, status, 0, out bytesNeeded); 
    if (queryResult == 0 && bytesNeeded == 0) 
    { 
     throw new ApplicationException("Failed to query service config."); 
    } 
    else 
    { 
     QueryServiceConfig(service, status, bytesNeeded, out bytesNeeded); 
    } 

    var servicePath = Marshal.PtrToStringUni(status.lpBinaryPathName); 
    return servicePath; 
} 

방법은 서비스에 대한 핸들을 사용하여 서비스 이름을 얻기 위해 호출되는^

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
private class SERVICE_CONFIG 
{ 
    public int dwServiceType; 
    public ServiceStartType dwStartType; 
    public int dwErrorControl; 
    public IntPtr lpBinaryPathName; 
    public IntPtr lpLoadOrderGroup; 
    public int dwTagID; 
    public IntPtr lpDependencies; 
    public IntPtr lpServiceStartName; 
    public IntPtr lpDisplayName; 
}; 

[DllImport("advapi32.dll", CharSet = CharSet.Unicode)] 
private static extern int QueryServiceConfig(IntPtr hService, SERVICE_CONFIG queryConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded); 

^PInvoke를 방법 및 구조가

그래서 예를 들어, 내가 봉쥬르 "에 대해 묻는 기대 관련 서비스 "경로를 반환

내가 변환 할 경우대신 나는

"C:\??? , "C:\??? , "C:\??? , "C:\??? , "C:\

그냥이 XP

"C??

의 Windows 8이 윈도우 10

"C:\??? , m Files\Bonjour\mDNSResponder.exe"

에이를 얻을 표시 이름 또는 시작 이름과 같은 다른 속성 중 하나를 선택하면 합법적 인 문자열. 실행 파일 경로가 다른 문자열과 같은 방식으로 해결되지 않는 이유는 무엇입니까?

+0

을 대신 당신도 할 필요가 없습니다 것입니다 string''의 IntPtr''와 같은 문자열을 표시 한 이유는 PtrToStringUni를 호출 하시겠습니까? – ckuri

+0

문자열을 곧장 마샬링하는 것은 전혀 효과가 없습니다. 시도했을 때 CLR 예외가 있습니다. – whyndsiv

답변

0

에 고정 버퍼 크기를 할당하지 않기 때문에 발생합니다. QUERY_SERVICE_CONFIG의 모든 문자열 포인터는 해당 버퍼에 포함됩니다.

QueryServiceConfig는 버퍼의 크기를 알려줍니다. 네이티브 예를 들어 여기에 있습니다 : 그래서 Querying a Service's Configuration

, 나는 그에 따라 코드를 수정했습니다 :

private static string GetServicePath(IntPtr service) 
    { 
     QueryServiceConfig(service, IntPtr.Zero, 0, out int size); 
     if (size == 0) 
      throw new Win32Exception(Marshal.GetLastWin32Error()); 

     var ptr = Marshal.AllocHGlobal(size); 
     try 
     { 
      if (!QueryServiceConfig(service, ptr, size, out size)) 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 

      var config = Marshal.PtrToStructure<QUERY_SERVICE_CONFIG>(ptr); 
      return config.lpBinaryPathName; 
     } 
     finally 
     { 
      Marshal.FreeHGlobal(ptr); 
     } 
    } 

    // note that I use a struct, not a class, so I can call Marshal.PtrToStructure. 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
    private struct QUERY_SERVICE_CONFIG 
    { 
     public int dwServiceType; 
     public int dwStartType; 
     public int dwErrorControl; 
     public string lpBinaryPathName; 
     public string lpLoadOrderGroup; 
     public int dwTagID; 
     public string lpDependencies; 
     public string lpServiceStartName; 
     public string lpDisplayName; 
    }; 

    // return type is a bool, no need for an int here 
    // user SetLastError when the doc says so 
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    private static extern bool QueryServiceConfig(IntPtr hService, IntPtr lpServiceConfig, int cbBufSize, out int pcbBytesNeeded); 
+0

감사합니다. 그 점이이 작품을 만들었지 만 구별을 잘 모르겠다. 이 코드를 상속 받았고 왜 메모리를 할당하고 구조체에 ptr을 정렬하는 것이 클래스에서 ptrs를 사용하여 문자열로 변환하는 것보다 효과적인지 더 잘 이해하고 싶습니다. – whyndsiv

+0

사실, 선택의 여지가 없습니다. QueryServiceConfig가 할당하라는 것을 할당해야합니다. 이 버퍼의 크기는 구조체/클래스 자체의 크기보다 훨씬 큽니다. 그런 다음 문자열 내용을 포함하여 버퍼를 채 웁니다. 그런 다음 구조체의 문자열 참조를 해당 할당 된 버퍼 안의 문자열 내용 *을 가리 키도록 설정합니다. 클래스 크기 자체 만 할당했기 때문에 오류가 발생했습니다. 또한 원하는 경우 문자열 대신 IntPtr을 사용할 수 있지만 쓸모가 없습니다. CLR이 버퍼를 최종 유형으로 변환하는 것이 더 쉽습니다. –