I need to open a URL in a new browser process. I need to be notified when that browser process quits. The code I'm currently using is the following:
Process browser = new Process();
browser.EnableRaisingEvents = true;
browser.StartInfo.Arguments = url;
browser.StartInfo.FileName = "iexplore";
browser.Exited += new EventHandler(browser_Exited);
browser.Start();
Clearly, this won't due because the "FileName" is fixed to iexplore, not the user's default web browser. How do I figure out what the user's default web browser is?
I'm running on Vista->forward. Though XP would be nice to support if possible.
A bit more context: I've created a very small stand-alone web server that serves some files off a local disk. At the end of starting up the server I want to start the browser. Once the user is done and closes the browser I'd like to quit the web server. The above code works perfectly, other than using only IE.
Thanks in advance!
Ok. I now have working C# code to do what I want. This will return the "command line" you should run to load the current default browser:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
namespace testDefaultBrowser
{
public enum ASSOCIATIONLEVEL
{
AL_MACHINE,
AL_EFFECTIVE,
AL_USER,
};
public enum ASSOCIATIONTYPE
{
AT_FILEEXTENSION,
AT_URLPROTOCOL,
AT_STARTMENUCLIENT,
AT_MIMETYPE,
};
[Guid("4e530b0a-e611-4c77-a3ac-9031d022281b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IApplicationAssociationRegistration
{
void QueryCurrentDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONTYPE atQueryType,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONLEVEL alQueryLevel,
[Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszAssociation);
void QueryAppIsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In] ASSOCIATIONTYPE atQueryType,
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void QueryAppIsDefaultAll(
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void SetAppAsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszSet,
[In] ASSOCIATIONTYPE atSetType);
void SetAppAsDefaultAll(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName);
void ClearUserAssociations();
}
[ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]//
class ApplicationAssociationRegistration
{
}
class Program
{
static void Main(string[] args)
{
IApplicationAssociationRegistration reg =
(IApplicationAssociationRegistration) new ApplicationAssociationRegistration();
string progID;
reg.QueryCurrentDefault(".txt",
ASSOCIATIONTYPE.AT_FILEEXTENSION,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
reg.QueryCurrentDefault("http",
ASSOCIATIONTYPE.AT_URLPROTOCOL,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
}
}
}
Whew! Thanks everyone for help in pushing me towards the right answer!
If you pass a path of the known file type to the (file) explorer application, it will 'do the right thing', e.g.
Process.Start("explorer.exe", #"\\path.to\filename.pdf");
and open the file in the PDF reader.
But if you try the same thing with a URL, e.g.
Process.Start("explorer.exe", #"http://www.stackoverflow.com/");
it fires up IE (which isn't the default browser on my machine).
I know doesn't answer the question, but I thought it was an interesting sidenote.
The way to determine the default browser is explained in this blog post:
http://ryanfarley.com/blog/archive/2004/05/16/649.aspx
From the blog post above:
private string getDefaultBrowser()
{
string browser = string.Empty;
RegistryKey key = null;
try
{
key = Registry.ClassesRoot.OpenSubKey(#"HTTP\shell\open\command", false);
//trim off quotes
browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
if (!browser.EndsWith("exe"))
{
//get rid of everything after the ".exe"
browser = browser.Substring(0, browser.LastIndexOf(".exe")+4);
}
}
finally
{
if (key != null) key.Close();
}
return browser;
}
Ok, I think I might have found it - IApplicationAssociationRegistration::QueryCurrentDefault [1]. According to the docs this is what is used by ShellExecute. I'll post code when I get it to work, but I'd be interested if others think this is the right thing to use (BTW, I'm Vista or greater for OS level).
[1]: http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault
Ok. Been away on the conference circuit for a week, now getting back to this. I can do this with C++ now - and it even seems to behave properly! My attempts to translate this into C# (or .NET) have all failed however (Post On Question).
Here is the C++ code for others that stumble on this question:
#include "stdafx.h"
#include <iostream>
#include <shobjidl.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <AtlDef.h>
#include <AtlConv.h>
using namespace std;
using namespace ATL;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitialize(NULL);
if (!SUCCEEDED(hr)) {
cout << "Failed to init COM instance" << endl;
cout << hr << endl;
}
IApplicationAssociationRegistration *pAAR;
hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration),
(void**) &pAAR);
if (!SUCCEEDED(hr))
{
cout << "Failed to create COM object" << endl;
cout << hr << endl;
return 0;
}
LPWSTR progID;
//wchar_t *ttype = ".txt";
hr = pAAR->QueryCurrentDefault (L".txt", AT_FILEEXTENSION, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for .txt" << endl;
cout << hr << endl;
}
CW2A myprogID (progID);
cout << "Result is: " << static_cast<const char*>(myprogID) << endl;
/// Now for http
hr = pAAR->QueryCurrentDefault (L"http", AT_URLPROTOCOL, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for http" << endl;
cout << hr << endl;
}
CW2A myprogID1 (progID);
cout << "Result is: " << static_cast<const char*>(myprogID1) << endl;
return 0;
}
I will post the C# code when I finally get it working!
I've written this code for a project once... it keeps in mind any additional parameters set for the default browser. It was originally created to open HTML documentation in a browser, for the simple reason I always set my default program for HTML to an editor rather than a browser, and it annoys me to no end to see some program open its HTML readme in my text editor. Obviously, it works perfectly for URLs too.
/// <summary>
/// Opens a local file or url in the default web browser.
/// </summary>
/// <param name="path">Path of the local file or url</param>
public static void openInDefaultBrowser(String pathOrUrl)
{
pathOrUrl = "\"" + pathOrUrl.Trim('"') + "\"";
RegistryKey defBrowserKey = Registry.ClassesRoot.OpenSubKey(#"http\shell\open\command");
if (defBrowserKey != null && defBrowserKey.ValueCount > 0 && defBrowserKey.GetValue("") != null)
{
String defBrowser = (String)defBrowserKey.GetValue("");
if (defBrowser.Contains("%1"))
{
defBrowser = defBrowser.Replace("%1", pathOrUrl);
}
else
{
defBrowser += " " + pathOrUrl;
}
String defBrowserProcess;
String defBrowserArgs;
if (defBrowser[0] == '"')
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.Substring(1).IndexOf('"') + 2).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.Substring(1).IndexOf('"') + 2).TrimStart();
}
else
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.IndexOf(" ")).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.IndexOf(" ")).Trim();
}
if (new FileInfo(defBrowserProcess.Trim('"')).Exists)
Process.Start(defBrowserProcess, defBrowserArgs);
}
}
Short answer, you can't.
If the default browser is, say, Firefox, and the user already has a Firefox instance running, it will just be opened in another window or tab of the same firefox.exe process, and even after they close your page, the process won't exit until they close every window and tab. In this case, you would receive notification of the process exiting as soon as you started it, due to the temporary firefox.exe proc that would marshal the URL to the current process. (Assuming that's how Firefox's single instance management works).
Related
I am going to open the photo viewer using .net core and this is my code
using System.Diagnostics;
namespace TestProcessForOpenPhoto
{
class Program
{
static void Main(string[] args)
{
var photoViewer = new Process();
photoViewer.StartInfo.FileName = #"C:\Program Files\Windows Photo Viewer\PhotoViewer.dll";
photoViewer.StartInfo.Arguments = #" C:\Users\XXXXX\Desktop\TestImage\abc.jpg";
photoViewer.StartInfo.UseShellExecute = false;
photoViewer.Start();
}
}
}
and I got this error message
System.ComponentModel.Win32Exception: 'The specified executable is not a valid application for this OS platform.'
Can anyone help me to fix this bug, thanks
After researching this I noticed folks using rundll32.exe to execute an export from PhotoViewer.dll to display a picture using Microsoft Photo Viewer application. So I think that's what OP was trying to do, they just forgot to use the rundll32.exe application.
So I thought I'd take a crack at this and not use the rundll32.exe and just call the export directly. I debugged it with x86dbg and saw that it's passing in 4 parameters: pointer, pointer, pointer (to wchar_t*), int. I don't know what the parameters do, so I just set them to NULL and made sure to pass in the path to the picture as the 3rd and it seems to work.
So this will do what you want it to do. I know that hard-coding system paths is bad practice, but maybe someone who has more time can make this more dynamic.
private static class WindowsPhotoViewer
{
private const string FilePath32 = #"c:\program files (x86)\Windows Photo Viewer\PhotoViewer.dll";
private const string FilePath64 = #"c:\program files\Windows Photo Viewer\PhotoViewer.dll";
[DllImport(FilePath32, CharSet = CharSet.Unicode, EntryPoint = "ImageView_FullscreenW")]
private static extern void ImageView_Fullscreen32(
IntPtr unknown1, IntPtr unknown2, string path, int unknown3);
[DllImport(FilePath64, CharSet = CharSet.Unicode, EntryPoint = "ImageView_FullscreenW")]
private static extern void ImageView_Fullscreen64(
IntPtr unknown1, IntPtr unknown2, string path, int unknown3);
public static bool ShowImage(FileInfo imageFile)
{
if ((IntPtr.Size == 8) && File.Exists(FilePath64) && imageFile.Exists)
{
ImageView_Fullscreen64(IntPtr.Zero, IntPtr.Zero, imageFile.FullName, 0);
return true;
}
else if ((IntPtr.Size == 4) && File.Exists(FilePath32) && imageFile.Exists)
{
ImageView_Fullscreen32(IntPtr.Zero, IntPtr.Zero, imageFile.FullName, 0);
return true;
}
return false;
}
}
Then you can call it as so:
if(!WindowsPhotoViewer.ShowImage(new FileInfo(#"c:\users\andy\desktop\test.jpg")))
{
Console.WriteLine("Failed to show image");
}
I'm developing in windows 7 and c# in visual studio 2015;
i have a problem when i import a function used in a dll in my other c program:
unsigned long ulaVersion[2];
MHM_GetDLLVersion(ulaVersion);
cout << "MHM_1SL_interface.dll Version: " << ulaVersion[1] << "." << ulaVersion[0] << endl << endl;
In the header file :
unsigned long __stdcall MHM_GetDLLVersion(unsigned long *pulDllVersion);
the function works and print 1.8.
Well, now i try to use with c#
[DllImport(#"MHM_1SL_interface.dll", CallingConvention = CallingConvention.StdCall)]
public static extern UInt32 MHM_GetDLLVersion(ref UInt32 ulaVersion);
I try to use this but i have received "8" with result...
Where i'm wronging?
Which is the right method to pass the ulong* parameter to the function in c#?
Is it possible to send Toast notifications from console application using ToastNotificationManager ?
I know that it is possible to send Toast notifications from Windows Universal app:
var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);
*doc - Toast stored in XML string
To use ToastNotificaionManager I need Windows.UI.Notifications library which I can't reference in console application project.
The library I mentionet before is actualy used by WinRT. Is it possible to use WinRT APIs in Windows console application ?
At first you need to declare that your program will be using winRT libraries:
Right-click on your yourProject, select Unload Project
Right-click on your yourProject(unavailable) and click Edit yourProject.csproj
Add a new property group:<targetplatformversion>8.0</targetplatformversion>
Reload project
Add reference Windows from Windows > Core
Now you need to add this code:
using Windows.UI.Notifications;
and you will be able to send notifications using this code:
var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);
Reference: How to call WinRT APIs in Windows 8 from C# Desktop Applications - WinRT Diagram
I ran into some problems here with Evaldas B's Code I was missing a string.
(Where It Says Need String Here)
.CreateToastNotifier(<needed a string here>).Show(toast);
warning I am kind of new to C# so my code probably sucks- but it does work and is pretty simplistic and that's more than I can say for most solutions I have found
Also I was having a hell of a time getting the xml document to read. I was fighting with System.xml (I think) and Windows.Data.Dom.Xml (also not completely sure).
In the end I settled on making them hard coded strings for my example file and used a switch statement to switch between them.
I have found a ton of people, looking for the solution that I have come up with, on stack overflow. It seems use of the toast notification system with console or background applications would be super useful, and the documentation that surrounds the toast notification system with windows applications all suggest that it needs to be used with an application. The Action Center is super useful for notifications vrs the NotificationTray/NotifyIcon route. I have not found a full solution anywhere else on the web. Here is example code.
/*
At first you need to declare that your program will be using winRT libraries:
1. Right click on your yourProject, select Unload Project
2. Right click on your youProject(unavailable) and click Edit yourProject.csproj
3. Add a new property group:<TargetPlatformVersion>8.0</TargetPlatformVersion>
4. Reload project
5. Add referece Windows from Windows > Core
*/
using System;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Notifications;
namespace ConsoleApplication6
{
public class NewToastNotification
{
public NewToastNotification(string input, int type)
{
string NotificationTextThing = input;
string Toast = "";
switch (type)
{
case 1:
{
//Basic Toast
Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
Toast += NotificationTextThing;
Toast += "</text></binding></visual></toast>";
break;
}
default:
{
Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
Toast += "Default Text String";
Toast += "</text></binding></visual></toast>";
break;
}
}
XmlDocument tileXml = new XmlDocument();
tileXml.LoadXml(Toast);
var toast = new ToastNotification(tileXml);
ToastNotificationManager.CreateToastNotifier("New Toast Thing").Show(toast);
}
}
class Program
{
static void Main(string[] args)
{
NewToastNotification Window = new NewToastNotification("Yes",1);
}
}
}
1) For a toast notification to appear using a console or Desktop application, your application must have a shortcut on the start menu.
2) For an application to have a shortcut icon(not tile icon) in the start menu of Windows, your app must have an AppId.
To create a short cut for you you application create a new class named ShellHelpers.cs and Paste this code in it.
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using MS.WindowsAPICodePack.Internal;
namespace DesktopToastsSample.ShellHelpers
{
internal enum STGM : long
{
STGM_READ = 0x00000000L,
STGM_WRITE = 0x00000001L,
STGM_READWRITE = 0x00000002L,
STGM_SHARE_DENY_NONE = 0x00000040L,
STGM_SHARE_DENY_READ = 0x00000030L,
STGM_SHARE_DENY_WRITE = 0x00000020L,
STGM_SHARE_EXCLUSIVE = 0x00000010L,
STGM_PRIORITY = 0x00040000L,
STGM_CREATE = 0x00001000L,
STGM_CONVERT = 0x00020000L,
STGM_FAILIFTHERE = 0x00000000L,
STGM_DIRECT = 0x00000000L,
STGM_TRANSACTED = 0x00010000L,
STGM_NOSCRATCH = 0x00100000L,
STGM_NOSNAPSHOT = 0x00200000L,
STGM_SIMPLE = 0x08000000L,
STGM_DIRECT_SWMR = 0x00400000L,
STGM_DELETEONRELEASE = 0x04000000L,
}
internal static class ShellIIDGuid
{
internal const string IShellLinkW = "000214F9-0000-0000-C000-000000000046";
internal const string CShellLink = "00021401-0000-0000-C000-000000000046";
internal const string IPersistFile = "0000010b-0000-0000-C000-000000000046";
internal const string IPropertyStore = "886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99";
}
[ComImport,
Guid(ShellIIDGuid.IShellLinkW),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellLinkW
{
UInt32 GetPath(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
int cchMaxPath,
//ref _WIN32_FIND_DATAW pfd,
IntPtr pfd,
uint fFlags);
UInt32 GetIDList(out IntPtr ppidl);
UInt32 SetIDList(IntPtr pidl);
UInt32 GetDescription(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
int cchMaxName);
UInt32 SetDescription(
[MarshalAs(UnmanagedType.LPWStr)] string pszName);
UInt32 GetWorkingDirectory(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
int cchMaxPath
);
UInt32 SetWorkingDirectory(
[MarshalAs(UnmanagedType.LPWStr)] string pszDir);
UInt32 GetArguments(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs,
int cchMaxPath);
UInt32 SetArguments(
[MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
UInt32 GetHotKey(out short wHotKey);
UInt32 SetHotKey(short wHotKey);
UInt32 GetShowCmd(out uint iShowCmd);
UInt32 SetShowCmd(uint iShowCmd);
UInt32 GetIconLocation(
[Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszIconPath,
int cchIconPath,
out int iIcon);
UInt32 SetIconLocation(
[MarshalAs(UnmanagedType.LPWStr)] string pszIconPath,
int iIcon);
UInt32 SetRelativePath(
[MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
uint dwReserved);
UInt32 Resolve(IntPtr hwnd, uint fFlags);
UInt32 SetPath(
[MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[ComImport,
Guid(ShellIIDGuid.IPersistFile),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFile
{
UInt32 GetCurFile(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile
);
UInt32 IsDirty();
UInt32 Load(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
[MarshalAs(UnmanagedType.U4)] STGM dwMode);
UInt32 Save(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
bool fRemember);
UInt32 SaveCompleted(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
}
[ComImport]
[Guid(ShellIIDGuid.IPropertyStore)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IPropertyStore
{
UInt32 GetCount([Out] out uint propertyCount);
UInt32 GetAt([In] uint propertyIndex, out PropertyKey key);
UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv);
UInt32 Commit();
}
[ComImport,
Guid(ShellIIDGuid.CShellLink),
ClassInterface(ClassInterfaceType.None)]
internal class CShellLink { }
public static class ErrorHelper
{
public static void VerifySucceeded(UInt32 hresult)
{
if (hresult > 1)
{
throw new Exception("Failed with HRESULT: " + hresult.ToString("X"));
}
}
}
}
Code for creating a shortcut(This code can be added to the same class where you will be showing the toast)
public bool TryCreateShortcut()
{
String shortcutPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft\\Windows\\Start Menu\\Programs\\FixSus Toasts Sample .lnk";
if (!File.Exists(shortcutPath))
{
InstallShortcut(shortcutPath);
return true;
}
return false;
}
private void InstallShortcut(String shortcutPath)
{
// Find the path to the current executable
String exePath = Process.GetCurrentProcess().MainModule.FileName;
IShellLinkW newShortcut = (IShellLinkW)new CShellLink();
// Create a shortcut to the exe
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath));
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));
// Open the shortcut property store, set the AppUserModelId property
IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;
using (PropVariant appId = new PropVariant(APP_ID))
{
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(SystemProperties.System.AppUserModel.ID, appId));
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
// Commit the shortcut to disk
IPersistFile newShortcutSave = (IPersistFile)newShortcut;
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true));
}
Now you can create an show a toast
// Get a toast XML template
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText04);
// Fill in the text elements
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
stringElements[1].AppendChild(toastXml.CreateTextNode("Message" + newMessage));
// Specify the absolute path to an image
string codeWebFolderPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, #"..\..\"));
String imagePath = "file:///" + Path.GetFullPath(codeWebFolderPath+ "Resources\\FixSus.png");
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
imageElements[0].Attributes.GetNamedItem("src").NodeValue = imagePath;
// Create the toast and attach event listeners
ToastNotification toast = new ToastNotification(toastXml);
toast.Activated += ToastActivated;
toast.Dismissed += ToastDismissed;
toast.Failed += ToastFailed;
// Show the toast. Be sure to specify the AppUserModelId on your application's shortcut!
ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);
The APP_ID can be any string. In my case it was "NotificationTest.KEY"
Note: Dont modify the ShellHelper class.
Edit : Follow Evaldas B's answer first then apply this solution.
I'm writing a C# com library. I need a function that takes string from C++ and modifies string, after I have modified this string in my library modified value should be usable by C++ code. Here is my function (it's not working),
C# Interface,
Int32 testString([param: MarshalAs(UnmanagedType.LPStr)] ref string strData);
C# Implementation,
class Test:ITest
{
public int testString(ref string strData)
{
strData = strData + strData;
Console.WriteLine("strData in lib :"+strData);
return 0;
}
}
C++ code (I missed the parts, registering com lib, importing tlb etc.),
ITest * mInterface = NULL;
//...initilizing interface , skipped since this parts has no problem
LPSTR tt = "OLA";
mInterface ->testString(&tt); //It crashes when comes here ..
cout << tt <<endl;
I have found a solution to my problem , I am not sure but maybe we cannot initilized object from c++ to c# using com interop.If a use a null pointer to c# com , and initilize that pointer inside c# library code it worked .You can see the sample below ;
c# library interface
int testString(out string val);
c# library imp
class Test
{
int testString(out string val)
{
val = "Hello world"; //now user of this method can use val
return 0;
}
}
c++ usage
#pragma warning (disable: 4278)
#import "../yourtlb.tlb" no_namespace named_guids
ITestInterface * fpInterface = NULL;
int retval = 1;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_Test ,
NULL, CLSCTX_INPROC_SERVER,
IID_ITestInterface, reinterpret_cast<void**>(&fpInterface));
if(!FAILED(hr))
{
BSTR val = NULL;
int ret = fpInterface->testString(val);
cout <<val<<endl;
}
Of course before using code in c++ , dont forget to generate tlb file from your c# project , and dont forget to edit registry files.Here you can see a good sample
How to find available COM ports in my PC? I am using framework v1.1. Is it possible to find all COM ports? If possible, help me solve the problem.
Framework v1.1 AFAIK doesn't allow you to do this.
In 2.0 there is a static function
SerialPort.GetPortNames()
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.getportnames.aspx
As others suggested, you can use WMI. You can find a sample in CodeProject
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\WMI",
"SELECT * FROM MSSerial_PortName");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("PortName: {0}", queryObj["PortName"]);
//If the serial port's instance name contains USB
//it must be a USB to serial device
if (queryObj["InstanceName"].ToString().Contains("USB"))
{
Console.WriteLine(queryObj["PortName"] + "
is a USB to SERIAL adapter/converter");
}
}
}
catch (ManagementException e)
{
Console.WriteLine("An error occurred while querying for WMI data: " + e.Message);
}
The available serial ports can also be found at the values at the HKEY_LOCAL_MACHINE\hardware\devicemap\serialcomm key in the registry.
How about asking a straight question from operating system:
using System;
using System.Collections.Generic;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public class MyClass
{
private const uint GENERIC_ALL = 0x10000000;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint GENERIC_EXECUTE = 0x20000000;
private const int OPEN_EXISTING = 3;
public const int INVALID_HANDLE_VALUE = -1;
public static void Main()
{
for (int i = 1; i <= 32; i++)
Console.WriteLine ("Port {0}: {1}", i, PortExists (i));
}
private static bool PortExists (int number) {
SafeFileHandle h = CreateFile (#"\\.\COM" + number.ToString (), GENERIC_READ + GENERIC_WRITE,
0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
bool portExists = !h.IsInvalid;
if (portExists)
h.Close ();
return portExists;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern SafeFileHandle CreateFile (string lpFileName, System.UInt32 dwDesiredAccess,
System.UInt32 dwShareMode, IntPtr pSecurityAttributes, System.UInt32 dwCreationDisposition,
System.UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile);
}
WMI contains a lot of hardware information. Query for instances of Win32_SerialPort.
(OTOH I can't recall how much WMI query support was in .NET 1.1.)
There is no support for SerialPort communication in .net v1.1. The most common solution for this was to use the MSCOMMCTL active X control from a VB6.0 installation (import into your .net project as a COM component from the add reference dialog box).
In later versions the Serial Port support is available through the System.IO.Ports name space. Also please note there is no API which will get you the list of free ports.
You can get a list of all the port names and then try opening a connection. An exception occurs if the port is already in use.
Since you are using .net 1.1 one option is to use the AxMSCommLib control.
Here is a web page that assisted me in starting to use AxMSCommLib control. There is even a FindDevicePort() method listed that can be easily modified.
I have since switched to System.IO.Ports which appears to be much more robust.
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=320
Thanks
Joe
Use QueryDosDevice API function. This is a VB6 snippet:
ReDim vRet(0 To 255)
sBuffer = String(100000, 1)
Call QueryDosDevice(0, sBuffer, Len(sBuffer))
sBuffer = Chr$(0) & sBuffer
For lIdx = 1 To 255
If InStr(1, sBuffer, Chr$(0) & "COM" & lIdx & Chr$(0), vbTextCompare) > 0 Then
vRet(lCount) = "COM" & lIdx
lCount = lCount + 1
End If
Next
Maybe you will find this useful?
I am showing you a simple way to check all the COM ports in you PC. To get started follow these steps:
Create a WinForms application in Visual Studio.
Darg and drop a comboBox in your form and name it comboBoxCOMPORT
Copy the following code and paste after the public Form1() method (autogenerated).
private void Form1_Load(object sender, EventArgs e)
{
string[] ports = SerialPort.GetPortNames();
comboBoxCOMPORT.Items.AddRange(ports);
}
Run the app and click on drop down arrow on the comboBox to reveal all the available COM PORTS.
The above method works for Edgeport USB-to-serial converters as well as virtual ports. I implemented this in my project and works smoothly. Let me know if I can provide any further assistance.