I have a plugin for a third-party application that calls a C#.NET method and I need to get the instance from the calling application.
_MapInfoApplication = (MapInfo.MapInfoApplication) System.Runtime.InteropServices.Marshal.GetActiveObject("Mapinfo.application");
Gives me an active instance from the application, but in the case there is two open instances of the application I can't know if I got the right one, is there way to determine who called?
Use the System.Runtime.InteropServices.Marshal.GetObjectForIUnknown method, passing an object pointer using the IDispatchID from MapInfo as a parameter:
public static void MINote(int MIWindowID, string Message)
{
System.IntPtr MIDispatchPtr = new IntPtr(MIWindowID);
DMapInfo MIConnection = (DMapInfo)Marshal.GetObjectForIUnknown(MIDispatchPtr);
MIConnection.Do(String.Format("Note \"Note from CSharp: {0}\"",Message));
DMBApplications Applications = (DMBApplications) MIConnection.MBApplications;
foreach (DMapBasicApplication mbApp in Applications)
{
MIConnection.Do(String.Format("Note \"MB App. running in this MapInfo instance: {0}\"", mbApp.Name));
}
}
Related
I am writing some integration tests for my web API, which means that it has to be running during the execution of the tests. Is there any way to run it with an in-memory database instead of a real one based on SQL Server?
Also, I need to run a few instances at a time, so I need somehow to change the base address of each of them to be unique. For example, I could append to the base URL these instance IDs, that are mentioned in the code below.
Here is the code which I am using to run a new instance for my tests:
public static class WebApiHelper
{
private const string ExecutableFileExtension = "exe";
private static readonly Dictionary<Guid, Process> _instances = new();
public static void EnsureIsRunning(Assembly? assembly, Guid instanceId)
{
if (assembly is null)
throw new ArgumentNullException(nameof(assembly));
var executableFullName = Path.ChangeExtension(
assembly.Location, ExecutableFileExtension);
_instances.Add(instanceId, Process.Start(executableFullName));
}
public static void EnsureIsNotRunning(Guid instaceId)
=> _instances[instaceId].Kill();
}
Talking in general, is this a good way to create test instances, or maybe I am missing something? Asking this, because maybe there is another 'legal' way to achieve my goal.
Okay, so in the end, I came up with this super easy and obvious solution.
As was mentioned in the comments - using the in-memory database is not the best way to test, because relational features are not supported if using MS SQL.
So I decided to go another way.
Step 1: Overwrite the connection strings.
In my case, that was easy since I have a static IConfiguration instance and was need just to overwrite the connection string within that instance.
The method looks as follows:
private const string ConnectionStringsSectionName = "ConnectionStrings";
private const string TestConnectionStringFormat = "{0}_Test";
private static bool _connectionStringsOverwitten;
private static void OverwriteConnectionStrings()
{
if (_connectionStringsOverwitten)
return;
var connectionStrings = MyStaticConfigurationContainer.Configuration
.AsEnumerable()
.Where(entry => entry.Key.StartsWith(ConnectionStringsSectionName)
&& entry.Value is not null);
foreach (var connectionString in connectionStrings)
{
var builder = new SqlConnectionStringBuilder(connectionString.Value);
builder.InitialCatalog = string.Format(TestConnectionStringFormat,
builder.InitialCatalog);
MyStaticConfigurationContainer.Configuration[connectionString.Key] = builder.ConnectionString;
}
_connectionStringsOverwitten = true;
}
Of course, you would need to handle the database creation and deletion before and after running the tests, otherwise - your test DBs may become a mess.
Step 2: Simply run your web API instance within a separate thread.
In my case, I am using the NUnit test framework, which means I just need to implement the web API setup logic within the fixture. Basically, the process would be more or less the same for every testing framework.
The code looks as follows:
[SetUpFixture]
public class WebApiSetupFixture
{
private const string WebApiThreadName = "WebApi";
[OneTimeSetUp]
public void SetUp() => new Thread(RunWebApi)
{
Name = WebApiThreadName
}.Start();
private static void RunWebApi()
=> Program.Main(Array.Empty<string>());
// 'Program' - your main web app class with entry point.
}
Note: The code inside Program.Main(); will also look for connection strings in the MyStaticConfigurationContainer.Configuration which was changed in the previous step.
And that's it! Hope this could help somebody else :)
I am currently using UWP Toolkit to navigate among app pages. There is a page that is being used for initializing and opening RaspberryPi GPIO pins. The following error occurs after navigating away from that page then trying to navigate back to it again.
The process cannot access the file because it is being used by another process .\r\n\r\nPin ' is currently opened in an incompatible sharing mode. Make sure this pin is not already in use by this application or another application
I can see that the constructor is being called each time the page is visited and hence there is an attempt to open pins that are already opened. What is the best way to overcome this issue?
You may add NavigationCacheMode = NavigationCacheMode.Required; to the ctor of the page so your app will not create a new instance of it when you navigate there.
What I always do is let a class deal with managing pins so that your user code can request pins to act on.
public class IO
{
private readonly GpioController _gpioController;
private readonly Dictionary<int, GpioPin> _pins;
public IO(GpioController gpioController)
{
_gpioController = gpioController;
_pins = new Dictionary<int, GpioPin>();
}
public GpioPin OpenPin(int pin, GpioSharingMode mode)
{
if (_pins.ContainsKey(pin))
{
var gpioPin = _pins[pin];
if (gpioPin.SharingMode == mode)
{
return gpioPin;
}
throw new ArgumentException($"Pin '{pin}' is already configured in mode '{gpioPin.SharingMode}'");
}
else
{
var gpioPin = _gpioController?.OpenPin(pin, mode);
_pins[pin] = gpioPin;
return gpioPin;
}
}
}
Then my viewmodels simply request a pin as follows
public MainViewModel()
{
_io = ServiceContainer.Instance.Get<IO>();
_brakingPin = _io.OpenPin(4, GpioSharingMode.Exclusive);
_io.SetDriveMode(_brakingPin, GpioPinDriveMode.Output);
_io.Write(_brakingPin, GpioPinValue.Low);
}
I have three vendor supplied ole com libraries that provide an interface to different but similar devices. These libraries are not compatible between devices, and have differing functions embedded. They also all share the same ID's, so I can only register one on a machine at a time. Unfortunately I do not have the source and have no way of recompiling these.
My problem is that quite often I need to be able to communicate with more than one model of device.
I've tried loading the dll from a specific path and converting it to an assembly, creating an instance of it and pulling out the methodinfo for the functions I need. This appears to work. The type information is different for the different libraries as expected, but whenever I instantiate it it still appears to only load the registered dll, since it crashes and burns with an access violation if I call a function that is not in the currently registered dll. Does anyone have any ideas?
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
public static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind, [MarshalAs(UnmanagedType.Interface)] out Object typeLib);
public enum RegKind
{
RegKind_Default = 0,
RegKind_Register = 1,
RegKind_None = 2
}
private void Initialize() {
Type ct;
object typeLib;
try {
// Load DLL
LoadTypeLibEx(#"c:\temp\zkA11C\zkemkeeper.dll", RegKind.RegKind_None, out typeLib);
if (typeLib == null)
throw new Exception("LoadTypeLibEx returned null pointer");
TypeLibConverter converter = new TypeLibConverter();
ConversionEventHandler eventHandler = new ConversionEventHandler();
AssemblyBuilder zkA11C = converter.ConvertTypeLibToAssembly(typeLib, "zkA11C.dll", 0, eventHandler, null, null, null, null);
// Get class type
ct = zkA11C.GetType("zkA11C.CZKEMClass");
// Create instance of the class
dllClassInstance = Activator.CreateInstance(ct);
// Get method info for functions
fmiConnect = ct.GetMethod("Connect_Net");
...
}
catch (Exception e) {
throw new Exception("Failed to load library: " + e.Message);
}
}
i am using the arcGIS api to make a plugin for arcFM, when i try to run this code
Type t = Type.GetTypeFromProgID("esriFramework.AppRef");
System.Object obj = Activator.CreateInstance(t);
pApp = obj as IApplication;
i get
System.Runtime.InteropServices.COMException(0x8000FFFF): Creating an instance of the component with CLSID {Appref CLSID HERE} from the IClassFactory faileddue to the following error: 8000ffff
Thanks
This was impossible i needed to be using arcMap not ArcFM
In the AppRef CoClass documentation, it says:
Note you can only use the AppRef
object if your code is running inside
one of the ArcGIS application
processes.
Forum posts seem to confirm that this is the same error which is seen when this constraint has been violated:
From http://forums.esri.com/Thread.asp?c=93&f=1729&t=217861:
It is my understanding that there is
indeed no way to access the
IApplication instance from a
geoprocessing script.
In theory, if your task is purely
geoprocessing, you should be able to
do it all without accessing the
IApplication object.
It looks like the OP of the above forum post was able to get around their problem by "using IToolboxWorkspace and accessing directely the Esri-toolboxes". This was her code:
public IGPTool GetTool(string _sToolName, string _sToolboxName)
{
IWorkspaceFactory pGPTFact;
IToolboxWorkspace pToolboxWorkspace;
IGPToolbox pGPToolbox;
IGPTool pGPTool;
pGPTFact = new ToolboxWorkspaceFactoryClass();
pToolboxWorkspace = pGPTFact.OpenFromFile(
ArcGISInstallFolder + #"\ArcToolbox\Toolboxes", 0) as IToolboxWorkspace;
pGPToolbox = pToolboxWorkspace.OpenToolbox(_sToolboxName);
pGPTool = pGPToolbox.OpenTool(_sToolName);
return pGPTool;
}
private string ArcGISInstallFolder
{
get
{
if (string.IsNullOrEmpty(this.m_sArcGISInstallFolder))
{
Microsoft.Win32.RegistryKey regkey;
regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
#"Software\ESRI\ArcGIS", false);
this.m_sArcGISInstallFolder = regkey.GetValue("InstallDir") as String;
}
return this.m_sArcGISInstallFolder;
}
}
Perhaps you can accomplish your goal either without the AppRef object or by running your script from inside the application.
Recently I have observed the following interesting scenario in one of the application I'm developing using .NET 3.5. In this particualr application I have a singletion object which I access as a static variable. I exepected that the .NET run time should initializes this singleton object at the very first time I access it, but it seems this is not the case. .NET runtime initialize it way before I access this particualr object. Following is some peudo code,
if(!initSingleton)
//Do some work without using the singletion class.
else
//Do some work using the singletion class.
Even at runtime my code only executes the code in side the if statement .NET runtime still initializes the singleton object. In some of the application runs I don't need to access this pariticualr object at all!
Also I don't see this behavior with debug builds. Seems this has something to do with optimized builds (release builds).
Is this is the expected behavior of the .NET runtime?
Update:
Following is the actual code,
private void InitServiceClient(NetworkCredential credentials, bool https)
{
string uri = currentCrawlingWebUrl;
if (!uri.EndsWith("/"))
uri = string.Concat(uri, "/");
uri = string.Concat(uri, siteDataEndPointSuffix);
siteDataService = new SiteData.SiteDataSoapClient();
siteDataService.Endpoint.Address = new EndpointAddress(uri);
if (credentials != null)
{
siteDataService.ClientCredentials.Windows.ClientCredential = credentials;
}
else if (MOSSStateHandler.Instance.UserName.Length > 0 && MOSSStateHandler.Instance.Password.Length > 0)
{
siteDataService.ClientCredentials.Windows.ClientCredential.UserName = MOSSStateHandler.Instance.UserName;
siteDataService.ClientCredentials.Windows.ClientCredential.Password = MOSSStateHandler.Instance.Password;
siteDataService.ClientCredentials.Windows.ClientCredential.Domain = MOSSStateHandler.Instance.Domain;
}
BasicHttpBinding httpBinding = (BasicHttpBinding)siteDataService.Endpoint.Binding;
httpBinding.Security.Mode = (https ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.TransportCredentialOnly);
string authmode = MOSSConnectorConfiguration.Instance.Config.GetString(ConfigConstants.SHAREPOINT_AUTH_PROVIDER, "ntlm");
if (authmode.Equals("ntlm", StringComparison.OrdinalIgnoreCase))
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
else if (authmode.Equals("kerberos", StringComparison.OrdinalIgnoreCase))
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
else
throw new Exception(string.Format("Not supported"));
}
Even though my application doesn't execute the code in side else if block the class MOSSStateHandler get initialized.
I suggest you read Jon Skeet's article about Singleton pattern in C#, and its appendix about lazy-loading etc. You'll get better understanding about the implementation issues.
From C# language spec:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
An instance of the class is created.
Any of the static members of the class are referenced.