为什么 PtrToStringUni 返回一些不正确的字符
Why is PtrToStringUni returning some incorrect characters
我正尝试在我的 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 Service" 到 return
的路径
"C:\Program Files\Bonjour\mDNSResponder.exe"
相反,我在 Windows 10
上得到了这个
"C:\??? , m Files\Bonjour\mDNSResponder.exe"
和这个 Windows 8
"C:\??? , "C:\??? , "C:\??? , "C:\??? , "C:\
在 XP 上只有这个
"C??
如果我尝试转换任何其他属性,如显示名称或开始名称,那么我会得到一个合法的字符串。为什么可执行文件路径不像其他字符串那样解析?
发生这种情况是因为您应该分配 缓冲区,而不是传递固定的缓冲区大小。 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);
我正尝试在我的 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 Service" 到 return
的路径"C:\Program Files\Bonjour\mDNSResponder.exe"
相反,我在 Windows 10
上得到了这个"C:\??? , m Files\Bonjour\mDNSResponder.exe"
和这个 Windows 8
"C:\??? , "C:\??? , "C:\??? , "C:\??? , "C:\
在 XP 上只有这个
"C??
如果我尝试转换任何其他属性,如显示名称或开始名称,那么我会得到一个合法的字符串。为什么可执行文件路径不像其他字符串那样解析?
发生这种情况是因为您应该分配 缓冲区,而不是传递固定的缓冲区大小。 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);