After reading Larry Osterman's response on the very same issue I am trying to solve at the moment, I thought I had found the answer to my question.
For the record, the question was : how can I from .Net (non-WinRT) list the types in a WinRT assembly ( mine are .dll files apparently, not .Winmd)
I therefore used the following code snippet :
//note, this wrapper function returns the metadata file name and token
// it immediately releases the importer pointer
static Tuple<string, UInt32> ResolveTypeName(string typename)
{
string path;
object importer = null;
UInt32 token;
try
{
var hr = RoGetMetaDataFile(typename, IntPtr.Zero, out path, out importer, out token);
//TODO: check HR for error
return Tuple.Create(path, token);
}
finally
{
Marshal.ReleaseComObject(importer);
}
}
[DllImport("WinTypes.dll")]
static extern UInt32 RoGetMetaDataFile(
[MarshalAs(UnmanagedType.HString)] string name,
IntPtr metaDataDispenser,
[MarshalAs(UnmanagedType.HString)] out string metaDataFilePath,
[MarshalAs(UnmanagedType.Interface)] out object metaDataImport,
out UInt32 typeDefToken);
( found on https://gist.github.com/2920743)
Unfortunately, I get a non-zero HResult.
I referred to the documentation and found this :
HR_RESULT_FROM_WIN32(ERROR_NO_PACKAGE) The function was called from a
process that is not in a Windows Store app.
Does that mean it is not possible to list the types from .Net (non-WinRT) at all ?
RoGetMetaDataFile is used to load a metadata file from within an app package. It locates the metadata file in which the named type is defined, loads that metadata file, and returns an IMetaDataImport interface pointer that represents that metadata file.
From ordinary .NET code you can call RuntimeEnvironment.GetRuntimeInterfaceAsIntPtr (or GetRuntimeInterfaceAsObject) to get the current runtime's IMetaDataDispenser interface pointer, which can be used to load arbitrary modules for inspection.
From native code, you can call ICLRMetaHost::GetRuntime to load a runtime, then from that object call ICLRRuntimeInfo::GetInterface to get its IMetaDataDispenser interface pointer.
RoGetMetaDataFile can be used from outside the app package, however it will only resolve system windows runtime types.
In order to resolve app specific types, you need to be running with "package identity" - in other words, in the context of a running application.
Related
I need to retrieve package information in my C# application running on Linux.
I've tried to use Python.NET which wrap python-apt which wrap libapt-pkg.so, and it works but I would prefer to go more direct by pinvoke libapt-pkg.
But I need help on my DLLImport statement.
When using python-apt I first initialize the apt module, then instantiat the Cache() class and then called the Cache.keys() function which retrieve all package-names as a string array.
If I can get this to work using pinvoke I believe I have a good chance getting the rest working too (retrieve the more advanced stuff)
Just to show some code which doesn't work, but just to get started:
[DllImport("libapt-pkg")]
public static extern dynamic GetPkgCache();
dynamic AptCache = GetPkgCache();
I get the error:
System.Runtime.InteropServices.MarshalDirectiveException: 'Cannot
marshal 'return value': Invalid managed/unmanaged type combination
(Marshaling to and from COM VARIANTs isn't supported).'
I've been looking in the header file cachefile.h of the apt project. Maybe these lines are useable for my DllImport statement?
..
inline operator pkgCache &() const {return *Cache;};
inline operator pkgCache *() const {return Cache;};
..
inline pkgCache* GetPkgCache() { BuildCaches(NULL, false); return Cache; };
..
But I'm new to C/C++ so it doesn't make much sense for me at present.
UPDATE:
Thanks for the feedback.
[DllImport("libapt-pkg")]
public static extern IntPtr GetPkgCache();
Now I get the error
System.EntryPointNotFoundException: 'Unable to find an entry point
named 'GetPkgCache' in shared library 'libapt-pkg'.'
I try to read the information of Microsoft Access (Office) CommandBars contained in an *.mdb. I could use Microsoft.Office.Interop.Access for this; however, these PIA assemblies are tied to specific Office versions. Therefore, to be version independent, I do it the late-bound way through C#'s dynamic type. I.e., I have no references to Microsoft Office specific assemblies. The price for this is that the access to the command bars is now weakly typed.
This approach works well, except when I try to access the custom button images. This is a condensed version of my real code to illustrate the problem:
dynamic access = Activator.CreateInstance(Type.GetTypeFromProgID("Access.Application", true));
// Starts an Access XP (2002) process in my case, but could be any version.
foreach (dynamic commandBar in access.CommandBars) {
if (!commandBar.BuiltIn) { // Only my menus and toolbars.
foreach (dynamic control in commandBar.Controls) {
if (control.Type == (int)MsoControlType.msoControlButton && !control.BuiltInFace) {
string caption = control.Caption; // Works.
stdole.IPictureDisp picture = control.Picture; // <==== Throws exception! ====
// ...
}
}
}
}
Calling the getter of the CommandBarButton.Picture property throws this exception:
Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at MyApplication.MyMethod in MyApplication.cs:line 64
How can I get the picture avoiding this exception?
Note, this question is not about converting the IPictureDisp object to a System.Drawing.Image object. I already have a solution for this. Also, it makes no difference whether picture is typed as object, dynamic, or IPictureDisp. The property does exist, otherwise I would get the exception 'System.__ComObject' does not contain a definition for 'Picture'.
It is a .NET Framework 4.0 Windows Forms project compiled for x86 (32-bit).
Edit: Switching to Primary Interop Assemblies (PIA) allows strong typing, but does not solve the problem. The exception persists.
Meanwhile I have read more about PIAs. Since .NET Framework 4.0 you can tweak the properties of the assembly reference to make them version independent. Right click on References / Microsoft.Office.Interop.Access and
Set Embed Interop Types to True.
Set Specific Version to False.
Do this for References / office as well. Therefore I will be switching to PIAs instead of using dynamic.
so I've been told I might not have access to the registry or programs with which usually load their IFilters onto the system, so I have to include the IFilter dlls in the application and load them directly from there. I'm currently using CodeProject's C# IFilter classes, but their are still a few things that are over my head when it comes to the filterPersistClass, persistentHandlerClass and COM and as such I am a bit lost on how I could get this to work.
I've done all the mundane stuff like, get the dlls, setup a resource file with "Extension, DLL Path" and that, but just can't seem to get a grasp on how to now load the IFilter DLL. It's maybe that I should just start from scratch, but thought I would ask for some help first.
EDIT (Partial Solution)
Well I figured out how to load query.dll using the code below in the FilterReader constructor in FilterReader.cs, though I'm having problems now loading the PDFFilter.dll file and am getting the following error:
Unable to find an entry point named 'LoadIFilter' in DLL 'C:\Program Files\Adobe\Adobe PDF iFilter 9 for 64-bit platforms\bin\PDFFilter.dll'
The problem I think I am now stuck at is that PDFFilter.dll uses STA and C# applications are MTA.
[DllImport("query.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int LoadIFilter(string pwcsPath, [MarshalAs(UnmanagedType.IUnknown)] ref object pUnkOuter, ref IFilter ppIUnk);
// --------------------------- constructor ----------------------------------
var isFilter = false;
object iUnknown = null;
LoadIFilter(fileName, ref iUnknown, ref _filter);
var persistFile = (_filter as IPersistFile);
if (persistFile != null)
{
persistFile.Load(fileName, 0);
IFILTER_FLAGS flags;
IFILTER_INIT iflags =
IFILTER_INIT.CANON_HYPHENS |
IFILTER_INIT.CANON_PARAGRAPHS |
IFILTER_INIT.CANON_SPACES |
IFILTER_INIT.APPLY_INDEX_ATTRIBUTES |
IFILTER_INIT.HARD_LINE_BREAKS |
IFILTER_INIT.FILTER_OWNED_VALUE_OK;
if (_filter.Init(iflags, 0, IntPtr.Zero, out flags) == IFilterReturnCode.S_OK)
isFilter = true;
}
if (_filter != null && isFilter) return;
if (_filter != null) Marshal.ReleaseComObject(_filter);
There is nothing magical about IFilter objects. They are housed in standard COM dlls. In the end, all you need the clsid of the class which knows how to process pdf files.
The LoadIFilter function in query.dll is just a convenient helper function. Everything it does you can do yourself.
There is a standard way, in the registry, in which a file extension (e.g. .pdf) is resolved to a clsid (e.g. {E8978DA6-047F-4E3D-9C78-CDBE46041603})
Note: You could also just skip to the end, and know that the clsid of Adobe's IFilter implementation is {E8978DA6-047F-4E3D-9C78-CDBE46041603}. But that's not guaranteed, so you need to crawl the registry.
The algorithm to resolve an .ext to the clsid of an object that implements IFilter is:
GetIFilterClassIDForFileExtension(String extension)
arguments:
extension (String) e.g. ".pdf"
returns:
clsid (Guid) e.g.
//Get the Persistent Handler for this extension
//e.g.
// HKLM\Software\Classes\.pdf\PersistentHandler\(Default)
//returns
// "{F6594A6D-D57F-4EFD-B2C3-DCD9779E382E}"
persistentHandlerGuid = HKLM\Software\Classes\.pdf\PersistentHandler\(Default)
//Get the clsid associated with this persistent handler
//e.g.
// HKLM\Software\Classes\CLSID\{F6594A6D-D57F-4EFD-B2C3-DCD9779E382E}\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}
//where the final guid is the interface identifier (IID) of IFilter
clsid = HKLM\persistentHandlerGuid\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}
//e.g. returns "{E8978DA6-047F-4E3D-9C78-CDBE46041603}", the clsid of Adobe's PDF IFilter
return clsid
Once you have the clsid of the appropriate object, you create it with:
Guid clsid = GetIFilterClassForFileExtension(".pdf")
IFilter filter = CreateComObject(clsid);
You now have the entire guts of the LoadIFilter function from query.dll:
IFilter LoadIFilter(String filename)
{
String extension = ExtractFileExt(filename); //e.g. "foo.pdf" --> ".pdf"
Guid clsid = GetIFilterClassForFileExtension(extension);
return CreateComObject(clsid) as IFilter;
}
Now, all that still requires the registry, because you still have to be able to resolve an extension into a clsid. If you already know the classid, then you don't need the registry:
IFilter adobeIFilterForPdfs = CreateComObject("{E8978DA6-047F-4E3D-9C78-CDBE46041603}")
And you're good to go.
The important point is that the function you're trying to call, LoadIFilter is not inside Adobe's dll (or any other IFilter dll provided by any other company, to crawl any other file types). The LoadIFilter function is exported by query.dll, and is simply a helper function for the above steps i described.
All IFilter dlls are COM dlls. The documented way to load a COM dll is through the CoCreateInstance function:
IUnknown CreateComObject(Guid ClassID)
{
IUnknown unk;
HRESULT hr = CoCreateInstance(ClassID, null, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IUnknown, ref unk);
if (Failed(hr))
throw new Exception("Could not create instance: "+hr);
return unk;
}
I'll leave it to you to find the correct way to create a COM object from C# managed code. I've forgotten.
Note: Any code released into public domain. No attribution required.
I am trying to use the IOfficeAntiVirus COM interface to invoke a scan using the Microsoft security essentials virus scanner.
I am doing early binding because the documentation says that IOfficeAntiVirus interface inherits from IUknown and does not support IDispatch.
[Guid("56FFCC30-D398-11d0-B2AE-00A0C908FA49"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IOfficeAntiVirus
{
void Scan(IntPtr info);
}
[ComImport, Guid("2781761E-28E1-4109-99FE-B9D127C57AFE")]
class SecurityEssentialsAntiVirus
{
}
The parameter for the scan method is a type that comes from this example. That example does the opposite of what I want because it implements the IOfficeAntiVirus interface using a .NET class rather than invoking the scan method. The marshalling and types in the example seem to match the documentation as far as I can tell.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MSOAVINFO
{
public int cbsize;
[MarshalAs(UnmanagedType.U4)]
public uint uFlags;
public IntPtr hwnd;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwzFullPath;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwzHostname;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwzOrigURL;
}
This is the code I'm trying to use to invoke the scan method:
var antivirus = (IOfficeAntiVirus)new SecurityEssentialsAntiVirus();
var file = new MSOAVINFO();
file.pwzFullPath = #"test.txt";
IntPtr lParam = Marshal.AllocHGlobal(Marshal.SizeOf(file));
Marshal.StructureToPtr(file, lParam, false);
antivirus.Scan(lParam);
It fails on the call to the scan method. I get an exception that says:
"Attempted to read or write protected memory. This is often an
indication that other memory is corrupt."
I'm running this from a 32 bit command line app on a 64 bit system. I have experimented with running the command line program in both 32 bit and 64 bit with no success.
I am sure that the IOfficeAntiVirus guid is correct because I got it from the documentation. Also trying a random GUID that isn't a COM interface causes an error when I try to cast the object to the interface.
I am sure that the SecurityEssentialsAntiVirus guid implements the IOfficeAntiVirus because when I tried casting a different type of COM object to that interface I got an error.
I think the problem might be that the Scan method on the interface hasn't been correctly declared (declaring a random method on the interface gives the same error). I am not working from documentation for the security essentials assembly so it might not implement the interface in the way I imagine (or at all). Does anyone have any idea how to check that?
I can see a MpOAv.dll file in its directory and that is the same name as the header file in the IOfficeAntiVirus documentation, I know that's not a lot to go on. I can't open that dll up in ole-com object viewer to see what is inside. I get a message saying:
IMoniker::BindToObject failed on the file moniker created from (
"C:\Program Files\Microsoft Security Client\MpOAv.dll" ). Bad
extension for file
MK_E_INVALIDEXTENSION ($800401E6)
It also could be that I'm not passing the struct correctly to the Scan method. I have tried about a hundred variations without any luck including using AllocCoTaskMem() and passing the struct by reference.
I'm really not had much experience doing interop (learnt a heap today trying to figure this out!) so I'd really appreciate a push in the right direction. :)
I'm was also spent a couple of days on the same problem but without any results. The maximum what I was able to do it run check without any errors but even for malware it returns OK status, in my case
So, I continued research and found that Mozilla uses IAttachmentExecute antivirus API for checks. And Save method, of it, doing same job like
Scan of IOfficeAntiVirus
IAttachmentExecute::Save may run virus scanners or other trust
services to validate the file before saving it. Note that these
services can delete or alter the file.
and implementation of it on codeples http://antivirusscanner.codeplex.com/
One thing I notice is that you didn't set cbsize. file.cbsize=Marshal.SizeOf(file);
So I'm trying to do manually what AxImp does (I'm doing it dynamically).
My product is a wide released, sanctioned "add-on" to a third party product. They have an OCX, which I add to my form with a COM reference...however, if the client has (or installs) an updated version of their product my product can no longer load the OCX.
Therefore I'm trying to load their OCX dynamically. I've got everything working except that I need the GUID of one of the interfaces in one of their OCXs. I know what the type name is, and the OCX >is< registered on the system. How can I get an object's GUID just from the type name?
Note, Assembly.LoadFrom() doesn't work because the OCX isn't .NET it's COM.
Since your comment let us know that the GUID is found in the OCX but not registered under HKEY_CLASSES_ROOT, we'll have to read it from the type library:
Call LoadTypeLib or LoadTypeLibEx, passing the path to the .OCX file
Then use the FindName method of the returned object.
Then GetTypeAttr followed by PtrToStructure to get a TYPEATTR structure with the GUID.
Not sure why you can't simply add a COM reference to the DLL to your project.
Visual Studio will automatically add a .NET wrapper to any COM object that is referenced this way.
I had to write something like that to migrate code from COM to C#
class GetGuid
{
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern int LoadTypeLib(string fileName, out ITypeLib typeLib);
public string SearchRegistry(string dllPath /* or ocx */)
{
var result = string.Empty;
ITypeLib tl;
if (LoadTypeLib(dllPath, out tl) == 0)
{
ITypeInfo tf;
tl.GetTypeInfo(0, out tf);
var ip = IntPtr.Zero;
tl.GetLibAttr(out ip);
if (ip != IntPtr.Zero)
{
var ta = (System.Runtime.InteropServices.ComTypes.TYPELIBATTR)Marshal.PtrToStructure(ip, typeof(System.Runtime.InteropServices.ComTypes.TYPELIBATTR));
result = ta.guid.ToString();
}
}
return result;
}
}