您好,
我们有一个winform应用程序只能作为singelton执行,如果第二个实例尝试启动这个新实例将连接到当前并通过namedpipes传输参数。
问题是,在启动第一个实例时,将尝试连接到现有主机。如果主机不存在(如本例所示),则抛出异常。处理此异常没有问题,但我们的开发人员经常使用“Break on Exception”,这意味着每次启动应用程序时,开发人员都会得到两个(在这种情况下)中断异常。每次开始时,Thay必须两次击中F5。
如果没有抛出异常,有没有办法检查服务是否可用?
BestRegards
编辑1:
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);
以下代码说明:错误152无法将类型'System.IntPtr'隐式转换为'Orbit.Client.Main.Classes.Controllers.MyClientController.SafeFileMappingHandle'
using (SafeFileMappingHandle fileMappingHandle
= OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
{
答案 0 :(得分:3)
如果已经有一个WCF服务器正在侦听命名管道端点,则会创建一个共享内存对象,服务器通过该对象发布管道的实际名称。 See here for details of this
如果没有服务器正在运行,您可以使用以下代码检查此共享内存对象是否存在,该代码不会抛出,只返回false。 (我从我已经工作的代码中提取了这个,然后编辑它来做你想要的 - 但是没有测试编辑的版本,所以如果你必须修复程序集/命名空间引用等以使其运行,那么道歉。)< / p>
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服务网址派生的。主机名是特定主机名(例如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;
您还需要正确指定hostname参数以匹配绑定中使用的hostNameComparisonMode设置。据我所知,这默认为NetNamedPipeBinding中的StrongWildcard匹配,因此您可能需要传递"+"
而不是"localhost"
。
答案 1 :(得分:0)
您可以尝试使用
列出可用的命名管道吗? String[] listOfPipes = System.IO.Directory.GetFiles(@"\.\pipe\");
然后确定你的命名管道是否在其中?
答案 2 :(得分:-1)
我的解决方案如下:
if (Debugger.IsAttached)
return true;
这将确保在调试期间永远不会运行用于检查服务的代码。
<强> BestRegards 强>