How to connect to windows phone 7 from a 64-bit application - c#

I have a 32-bit program (written in C++) that can connect to some different devices and as long as it is 32-bit everything works fine. However, now I need to build it as a 64-bit program but then I came across some problems with Windows Phone 7.
I found out that a dll (written in C#) that I rebuilt as 64-bit throws exception at this line:
MultiTargetingConnectivity connectivity = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
The exception is:
An unhandled exception of type 'Microsoft.SmartDevice.Connectivity.DatastoreException' occurred in Microsoft.SmartDevice.Connectivity.dll
Additional information: Retrieving the COM class factory for component with CLSID {349AB2E8-71B6-4069-AD9C-1170849DA64C} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
(For example, if I try to run this example program it works in 32-bit but throws that exception in 64-bit at the same line)
When I searched for that CLSID in the registry I found a path to to "C:\Program Files (x86)\Common Files\Microsoft Shared\Phone Tools\CoreCon\11.0\Bin\ConMan2.dll" so I registered that dll using regsvr32 but I still get the same exception.
UPDATE:
Since I might need to create a workaround instead of finding a 64bit version of ConMan2.dll, I post a bit of my current dll here if anybody can show me a possible workaround so that it will work in both 32 and 64 bit.
namespace WP7DLL
{
// Interface declaration.
[Guid("11111111-1111-1111-1111-111111111111")]
public interface IWP7DLL
{
int GetStatus();
};
[ClassInterface(ClassInterfaceType.None)]
[Guid("22222222-2222-2222-2222-222222222222")]
public class WP7DLL : IWP7DLL
{
public WP7DLL() { }
public int GetStatus()
{
//Line that gives an exception in 64 bit
MultiTargetingConnectivity connectivity = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
...
...
}
}
}

The COM server with CLSID = {349AB2E8-71B6-4069-AD9C-1170849DA64C} is implemented in C:\Program Files (x86)\Common Files\Microsoft Shared\Phone Tools\CoreCon\11.0\Bin\ConMan2.dll
There’s no 64-bit version of that DLL.
And you can’t use a 32-bit DLLs directly from a 64 bit process.
There’s a workaround. You can create another project, 32-bit EXE, that will call that 32-bit DLL however you want, and implement any IPC to interact with your main 64-bit application.
For the specific IPC mechanism, if you only need to invoke a single relatively long task and wait for it to complete, command-like app + command-line arguments + exit code may me enough for you.
If you need to issue many calls, I’d choose WCF over named pipe transport. If you’ll choose this way, below is some sample code implementing that .EXE.
/// <summary>The class from the shared assembly that defines WCF endpoint, and named events</summary>
public static class InteropShared
{
// Host signals it's ready and listening. Replace the zero GUID with a new one
public static readonly EventWaitHandle eventHostReady = new EventWaitHandle( false, EventResetMode.AutoReset, #"{00000000-0000-0000-0000-000000000000}" );
// Client asks the host to quit. Replace the zero GUID with a new one
public static readonly EventWaitHandle eventHostShouldStop = new EventWaitHandle( false, EventResetMode.AutoReset, #"{00000000-0000-0000-0000-000000000000}" );
const string pipeBaseAddress = #"net.pipe://localhost";
/// <summary>Pipe name</summary>
// Replace the zero GUID with a new one.
public const string pipeName = #"00000000-0000-0000-0000-000000000000";
/// <summary>Base addresses for the hosted service.</summary>
public static Uri baseAddress { get { return new Uri( pipeBaseAddress ); } }
/// <summary>Complete address of the named pipe endpoint.</summary>
public static Uri endpointAddress { get { return new Uri( pipeBaseAddress + '/' + pipeName ); } }
}
static class Program
{
/// <summary>The main entry point for the application.</summary>
[STAThread]
static void Main()
{
// The class implementing iYourService interface that calls that 32-bit DLL
YourService singletoneInstance = new YourService();
using( ServiceHost host = new ServiceHost( singletoneInstance, InteropShared.baseAddress ) )
{
// iYourService = [ServiceContract]-marked interface from the shared assembly
host.AddServiceEndpoint( typeof( iYourService ), new NetNamedPipeBinding(), InteropShared.pipeName );
host.Open();
InteropShared.eventHostReady.Set();
// Wait for quit request
InteropShared.eventHostShouldStop.WaitOne();
host.Close();
}
}
}

Related

Problem with building C# code in Windows Service App

I have some server app. This app run, read some files, create C# classes, build this and load assembly. This app can work in two modes - one mode is window desktop application, and other mode - as windows service but core in dll is common.
Sometimes when this app work long time as service, and machine server has long timeup, they can't build anything. I attach to debugger, and debug. I debug .NET source (CompileAssemblyFromSource), and I see, that .NET classes just run csc.exe process with some params (CSharpCodeProvider class), but csc.exe run, return no errors or exceptions, output is blank and nothing is happend. No assembly is build.
I wrote some dump test service to compile code:
namespace CompilerService
{
public class Compiler
{
private Task _compilerTask;
public Compiler()
{
_compilerTask = Task.Run(() => CompileHalloWorld());
}
private const string _workingDir = #"C:\tmp";
private void CompileHalloWorld()
{
System.Threading.Thread.Sleep((30000));
if (!Directory.Exists(_workingDir))
{
Directory.CreateDirectory(_workingDir);
}
Directory.SetCurrentDirectory(_workingDir);
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = null;
try
{
results = csc.CompileAssemblyFromSource(parameters,
#"using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine(""Hallo World!"");
}
}");
}
catch (Exception e)
{
int a = 2;
}
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
}
This dump service is fail too with build hallo world in this state of machine.
After restart machine, all work again ok, compile and load assembly all the time. After few weeks, problem come back, and we must reset server. This problem is on only one machine. On otger machines this service and csc.exe work perfect from years.
If machine is in this wird state, csc.exe dont build in windows service app, but when We run this app as Windows Desktop App all work fine, and csc.exe build normal...
Can you tell me, this is some known issue, oraz is some solution of don't compile csc.exe without machine restart?

Retrieving the COM class factory for component 80040154 Class not registered - DLL is definitely in registry and app is using x86

I'm trying to integrate a Brother QL-700 label printer into a C# application that I'm writing. I've created a console application to test the printer with and added the following code (taken from the SDK on the Brother website):
class Program
{
private const string TEMPLATE_DIRECTORY = #"c:\program files\brother bpac3 sdk\templates\";
private const string TEMPLATE_FRAME = #"NamePlate2.LBX";
static void Main(string[] args)
{
string templatePath = TEMPLATE_DIRECTORY;
templatePath += TEMPLATE_FRAME;
IDocument doc = new bpac.Document();
if (doc.Open(templatePath) != false)
{
doc.GetObject("objCompany").Text = "Company";
doc.GetObject("objName").Text = "Your name here...";
doc.StartPrint("", PrintOptionConstants.bpoDefault);
doc.PrintOut(1, PrintOptionConstants.bpoDefault);
doc.EndPrint();
doc.Close();
}
else
{
Console.WriteLine("Open() error: " + doc.ErrorCode);
Console.ReadLine();
}
}
}
The application stops on the line where I create a new instance of bpac.Document and it throws the following error message:
System.Runtime.InteropServices.COMException: 'Retrieving the COM class factory for component with CLSID {B940C105-7F01-46FE-BF41-E040B9BDA83D} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).'
There are several other posts on Stack Overflow about this issue and I have tried some of the commonly suggested fixes:
The DLL in question is definitely in the Windows registry
I have changed the application architecture on the console application to x86
I'm still unable to make any progress. Does anyone else have any other ideas?

Inno Setup - External .NET DLL with dependencies

I am trying to use a custom DLL in a Inno Setup script during installation. I wrote a very simple function that basically checks a connection string for a MySQL database using MySQL .NET connector (there is no MySQL client on the target server). The code of this exported function is:
public class DbChecker
{
[DllExport("CheckConnexion", CallingConvention.StdCall)]
public static int CheckConnexion([MarshalAs(UnmanagedType.LPStr)] string connexionString)
{
int success;
try
{
MySqlConnection connection = new MySqlConnection(connexionString);
connection.Open();
connection.Close();
success = 0;
}
catch (Exception)
{
success = 1;
}
return success;
}
}
The function is imported this way in Inno Setup :
[Files]
Source: "..\..\MyDll\bin\x86\Release\*"; Flags: dontcopy;
and
[Code]
function CheckConnexion(connexionString: AnsiString): Integer;
external 'CheckConnexion#files:MyDll.dll,MySql.Data.dll stdcall setuponly loadwithalteredsearchpath';`
The problem is that the setup throws an exception at runtime:
Runtime Error (at 53:207):
External exception E0434352.
I think I have to use the files prefix because the function is called in the NextButtonClick event handler, before files are copied to the {app} directory.
Both MyDll.dll and MySql.Data.dll are correctly extracted to the {tmp} directory at runtime.
I tried both with and without the loadwithalteredsearchpath flag with the same result.
What I found is that this error code is a generic .NET runtime error code.
If I remove the part using MySql.Data it works perfectly fine (except that it does nothing...)
As advised on other threads I've been trying to log the error in my .NET code using EventLog and UnhandledException but I have the same exception no matter what (and no log source is created), even without the MySQL part. I checked EventLog permissions on my computer.
It seems that the exception is thrown as soon as I use anything else that "basic" C# code (whenever I try to load another DLL).
There is probably a better way, but this will do.
Implement an initialization function (Init here) that sets up AppDomain.AssemblyResolve handler that looks for an assembly in the path of the main (executing) assembly:
[DllExport("Init", CallingConvention.StdCall)]
public static void Init()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
string location = Assembly.GetExecutingAssembly().Location;
AssemblyName name = new AssemblyName(args.Name);
string path = Path.Combine(Path.GetDirectoryName(location), name.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
Import it to the Inno Setup:
procedure Init(); external 'Init#files:MyDll.dll stdcall setuponly';
And call it before calling the function that needs the dependency (CheckConnexion).
Another solution might be this:
Embedding DLLs in a compiled executable
Btw, no need for the loadwithalteredsearchpath flag. It has no effect on .NET assemblies imo. They are needed for native DLL dependencies: Loading DLL with dependencies in Inno Setup fails in uninstaller with "Cannot import DLL", but works in the installer.
I found something else that might be helpful for anyone stumbling upon this page.
In my scenario, I have several C# methods that I call from InnoSetup using DllExport. In one of those methods, I call another of the methods. This caused Inno to throw "External exception E0434352".
If I moved the code to a method not called by InnoSetup, everything worked fine.
So...
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
Fu();
}
...causes InnoSetup to cry, but:
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
LocalFu();
}
private static int LocalFu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
// Stuff
LocalFu();
// Other stuff
}
...is fine.
I don't know if this is caused by Inno or DllExport, so I'll forgo direct derision and blame society as a whole for my lost morning. (Or myself for being a new to this thing.)
I would like to expand upon Martin's answer. There is a way to resolve the assemblies without having to call an Init method first and that is by including a static constructor in your .NET class:
public class MyClass
{
static MyClass()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += MyResolveEventHandler;
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
var location = Assembly.GetExecutingAssembly().Location;
var assemblyName = new AssemblyName(args.Name);
var path = Path.Combine(Path.GetDirectoryName(location), assemblyName.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
}

Effect of LoaderOptimizationAttribute

I have written a small piece of code regarding the dynamic loading of assemblies and creating class instances from those assemblies, including an executable, a test lib to be dynamically loaded and a loader library to load dynamic assembly into a new Appdomain. Loader library is referenced by both executable and the dynamic library.
//executable
[System.STAThreadAttribute()]
[System.LoaderOptimization(LoaderOptimization.MultiDomain)]
static void Main(string[] args)
{
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomain
};
AppDomain childDomain = AppDomain.CreateDomain("MyDomain", null, domainSetup);
Console.WriteLine(AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
Console.WriteLine(childDomain.SetupInformation.LoaderOptimization.ToString());
byte[] assembly = null;
string assemblyName = "CSTestLib";
using (FileStream fs = new FileStream(assemblyName+".dll",FileMode.Open))
{
byte[] byt = new byte[fs.Length];
fs.Read(byt,0,(int)fs.Length);
assembly = byt;
}
object[] pararmeters = {assemblyName,assembly};
string LoaderAssemblyName = typeof(AssemblyLoader).Assembly.FullName;
string LoaderClassName = typeof(AssemblyLoader).FullName;
AssemblyLoader assloader = (AssemblyLoader)childDomain.CreateInstanceAndUnwrap(LoaderAssemblyName,LoaderClassName , true, BindingFlags.CreateInstance, null, parameters, null, null);
object obj = assloader.Load("CSTestLib.Class1");
object obj2 = assloader.Load("CSTestLib.Class2");
AppDomain.Unload(childDomain);
Console.ReadKey();
}
//Dynamic Lib
using System;
namespace CSTestLib
{
public class Class1 :MarshalByRefObject
{
public Class1() { }
}
public class Class2 : MarshalByRefObject
{
public Class2() { }
}
}
//Loader Library
using System;
namespace LoaderLibrary
{
public class AssemblyLoader : MarshalByRefObject
{
string assemblyName;
public AssemblyLoader(string assName, byte[] ass)
{
assemblyName = assName;
AppDomain.CurrentDomain.Load(ass);
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + " " + AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
}
public object Load(string className)
{
object ret = null;
try
{
ret = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assemblyName, className);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
return ret;
}
}
}
Here I set LoaderOptimizationAttribute on main() method but AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString(); says it is NotSpecified Why?
The differences between MultiDomain and MultiDomainHost is not so clear to me. Is MultiDomainHost for only GAC assemblies? For my situation which is more suitable?
According to this
JIT-compiled code cannot be shared for
assemblies loaded into the load-from
context, using the LoadFrom method of
the Assembly class, or loaded from
images using overloads of the Load
method that specify byte arrays.
So how can I detect if an assembly is loaded domain-neutral or not? How can assure I it is loaded domain-neutral?
This attribute has only an effect if you precompile your assemblies with NGen to speed up a warm start of your application. When you specify MultiDomain or MultiDomainHost you enable the usage of precompiled (ngenned) assemblies. You can verify this with Process Explorer where you can look at the list of loaded modules.
This is one of the biggest startup time savers if your application consists of several executable instances which share assemblies. This enables .NET to share the code pages between processes which in turn saves real memory (one assembly exists only once in the physical memory but it is shared between one or more processes) and prevents JITing the same code over and over again in each process which takes time at the cost that the generated code is a little less efficient as it could be when it would be compiled with the regular JIT which can use more dynamic data to generate the most efficient code.
In your example you load the assembly into a byte array which is located in the managed heap and increases your private byte count. This makes it impossible to share data between processes. Only read only pages which have a counterpart on your hard disc can be shared between processes. This is the reason why the attribute has no effect. If you are after a factor 2 of warm startup performance this is the attribute you were seeking for. For anything else it is not relevant.
Now back to your original question:
It is set but when you start your application under a debugger this MultiDomain attribute is ignored. When you start it outside of a debugger you will get the expected results.
Yes MultiDomainHost does enable AppDomain neutrality only for signed assemblies all others are not shared.
Code sharing can only happen when it is precompiled. The real question is: How to check if the assembly is precompiled? I do it with Process Explorer by looking at the list of loaded modules. When my loaded assembly shows up with a path to the Native Image cache and an .ni extension I am sure the precompiled image is beeing used. You can check this also with fuslogvw when you set the radio button to Native Images to check why a native images was not used by the runtime.

Setting Certificate Friendly Name

Im trying to set the certificate friendly name during the certificate request/acceptance process. I understand that this a property of the microsoft store rather than the certificate and an wondering what .net/c# technique might be used to set it.
Use X509Certificate2.FriendlyName. However, you must export the certificate as PFX/PKCS#12:
X509Certificate2 certificate = new X509Certificate2(...);
certificate.FriendlyName = "MyName";
File.WriteAllBytes(path, certificate.Export(X509ContentType.Pkcs12));
So here is a commmand line example of how to do this. You need CAPICOM from microsoft which wraps the CryptoAPI.
The friendly name is a property of the cert store rather than the certificate so this code imports a certificate to the cert store and sets the friendly name as it does so.
The code takes two parameters the path to the cert file and the friendly name you wish to set.
Code:-
using System;
using System.Collections.Generic;
using System.Text;
using CAPICOM;
using System.Collections;
using System.Runtime.InteropServices;
namespace CertTool
{
class Program
{
const uint CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x20000;
const int CAPICOM_PROPID_FRIENDLY_NAME = 11;
const int CAPICOM_ENCODE_BINARY = 1;
static private String _currStoreName = "My";
static private String _FriendlyName = "Not Set";
static private String _CertPath = "C:\\test.cer";
static StoreClass _oCurrStore;
static ExtendedPropertyClass _friendlyProp;
static CertificateClass _certificate;
static ExtendedProperties _extendedProp;
static void Main(string[] args)
{
try
{
//Friendly name Argument
if (args.Length > 0)
{
_FriendlyName = args[0];
}
//Certpath argument
if (args.Length > 1)
{
_CertPath = args[1];
}
//Set and open the Store
_oCurrStore = new StoreClass();
_oCurrStore.Open(
CAPICOM_STORE_LOCATION.CAPICOM_LOCAL_MACHINE_STORE,
_currStoreName,
CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_EXISTING_ONLY |
CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
//Call the import certificate function
importCert();
}
catch(Exception ex){
Console.WriteLine(ex.Message);
Console.WriteLine(args[0]);
}
}
//Function import the certificate to the machine store and sets the friendly name
static bool importCert()
{
try
{
//Create Certificate Object
_certificate = new CertificateClass();
//Load the certificate into the obejct from file
_certificate.Load(_CertPath, "", CAPICOM_KEY_STORAGE_FLAG.CAPICOM_KEY_STORAGE_EXPORTABLE, CAPICOM_KEY_LOCATION.CAPICOM_LOCAL_MACHINE_KEY);
//Create extended property Class for friendly name
_friendlyProp = new ExtendedPropertyClass();
_friendlyProp.PropID = CAPICOM_PROPID.CAPICOM_PROPID_FRIENDLY_NAME;
_friendlyProp.set_Value(CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BINARY, _FriendlyName);
//Add extendedProp on cert object
_extendedProp = _certificate.ExtendedProperties();
//Set extendded prop to friendly name object
_extendedProp.Add(_friendlyProp);
_oCurrStore.Add(_certificate);
return true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(_CertPath);
return true;
}
}
}
}
Ok, found an answer to that here:
Hi,
Please have a look at this to check if it suits your need:
When you run the .net Code in X64 Environment you will get the following error message.
" Failed --Retrieving the COM class factory for component with CLSID ...."
E.g. in CMS Export / Import server side .net code = "ExportSiteContentIncremental(...) Failed --Retrieving the COM class factory for component with CLSID {CA0752B3-021C-4F99-82E3-2C0F19C5E953} failed due to the following error: 80040154."
WORKAROUND:
The possible workaround is modify your project's platform from 'Any CPU' to 'X86' (in Project's Properties, Build/Platform's Target)
ROOTCAUSE
The VSS Interop is a managed assembly using 32-bit Framework and the dll contains a 32-bit COM object. If you run this COM dll in 64 bit environment, you will get the error message.

Categories

Resources