2017-12-04 70 views
0

C#アプリケーションのpinvoke経由でadvapi32.dllを使用して、サービスで使用する実行可能ファイルへのパスを取得しようとしています。私はUnicodeを指定していて、パスのように見える部分を取得していますが、文字の一部は明らかに翻訳にガーベージされています。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; 
} 

^方法は、したがって、たとえば、私はボンジュール」を求め期待

関連のPInvoke方法と構造^

[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); 

サービスへのハンドルを使用してサービス名を取得するために呼び出されています返すパス

"C:\Program Files\Bonjour\mDNSResponder.exe"

代わりに私が変換しようとした場合、私は、Windows 10 XP

"C??

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

のWindows 8上の

この

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

とちょうどこの上でこれを取得します表示名や開始名などの他のプロパティのいずれかを次に取得します正当な文字列。実行可能パスが他の文字列のやり方で解決されないのはなぜですか?

+0

なぜあなたはIntPtr'代わりに、あなたもする必要はありませんSTRING' 'の'として文字列をマークしています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があなたに割り当てるように指示するものを割り当てる必要があります。このバッファのサイズは、struct /クラス自体のサイズよりもはるかに大きいです。それは、文字列の内容を含むそのバッファを埋めるでしょう。次に、構造体内の文字列参照を、その割り当てられたバッファー内の文字列コンテンツ*を指すように設定します。あなたはクラスサイズだけを割り当てたので、エラーが発生しました。また、あなたが望むならば、文字列の代わりにIntPtrを使うこともできますが、それは無用です。 CLRにバッファを最終型に変換させる方が簡単です。 –

関連する問題