vb.net running a exe in memory - vb.net

I'm trying to run a app on memory but I'm having error at Invoke. What am I doing wrong?
I'm trying to do this but in vb.net
Code C#
// read the bytes from the application exe file
FileStream fs = new FileStream(filePath, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
// load the bytes into Assembly
Assembly a = Assembly.Load(bin);
// search for the Entry Point
MethodInfo method = a.EntryPoint;
if (method != null)
{
// create an istance of the Startup form Main method
object o = a.CreateInstance(method.Name);
// invoke the application starting point
method.Invoke(o, null);
}
in vb:
Dim instance As FileStream = File.Open("teste.exe", FileMode.Open)
Dim br As New BinaryReader(instance)
Dim bin As Byte() = br.ReadBytes(Convert.ToInt32(instance.Length))
instance.Close()
br.Close()
Dim a As Assembly = Assembly.Load(bin)
Dim metodo As MethodInfo = a.EntryPoint
If (IsDBNull(metodo) = False) Then
'create an istance of the Startup form Main method
Dim o As Object = a.CreateInstance(metodo.Name)
'invoke the application starting point
metodo.Invoke(o, Nothing)
Else
MessageBox.Show("Nao encontrado")
End If
UPDATE:
I found the answer. I created the "test.exe" like a ConsoleAplication, than in the module I code
Imports System.Windows.Forms
Module Module1
Sub Main()
Dim Form1 As New Form1
Form1.Show()
Do Until Form1.Visible = False
Application.DoEvents()
Loop
End Sub
End Module
Then I changed from ConsoleApplication to Windowsform And create my Form1. And this
metodo.Invoke(o, Nothing)
to this:
metodo.Invoke(Nothing, New Object() {})
Thank you guys for the support!

The Main method expects that you pass it the args parameter. Your current call is not passing any parameters, so I'm expecting that you are getting the following error if it ever reaches that line:
System.Reflection.TargetParameterCountException: Parameter count mismatch.
To fix it, just pass a one-element object array as the second parameter of the method.Invoke. Also, because the Main method is a static method, you don't need to do a CreateInstance before invoking the method.
So all you need is this:
metodo.Invoke(Nothing, New Object() {Nothing})
If, for some reason, you actually need pass values to the main's args parameter, you can do it like this:
metodo.Invoke(Nothing, New Object() {New String() {"param1", "param2"}})

Assuming the c# code works you mistranslated the null check.
The equivalent of 'if (method != null)' in vb.net is
If method IsNot Nothing Then
Dim o = a.CreateInstance(method.Name)
method.Invoke(o, Nothing)
End If

Related

Invoke EntryPoint of assembly without showing any window - VB.Net

I need to execute an application embeeded in my resources . Heres my code :
Dim MainAssembly As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly()
Dim resourceStream As Stream = MainAssembly.GetManifestResourceStream("MyApp.Nircmd.exe")
If resourceStream Is Nothing Then
Throw New NullReferenceException("error")
End If
Dim toolAssemblyBuffer(CInt(resourceStream.Length) - 1) As Byte
resourceStream.Read(toolAssemblyBuffer, 0, toolAssemblyBuffer.Length)
resourceStream.Close()
AudioTool = Reflection.Assembly.Load(toolAssemblyBuffer)
Dim args() As String = {Application.StartupPath, "argumentsHere"}
Dim parameters = New Object() {args}
Try
AudioTool.EntryPoint.Invoke(Nothing, parameters)
Catch
End Try
This application opens a new cmd.exe window everytime , so , i need to invoke the EntryPoint without showing any window .
Is there a WindowStyle property or something equal in an assembly ?
Thanks

How to properly render an embedded Font?

I download a True Type Font and I embedded it just as this page explains.
I had to set the UseCompatibleTextRendering property to be able to load it but it looks very weird, I don't know why it looks good in the browser but not in the application.
Just to be clear I added the font to my resources, set it as embedded resource, I used this module:
Imports System.IO
Imports System.Reflection
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Module ExternalFontType
Public Function GetFont(aAssembly As Assembly,
strFontName As String, intFontSize As Integer,
fsFontStyle As FontStyle) As Font
Using pcolFonts As New PrivateFontCollection
Dim bFont() As Byte = ExternalFontType.bRawFontData(aAssembly, strFontName)
Dim ptrMemFont As IntPtr =
Marshal.AllocCoTaskMem(bFont.Length)
Marshal.Copy(bFont, 0, ptrMemFont, bFont.Length)
pcolFonts.AddMemoryFont(ptrMemFont, bFont.Length)
Marshal.FreeCoTaskMem(ptrMemFont)
Return New Font(pcolFonts.Families(0),
intFontSize, fsFontStyle)
End Using
End Function
Private Function bRawFontData(aAssembly As Assembly, strFontName As String) As Byte()
Using stFont As Stream =
aAssembly.GetManifestResourceStream(strFontName)
If (stFont Is Nothing) Then Throw _
New Exception(String.Format("Cannot load _
font '{0}'", strFontName))
Dim bFontBuffer() As Byte = New _
Byte(CInt(stFont.Length - 1)) {}
stFont.Read(bFontBuffer, 0, CInt(stFont.Length))
Return bFontBuffer
End Using
End Function
End Module
and included it in this code
lbl.UseCompatibleTextRendering = True
lbl.Font = ExternalFontType.GetFont(Me.GetType.Assembly, "ProyectName.FontName.ttf", 15, FontStyle.Bold)
More than one problem with that code:
The PrivateFontCollection cannot be declared with a Using statement: this collection must be preserved as long as the Fonts it points to are needed. It's usually declared as a Field in the class (Form) that uses it or in a shared class (or Module, here), then disposed of when not needed anymore.
Marshal.FreeCoTaskMem() cannot be used here; it's a temptation to call it after Marshal.AllocCoTaskMem(), but not in this occasion. This can (will) compromise the Font data allocation. What you need to do is dispose of the PrivateFontcollection object. The Framework will take care of the COM affair (it will do it for you even if you forget to dispose of the PrivateFontcollection object. You should try not to forget, though).
The assembly reference is not required: the Font is added to the Project's Resources as a byte array, which is all that's needed. It can then be retrieved either by name, e.g., My.Resources.SomeFontName, or using the ResourceManager.GetObject() method, casting the returned object to Byte():
Dim fontData As Byte() = My.Resources.SomeFontName
Dim fontData As Byte() = DirectCast(My.Resources.ResourceManager.GetObject("SomeFontName"), Byte())
▶ You have already mentioned this but let's say it again: not all controls can use these Fonts. Only controls that can use Fonts drawn by GDI+ can actually use Fonts from the PrivateFontCollection, the Label and Button controls are among of these, in fact both expose a UseCompatibleTextRendering property. A RichTextBox, for example, cannot.
If the Font is created correctly, you can use Graphics.DrawString() to draw strings content using that Font, even when you cannot set it as the Font of a Control.
Private myFontCollection As PrivateFontCollection = New PrivateFontCollection()
In the Form's Constructor, add Font from the Project's Resources.
Here I'm using a helper class, FontManager, which exposes a public shared method AddFontsFromResource(): pass to this method the PrivateFontCollection and a list of resources names corresponding to the Font names.
This method fills the collection with Fonts that can be installed successfully and returns the number of Fonts installed.
Of course you use whatever other method you prefer to reference your Fonts.
Note. In the example, three Font resources are added to the collection:
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"}
but two belong to the same FontFamily, so the PrivateFontCollection will contain just two elements, not three.
Public Sub New()
Dim installedFontsCount = FontManager.AddFontsFromResources(myFontCollection,
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"})
' The Font can set here or anywhere else
someLabel.UseCompatibleTextRendering = True
someLabel.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Regular)
someButton.UseCompatibleTextRendering = True
someButton.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Italic)
End Sub
It's important to dispose of the PrivateFontCollection when it's not needed anymore: when the Form that initialized it closes or before the Application closes:
You could also use a shared object to reference a PrivateFontCollection that can be used anywhere in the Project. In this case the collection needs to be disposed of when the Application closes.
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
myFontCollection.Dispose()
End Sub
Helper class:
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Public Class FontManager
Public Shared Function AddFontsFromResources(fontCollection As PrivateFontCollection, fontNames As String()) As Integer
If fontNames.Length = 0 Then Return Nothing
Dim installedFontsCount = 0
For Each fontName As String In fontNames
Try
Dim fontData As Byte() = CType(My.Resources.ResourceManager.GetObject(fontName), Byte())
If fontData Is Nothing Then Throw New InvalidOperationException()
Dim data As IntPtr = Marshal.AllocCoTaskMem(fontData.Length)
Marshal.Copy(fontData, 0, data, fontData.Length)
fontCollection.AddMemoryFont(data, fontData.Length)
installedFontsCount += 1
Catch ex As Exception
' Placeholder: Notify User/Log/Whatever
Debug.Print($"Font installation failed for {fontName}")
End Try
Next
Return installedFontsCount
End Function
End Class
C# version:
using System.Drawing.Text;
using System.Runtime.InteropServices;
public static int AddFontsFromResources(PrivateFontCollection fontCollection, string[] fontNames)
{
int installedFontsCount = 0;
if (fontNames.Length == 0) return 0;
foreach (string fontName in fontNames) {
try {
byte[] fontData = (byte[])Properties.Resources.ResourceManager.GetObject(fontName);
var data = Marshal.AllocCoTaskMem(fontData.Length);
Marshal.Copy(fontData, 0, data, fontData.Length);
fontCollection.AddMemoryFont(data, fontData.Length);
installedFontsCount += 1;
}
catch (Exception) {
// Placeholder: Notify User/Log/Whatever
Console.WriteLine($"Font installation failed for {fontName}");
}
}
return installedFontsCount;
}

Calling System.IO.ReadAllBytes by string name

This post is related to Visual Basic .NET 2010
So, I'm wondering if there's any way to call a function from a library such as System.ReadAllBytes by string name.
I've been trying Assembly.GetExecutingAssembly().CreateInstance and System.Activator.CreateInstance followed by CallByName(), but none of them seemed to work.
Example of how I tried it:
Dim Inst As Object = Activator.CreateInstance("System.IO", False, New Object() {})
Dim Obj As Byte() = DirectCast(CallByName(Inst, "ReadAllBytes", CallType.Method, new object() {"C:\file.exe"}), Byte())
Help is (as always) much appreciated
It is System.IO.File.ReadAllBytes(), you missed the "File" part. Which is a Shared method, the CallByName statement is not flexible enough to permit calling such methods. You will need to use the more universal Reflection that's available in .NET. Which looks like this for your specific example, spelled out for clarity:
Imports System.Reflection
Module Module1
Sub Main()
Dim type = GetType(System.IO.File)
Dim method = type.GetMethod("ReadAllBytes")
Dim result = method.Invoke(Nothing, New Object() {"c:\temp\test.bin"})
Dim bytes = DirectCast(result, Byte())
End Sub
End Module

Object reference not set to an instance of an object? VB.NET

I have this code here:
Dim MasterIndex As String()()
Private Function Lookup(ByVal Search_path As String) As Integer
Dim i As Integer = 0
Do Until MasterIndex(i)(0) Is Nothing
If Search_path = MasterIndex(i)(0) Then
Return MasterIndex(i)(1)
End If
Loop
Return -1
End Function
Which gives me the error Object reference not set to an instance of an object occuring on the Do Until line. Why is this? How can I fix this?
The MasterIndex variable is never assigned that is why you have the exception
You should instantiate MasterIndex first by calling the New() constructor:
Dim MasterIndex As new String()()
and fill it with data before calling the Lookup function.
Something like:
Private MasterIndex As String()() = New String()() {New String() {"A1", "A2"}, New String() {"B1", "B2"}}
Either MasterIndex is not initialized or MasterIndex(0) is not initialized.
Can you show the code that initializes that variable, assuming you do that somewhere else in the program?
What happens if you put a breakpoint on that line and examine MasterIndex?

.Net Dynamically Load DLL

I am trying to write some code that will allow me to dynamically load DLLs into my application, depending on an application setting. The idea is that the database to be accessed is set in the application settings and then this loads the appropriate DLL and assigns it to an instance of an interface for my application to access.
This is my code at the moment:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim obj As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj, ICRDataLayer)
MsgBox(SQLDataSource.ModuleName & vbNewLine & SQLDataSource.ModuleDescription)
I have my interface (ICRDataLayer) and the SQLServer.dll contains an implementation of this interface. I just want to load the assembly and assign it to the SQLDataSource object.
The above code just doesn't work. There are no exceptions thrown, even the Msgbox doesn't appear.
I would've expected at least the messagebox appearing with nothing in it, but even this doesn't happen!
Is there a way to determine if the loaded assembly implements a specific interface. I tried the below but this also doesn't seem to do anything!
For Each loadedType As Type In ass.GetTypes
If GetType(ICRDataLayer).IsAssignableFrom(loadedType) Then
Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj1, ICRDataLayer)
End If
Next
EDIT: New code from Vlad's examples:
Module CRDataLayerFactory
Sub New()
End Sub
' class name is a contract,
' should be the same for all plugins
Private Function Create() As ICRDataLayer
Return New SQLServer()
End Function
End Module
Above is Module in each DLL, converted from Vlad's C# example.
Below is my code to bring in the DLL:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t As Type = factory.GetType
Dim method As MethodInfo = t.GetMethod("Create")
Dim obj As Object = method.Invoke(factory, Nothing)
SQLDataSource = DirectCast(obj, ICRDataLayer)
EDIT: Implementation based on Paul Kohler's code
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
For Each ty As System.Type In s
If ty.Name.Contains("ICRDataLayer") Then
MsgBox(ty.Name)
plugin = DirectCast(Activator.CreateInstance(assemblyType), ICRDataLayer)
MessageBox.Show(plugin.ModuleName)
End If
Next
I get the following error with this code:
Unable to cast object of type 'SQLServer.CRDataSource.SQLServer' to type 'DynamicAssemblyLoading.ICRDataLayer'.
The actual DLL is in a different project called SQLServer in the same solution as my implementation code. CRDataSource is a namespace and SQLServer is the actual class name of the DLL.
The SQLServer class implements ICRDataLayer, so I don't understand why it wouldn't be able to cast it.
Is the naming significant here, I wouldn't have thought it would be.
Final Working code
Contents of PluginUtility:
enter code here Public Shared Function GetInstances1(Of Type)(ByVal baseDir As String, ByVal searchPattern As String) As System.Type()
Dim tmpInstances As New List(Of Type)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
Return s.ToArray()
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
End Function
Code to load the DLL:
enter code here
Dim basedir As String = "M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\"
Dim searchPattern As String = "*SQL*.dll"
Dim plugin As CRDataLayer.ICRDataLayer
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetExportedTypes
If assemblyType.GetInterface("CRDataLayer.ICRDataLayer") IsNot Nothing Then
plugin = DirectCast(Activator.CreateInstance(assemblyType), CRDataLayer.ICRDataLayer)
MessageBox.Show(plugin.ModuleDescription)
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
Catch ex As Exception
MsgBox(ex.Message)
Clipboard.SetText(ex.Message)
End Try
Version 2 - This sample loads up a DLL from it current directory.
There are 2 projects, 1 console application project and a "module" project (the module 'coppies' its DLL to the working directory of the console app).
The sample below simply demonstrates dynamically loading a DLL that implements an interface. The IModule interface just reports its name. PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll") will create an instance of any IModule instance found in a DLL within the current directory ending with ".Module.dll". It's a reflected VB.NET version straight out of Mini SQL Query.
With that in mind something like:
Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Should satisfy your requirement. Then you just need to chose which one to execute!
The code:
In "VB.LoaderDemo Colsole App"
' IModule.vb
Public Interface IModule
Property ModuleName() As String
End Interface
' PlugInUtility.vb
Imports System.IO
Imports System.Reflection
Public Class PlugInUtility
Public Shared Function GetInstances(Of T)(ByVal baseDir As String, ByVal searchPattern As String) As T()
Dim tmpInstances As New List(Of T)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes()
If (Not assemblyType.GetInterface(GetType(T).FullName) Is Nothing) Then
tmpInstances.Add(DirectCast(Activator.CreateInstance(assemblyType), T))
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
Return tmpInstances.ToArray()
End Function
End Class
' MainModule.vb
Module MainModule
Sub Main()
Dim plugins As IModule() = PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")
Dim m As IModule
For Each m In plugins
Console.WriteLine(m.ModuleName)
Next
End Sub
End Module
In "Sample1 DLL" (references 'VB.LoaderDemo' for IModule)
Imports VB.LoaderDemo
Public Class MyModule1
Implements IModule
Dim _name As String
Public Sub New()
_name = "Sample 1, Module 1"
End Sub
Public Property ModuleName() As String Implements IModule.ModuleName
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
The output is:
> Sample 1, Module 1
Is the type ICRDataLayer defined in the DLL you are going to load? If so, you seem to already reference the DLL in your project settings.
You need to work with just reflection:
Dim obj As Object = ass.CreateInstance("ICRDataLayer", True)
Dim t as Type = obj.GetType()
Dim method as MethodInfo = t.GetMethod("DoSomething")
method.Invoke(obj, ...)
Edit: If ICRDataLayer is implemented in the application, and the plugin just implements the interface, you need the plugin to provide a factory for you: (sorry for C# code, I am not familiar with VB.NET's syntax)
// in each of plugins:
static class CRDataLayerFactory // class name is a contract,
{ // should be the same for all plugins
static ICRDataLayer Create()
{
return new CRDataLayerImplementation();
}
}
The application's code should look like this:
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t as Type = factory.GetType()
Dim method as MethodInfo = t.GetMethod("Create")
Dim obj as Object = method.Invoke(factory, null)
SQLDataSource = DirectCast(obj, ICRDataLayer)
A few things to look for in your code
Debug through and check that the
assembly is loaded correctly, in case
it fails due to dependency checking
Instead of using GetType, use GetExportedType so you have smaller subset to iterate through
The CreateInstance should use your loadedType rather than the interface (you cant create object from an interface)
Personally, I dont like naming my variable ass, I would shorten it to assem instead :)