我试图在我的C#应用程序中通过pinvoke使用advapi32.dll来获取服务使用的可执行文件的路径。我指定了Unicode并获取了一些似乎是路径的部分,但是某些字符显然在翻译中被加入了。
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方法和结构
因此,举例来说,我希望能够为'" Bonjour服务"返回路径
"C:\Program Files\Bonjour\mDNSResponder.exe"
相反,我在Windows 10上得到了这个
"C:\??? , m Files\Bonjour\mDNSResponder.exe"
,这在Windows 8上
"C:\??? , "C:\??? , "C:\??? , "C:\??? , "C:\
在XP上就是这个
"C??
如果我尝试转换任何其他属性,如显示名称或开始名称,那么我会得到一个合法的字符串。为什么不能解析其他字符串的可执行路径?
答案 0 :(得分: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);