如果已经有 WCF 服务器在命名管道端点上侦听,则会创建一个共享内存对象,服务器通过该对象发布管道的实际名称。 See here for details of this.
您可以使用类似以下的代码检查此共享内存对象的存在,如果没有服务器正在运行,则不会抛出,只是返回 false。 (我已经从我已经工作的代码中提取了它,然后对其进行了编辑以执行您想要的操作 - 但没有测试编辑后的版本,因此如果您必须修复程序集/命名空间引用等以使其运行,请道歉。)
public static class ServiceInstanceChecker
{
public static bool DoesAServerExistAlready(string hostName, string path)
{
return IsNetNamedPipeSharedMemoryMetaDataPublished(DeriveSharedMemoryName(hostName, path));
}
private static string DeriveSharedMemoryName(string hostName, string path)
{
StringBuilder builder = new StringBuilder();
builder.Append(Uri.UriSchemeNetPipe);
builder.Append("://");
builder.Append(hostName.ToUpperInvariant());
builder.Append(path);
byte[] uriBytes = Encoding.UTF8.GetBytes(builder.ToString());
string encodedNameRoot;
if (uriBytes.Length >= 0x80)
{
using (HashAlgorithm algorithm = new SHA1Managed())
{
encodedNameRoot = ":H" + Convert.ToBase64String(algorithm.ComputeHash(uriBytes));
}
}
else
{
encodedNameRoot = ":E" + Convert.ToBase64String(uriBytes);
}
return Uri.UriSchemeNetPipe + encodedNameRoot;
}
private static bool IsNetNamePipeSharedMemoryMetaDataPublished(string sharedMemoryName)
{
const uint FILE_MAP_READ = 0x00000004;
const int ERROR_FILE_NOT_FOUND = 2;
using (SafeFileMappingHandle fileMappingHandle
= OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
{
if (fileMappingHandle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
if (ERROR_FILE_NOT_FOUND == errorCode) return false;
throw new Win32Exception(errorCode); // The name matched, but something went wrong opening it
}
return true;
}
}
private class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeFileMappingHandle() : base(true) { }
public SafeFileMappingHandle(IntPtr handle) : base(true) { base.SetHandle(handle); }
protected override bool ReleaseHandle()
{
return CloseHandle(base.handle);
}
}
}
您传入的主机名和路径是从 WCF 服务 url 派生的。主机名是特定主机名(例如localhost)或+,或*,具体取决于HostNameComparisonMode 的设置。
编辑:您还需要一些用于 Win API 函数的 P/Invoke 声明:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileMappingHandle OpenFileMapping(
uint dwDesiredAccess,
bool inheritHandle,
string name
);
EDIT2:我们需要调整 DeriveSharedMemoryName 的返回值以指定本地内核命名空间,假设您的应用程序没有以提升的权限运行。将此函数的最后一行更改为:
return @"Local\" + Uri.UriSchemeNetPipe + encodedNameRoot;
您还需要正确指定主机名参数以匹配绑定中使用的 hostNameComparisonMode 设置。据我记得,这默认为 NetNamedPipeBinding 中的 StrongWildcard 匹配,因此您可能需要传入 "+" 而不是 "localhost"。