I'm using an old fashioned ASMX WebService (not WCF) which internally calls an unmanaged DLL (via PInvoke). I don't get any exception but it does not work as expected. The same code in a regular WinForms application works fine.
I think it's because of the working directory when debugging the WebService in VisualStudio. The working directory is: C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0 and I guess the unmanaged DLL's are missing. But in the \bin folder of my project all DLL's are there.
Is there a way to tell configure the service accordingly or do I need special access rights?
PS: sorry for the newbie question.
Here's some part of the code:
First: The service accepts some vector graphics that are then projected by laser-projector.
[WebService(Namespace = "http://www.myDomain.eu/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class BeamerService : System.Web.Services.WebService
{
[WebMethod]
public void ShowBounds()
{
// ToDo: construct some graphics
// ToDo: get laser/DAC
DAC dac;
if (!Laser.DAC.TryInitializeAny(out dac))
{
// DAC not connected
return;
}
// ToDo: send graphics to DAC
Second: Get access to the laser-device which is connected to the computer via USB.
public static bool TryInitializeAny(out DAC dac)
{
return UniversalDAC.TryInitialize(ControllerTypes.Netlase, ControllerType.Netlase, out dac);
}
Third: Use unmanaged DLL (not mine) to search for connected devices and acquire the first one. All lines that start with "DrLava" are P.Invoke calls to unmanaged code.
public static bool TryInitialize(ControllerTypes types, ControllerType type, out DAC dac, DeviceChooser deviceChooser = null)
{
try
{
uint deviceCount = 0;
if (DrLava.LDL_Init((uint)ConvertToNativeMask(types), ref deviceCount) != NativeConstants.DAC_OK)
throw new ArgumentException(string.Format("Could not intialize DAC with '{0}'", type));
uint device = deviceChooser == null ? 0 : deviceChooser(deviceCount);
uint tType = 0;
uint tEnum = 0;
var sName = new StringBuilder(128);
if (DrLava.LDL_GetDACInfo(device, sName, (uint)sName.Length, ref tType, ref tEnum) != NativeConstants.DAC_OK)
throw new ArgumentException(string.Format("Could retriev name of DAC device number {0} with '{1}'.", device, type));
if (DrLava.LDL_DAC_Init(device) != NativeConstants.DAC_OK)
throw new ArgumentException(string.Format("Could not intialize DAC with '{0}' using device number {1}", type, device));
dac = new UniversalDAC(type, sName.ToString(), device);
return true;
}
catch (Exception ex)
{
Trace.Error("Could not initialize DAC.Instance with '{0}'. {1}", type, ex);
}
dac = null;
return false;
}
So, the problem is in step 3. If I run the code in a console or WinForms application, the connected USB is found and I can use it. If I run the same code in a WebService, the first line in step 3 (DrLava.LDL_Init) will yield no results.
Related
Getting a list of open windows in .Net Framework on Windows was relatively easy. How can I do the same in .Net Core/.Net 5 or later on macOS?
To clarify, I'm looking for a way to retrieve a list of all open windows owned by any running application/process. I don't have much experience of macOS development - I'm a Windows developer - but I've tried to use the NSApplication as suggested by this answer.
I created a .Net 6.0 Console application in VS2022 on macOS Monterey (12.2), added a reference to Xamarin.Mac and libxammac.dylib as described here - which describes doing this in Xamarin rather than .Net, but I don't see any other option to create a Console application. With the simple code:
static void Main(string[] args)
{
NSApplication.Init();
}
I get the output
Xamarin.Mac: dlopen error: dlsym(RTLD_DEFAULT, mono_get_runtime_build_info): symbol not found
I've no idea what this means. I'm not even sure this approach has any merit.
Does anyone know if it's possible to use NSApplication from a .Net Core/6.0 application, and if so whether NSApplication will give me the ability to read a system-wide list of open windows? If not, is there another way to accomplish this?
This is only for my own internal use, it doesn't need to be in any way portable or stable outside of my own environment.
In the link you refer to, there is an important note:
... as Xamarin.Mac.dll does not run under the .NET Core runtime, it only runs with the Mono runtime.
Because you try to run Xamarin.Mac.dll under .net-core, you get this dlopen error.
No System-wide List via NSApplication
The linked answer with NSApplication.shared.windows is incorrect if you want to read a system-wide list of open windows. It can only be used to determine all currently existing windows for the application from which the call is made, see Apple's documentation.
Alternative solution
Nevertheless, there are several ways to access the Window information in macOS. One of them could be a small unmanaged C-lib that gets the necessary information via CoreFoundation and CoreGraphics and returns it to C# via Platform Invoke (P/Invoke).
Native Code
Here is example code for a C-Lib that determines and returns the names of the window owners.
WindowsListLib.h
extern char const **windowList(void);
extern void freeWindowList(char const **list);
The interface of the library consists of only two functions. The first function called windowList returns a list with the names of the window owners. The last element of the list must be NULL so that you can detect where the list ends on the managed C# side. Since the memory for the string list is allocated dynamically, you must use the freeWindowList function to free the associated memory after processing.
WindowsListLib.c
#include "WindowListLib.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
static void errorExit(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
}
static char *copyUTF8String(CFStringRef string) {
CFIndex length = CFStringGetLength(string);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
char *buf = malloc(size);
if(!buf) {
errorExit("malloc failed");
}
if(!CFStringGetCString(string, buf, size, kCFStringEncodingUTF8)) {
errorExit("copyUTF8String with utf8 encoding failed");
}
return buf;
}
char const **windowList(void) {
CFArrayRef cfWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex count = CFArrayGetCount(cfWindowList);
char const **list = malloc(sizeof(char *) * (count + 1));
if(!list) {
errorExit("malloc failed");
}
list[count] = NULL;
for(CFIndex i = 0; i < count; i++) {
CFDictionaryRef windowInfo = CFArrayGetValueAtIndex(cfWindowList, i);
CFStringRef name = CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
if(name) {
list[i] = copyUTF8String(name);
} else {
list[i] = strdup("unknown");
}
}
CFRelease(cfWindowList);
return list;
}
void freeWindowList(char const **list) {
const char **ptr = list;
while(*ptr++) {
free((void *)*ptr);
}
free(list);
}
CGWindowListCopyWindowInfo is the actual function that gets the window information. It returns a list of dictionaries containing the details. From this we extract kCGWindowOwnerName. This CFStringRef is converted to a dynamically allocated UTF-8 string by the function copyUTF8String.
By convention, calls like CGWindowListCopyWindowInfo that contain the word copy (or create) must be released after use with CFRelease to avoid creating memory leaks.
C# Code
The whole thing can then be called on the C# side something like this:
using System.Runtime.InteropServices;
namespace WindowList
{
public static class Program
{
[DllImport("WindowListLib", EntryPoint = "windowList")]
private static extern IntPtr WindowList();
[DllImport("WindowListLib", EntryPoint = "freeWindowList")]
private static extern void FreeWindowList(IntPtr list);
private static List<string> GetWindows()
{
var nativeWindowList = WindowList();
var windows = new List<string>();
var nativeWindowPtr = nativeWindowList;
string? windowName;
do
{
var strPtr = Marshal.ReadIntPtr(nativeWindowPtr);
windowName = Marshal.PtrToStringUTF8(strPtr);
if (windowName == null) continue;
windows.Add(windowName);
nativeWindowPtr += Marshal.SizeOf(typeof(IntPtr));
} while (windowName != null);
FreeWindowList(nativeWindowList);
return windows;
}
static void Main()
{
foreach (var winName in GetWindows())
{
Console.WriteLine(winName);
}
}
}
}
The GetWindows method fetches the data via a native call to WindowList and converts the C strings to managed strings, then releases the native resources via a call to FreeWindowList.
This function returns only the owner names, such as Finder, Xcode, Safari, etc. If there are multiple windows, the owners will also be returned multiple times, etc. The exact logic of what should be determined will probably have to be changed according to your requirements. However, the code above should at least show a possible approach to how this can be done.
Screenshot
when i load my dll to my other app domain it is loaded correctly
but when i excute the exe which is need to the dll it throw exception
this code is from another question on StackOverFlow
my code :
static void Main(string[] args)
{
AppDomain app = AppDomain.CreateDomain("seco");
//my dll that i want to add to my app domain
string path = #"D:\vs projects\ConsoleApplication17\ConsoleApplication17\bin\Debug\lom.dll";
Type type = typeof(Proxy);
var asmLoaderProxy = (Proxy)app.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
asmLoaderProxy.GetAssembly(path); //load succed here
// the exe that needs to the dll
app.ExecuteAssembly("op.exe"); // -> throw exception FileNotFoundException here
AppDomain.Unload(app);
Console.WriteLine("finished");
Console.ReadKey();
}
[Serializable]
public class Proxy
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
return Assembly.LoadFrom(assemblyPath);
}
catch (Exception)
{
return null;
// throw new InvalidOperationException(ex);
}
}
}
}
what is my mistake ?
note -> the dll is has one class that has one mehtod that print " print hello in dll"
and the exe take instance from the class and call the method
thanks :)
According to your description, I make a test.
However, I find that I can run the code successfully without any exception.
Here I want to mention that we need to use absolute path about "op.exe".
Like:#"D:\sample\TestDll\ConsoleApp1\bin\Debug\op.exe"
Hope this can help you.
I want to access partitioned COM+ applications on a remote server.
I have tried this:
using COMAdmin
using System.Runtime.InteropServices;
_serverName = myRemoteServer;
_partionName = myPartionName;
_message = myMessage;
ICOMAdminCatalog2 catalog = new COMAdminCatalog();
catalog.Connect(_serverName);
string moniker = string.Empty;
string MsgInClassId = "E3BD1489-30DD-4380-856A-12B959502BFD";
//we are using partitions
if (!string.IsNullOrEmpty(_partitionName))
{
COMAdminCatalogCollection partitions = catalog.GetCollection("Partitions");
partitions.Populate();
string partitionId = string.Empty;
foreach (ICatalogObject item in partitions)
{
if (item.Name == _partitionName)
{
partitionId = item.Key;
break;
}
}
if (!string.IsNullOrEmpty(partitionId) )
{
moniker = $"partition:{partitionId}/new:{new Guid(MsgInClassId)}";
try
{
var M = (IMsgInManager)Marshal.BindToMoniker(moniker);
M.AddMsg(_message);
}
catch (Exception ex)
{
throw new Exception($"We can not use: {_partitionName} with Id {partitionId}. {ex.ToString()}");
}
}
else
{
throw;
}
}
else
//we don't have partitions and this will work
{
Type T = Type.GetTypeFromCLSID(new Guid(MsgInClassId), _serverName, true);
var M = (IMsgInManager)Activator.CreateInstance(T);
M.AddMsg(_message);
}
}
So when we are local on the (remote) machine, partitions are working with the moniker and Marshal.BindToMoniker.
But when I try do the same remotely from my machine, I get an error from
Marshal.BindToMoniker that Partitons is not enabled. Because on my machine partitions is not enabled.
Message = "COM+ partitions are currently disabled. (Exception from HRESULT: 0x80110824)"
How can I use Marshal.BindToMoniker to run on the remote server.
Is it something I can add to the moniker string i.e.
moniker = $"server:_server/partition:{partitionId}/new:{new Guid(MsgInClassId)}"
My questions is very simular to this:
COM+ object activation in a different partition
tl;dr
According to MS documentation there is a way to do this by setting the pServerInfo in BIND_OPTS2 structure for binding the moniker. Unfortunately this is not working for the COM class moniker.
see:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms694513(v=vs.85).aspx
where it says for *pServerInfo:
COM's new class moniker does not currently honor the pServerInfo flag.
But maybe just try your scenario and at some future time it might be supported (or already is and documentation is wrong).
also see:
http://thrysoee.dk/InsideCOM+/ch11c.htm
where it also says in the footnote it does not work for class moniker: http://thrysoee.dk/InsideCOM+/footnotes.htm#CH1104
Theory and suggested solution if it was supported in c#
Disclaimer: I couldn't test the code as I don't have a test setup. This is off the top of my head. A bit pseudo code.
To do this you would have to code the COM/Moniker calls yourself. For this you could look at the source of microsofts implementation as a starting point.
There BindToMoniker is implemented like:
public static Object BindToMoniker(String monikerName)
{
Object obj = null;
IBindCtx bindctx = null;
CreateBindCtx(0, out bindctx);
UInt32 cbEaten;
IMoniker pmoniker = null;
MkParseDisplayName(bindctx, monikerName, out cbEaten, out pmoniker);
BindMoniker(pmoniker, 0, ref IID_IUnknown, out obj);
return obj;
}
CreateBindCtx, MkParseDisplayName and BindMoniker are OLE32.dll functions.
IBindCtx has methods to change the binding context. For this you call IBindCtx.GetBindContext(out BIND_OPTS2) and change the settings to what you need. Then set the new binding context with IBindCtx.SetBindContext(BIND_OPTS2). So essentially your own version of code would look something like this (pseudo code):
public static Object BindToMoniker(String monikerName)
{
Object obj = null;
IBindCtx bindctx = null;
CreateBindCtx(0, out bindctx);
BIND_OPTS2 bindOpts;
bindOpts.cbStruct = Marshal.SizeOf(BIND_OPTS2);
bindctx.GetBindOptions(ref bindOpts);
// Make your settings that you need. For example:
bindOpts.dwClassContext = CLSCTX_REMOTE_SERVER;
// Anything else ?
bindOpts.pServerInfo = new COSERVERINFO{pwszName = "serverName"};
bindctx.SetBindOptions(ref bindOpts);
UInt32 cbEaten;
IMoniker pmoniker = null;
MkParseDisplayName(bindctx, monikerName, out cbEaten, out pmoniker);
BindMoniker(pmoniker, 0, ref IID_IUnknown, out obj);
return obj;
}
As said, unfortunately this code is not possible to write in C# out of the box. Even the OLE32.dll method declarations CreateBindCtx, MkParseDisplayName and BindMoniker are privately declared in Marshal.cs so you will have to declare them in your project again.
But we are lucky with the IBindCtx declaration using a BIND_OPTS2 and the BIND_OPTS2 structure definition itself. They are declared in Microsoft.VisualStudio.OLE.Interop (interesting declarations in this namespace anyway). So you can try using them because inside the Marshal object and marshal.cs only the BIND_OPTS structure is used. I don't know if this is part of the framework and redistributable (I doubt it) but for testing this should be good enough. If it works these things can be declared again in your own solution.
Some Info on the used functions:
BindMoniker
CreateBindCtx
MkParseDisplayName
BIND_OPTS2
The remote COM needs to be accessed by Queue or DCOM. You need to export the application proxy on the server when accessing by DCOM. And install the proxy in the client PC.
The COM activation type must be configured as "Server Application" to export application proxy.
After installing application proxy, the client can directly call
moniker = $"new:{new Guid(MsgInClassId)}";
try
{
var M = Marshal.BindToMoniker(moniker);
}
For the partition, it's designed to show each user with own application set. If the current partition is associated to the user, the partition needs not to be written in codes.
I am trying to prevent the user from pinning my .NET app to the taskbar. I've found some code on the Old New Thing that does just that. However, it is in C++.
#include <shellapi.h>
#include <propsys.h>
#include <propkey.h>
HRESULT MarkWindowAsUnpinnable(HWND hwnd)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
PROPVARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
pps->Release();
}
return hr;
}
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
MarkWindowAsUnpinnable(hwnd);
return TRUE;
}
I am having very little luck converting it to c#. Can someone help?
You can download the Windows API Code Pack which has the necessary p/invoke calls you need to translate the code in your post to C#.
Either use the library in whole or find the specific calls and definitions you require (search it for SHGetPropertyStoreForWindow and then its other dependencies).
In the question, the Old New Thing post also talks about how you can set some registry settings on per application basis that will so prevent the pinning an application to the taskbar.
All you have to do is add the value of "NoStartPage" to a key for your application under the Root\Applications. The value can be blank and of any type, if Windows just sees it is there it will not show the ability to pin the app, when the user right clicks on it in the taskbar.
Here is the documentation from Microsoft on this feature: Use Registry to prevent pinning of an application
The one caveat to this is that in Windows 7, due to UAC, you have to run as administrator to update the registry. I did this via the app.manifest.
The code to find the right and update the correct registry keys is below (hopefully it is not too verbose):
public static void Main(string[] args)
{
// Get Root
var root = Registry.ClassesRoot;
// Get the Applications key
var applicationsSubKey = root.OpenSubKey("Applications", true);
if (applicationsSubKey != null)
{
bool updateNoStartPageKey = false;
// Check to see if your application already has a key created in the Applications key
var appNameSubKey = applicationsSubKey.OpenSubKey("MyAppName.exe", true);
if (appNameSubKey != null)
{
// Check to see if the NoStartPage value has already been created
if (!appNameSubKey.GetValueNames().Contains("NoStartPage"))
{
updateNoStartPageKey = true;
}
}
else
{
// create key for your application in the Applications key under Root
appNameSubKey = applicationsSubKey.CreateSubKey("MyAppName.exe", RegistryKeyPermissionCheck.Default);
if (appNameSubKey != null)
{
updateNoStartPageKey = true;
}
}
if (updateNoStartPageKey)
{
// Create/update the value for NoStartPage so Windows will prevent the app from being pinned.
appNameSubKey.SetValue("NoStartPage", string.Empty, RegistryValueKind.String);
}
}
}
Using the WindowsAPICodePack (via NuGet) you need code resembling:
// Ensure the handle is available
new WindowInteropHelper(window).EnsureHandle();
// Prevent the window from being pinned to the task bars
var preventPinningProperty = new PropertyKey(
new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 9);
WindowProperties.SetWindowProperty(window, preventPinningProperty, "1");
Is there a way to set a dependency of one "myWindowsService" to another service running on the same machine such as "SqlService"?
The prob is if you dont know the name of the "sql service" where "myWindowsService" will be installed, but my service depends on that the sql is already running..
thanx
Edit:
Didn't read the question correctly.
A workaround (not the most elegant solution) is to enumerate all services against a known whitelist with known instance names.
Using ManagementObject you could set dependencies (but you have to know the name of the service):
bool SetServiceDependencies(string serviceName, string[] dependencies)
{
try
{
string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
//Uses lazy initialization
ManagementObject mmo = new ManagementObject(new ManagementPath(objPath));
//Get properties to check if object is valid, if not then it throws a ManagementException
PropertyDataCollection pc = mmo.Properties;
}
catch (ManagementException me)
{ //Handle errors
if (me.ErrorCode == ManagementStatus.NotFound) {
//Service not found
}
return false;
}
try
{
object[] wmiParams = new object[11]; //parameters for Win32_Service mmo object Change-parameters
wmiParams[10] = dependencies;
//Should we remove dependencies, use array containging 1 empty string
if (dependencies == null || dependencies.Length == 0)
{
wmiParams[10] = new string[] { "" };
}
//Change dependencies
string returnStatus = mWmiService.InvokeMethod("Change", wmiParams).ToString();
}
catch (Exception)
{
return false;
}
return true;
}
If you are using
sc create <service>
syntax, you can supply other services to this which will make your installed service depend on that service being started.
sc create <service> depend= mssqlserver
This can also be done automatically if you are using the ServiceInstaller classes. There is an area in the properties for that controller to define startup dependencies.
I know you said you might not know the name of the other services, but changing the dependencies is quite simple also.