Compilation error: object library invalid or contains references to object definitions that could not be found - vba

I want to import some data from an Oracle Database, but I don't want to leave the Password on the macro, as it is very easy to access. Then I found this code to hide myconnection string, which works pretty well, at least on my PC. I created a .dll of the next code so that the database data is not available for users.
using ADODB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace HiddenConnectionString
{
[InterfaceType(ComInterfaceType.InterfaceIsDual),
Guid("2FCEF713-CD2E-4ACB-A9CE-E57E7F51E72E")]
public interface IMyServer
{
Connection GetConnection();
void Shutdown();
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("57BBEC44-C6E6-4E14-989A-B6DB7CF6FBEB")]
public class MyServer : IMyServer
{
private Connection cn;
private string cnStr = "Provider=MSDAORA.1;Password=YourPass;User ID=YourID;Data Source=IP:Port";
public MyServer)(
{
}
public Connection GetConnection()
{
cn = new Connection();
cn.ConnectionString = cnStr;
cn.Open();
return cn;
}
public void Shutdown()
{
cn.Close();
}
}
}
The next code is the one that I am trying to run:
Option Explicit
Sub Main()
Dim myCn As MyServer
Set myCn = New MyServer
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
'Consumos
rs.Open "select * from tablename, myCn.GetConnection
Sheets.Add After:=ActiveSheet
Range("A2").CopyFromRecordset rs
rs.Close
myCn.Shutdown
Set rs = Nothing
Set myCn = Nothing
End Sub
I've been trying to make it work on other computers but when I run the testing code on them i get this error:
Compilation error: object library invalid or contains references to object definitions that could not be found
So it seems like the library hasn't been imported, but when I check on the reference list, my "hiddenconnectionString.dll" is activated, so I don't know what it going on.
Can someone give me some advice on how to make it work? Any help is welcome. Thanks :D

Sounds like the dll is only installed on your machine. Items in the reference list are not imported and/or stored in the VBA. They need to be present on each host machine.
I'm not sure that hidden connection strings are a good idea. Security by obscurity is generally discouraged, as often it can be bypassed by anyone willing to invest enough time.
This design also uses a shared UID and PWD. With each user sharing an account you lose the ability to audit who did what.
Where possible, I'd always recommend providing each user with their own account.

Related

Programmatically detect if Mouse and Keyboard are plugged in

I was wanting to know what is the best way to detect if a mouse or keyboard is plugged in to the computer? I have an application that is mostly used in a touchscreen standalone but I want to know if there is a keyboard plugged in don't fire off pulling up the windows keyboard or a form that contains a numeric keypad. Then change some behavior if the mouse is plugged in against if it isn't.
Using VB.Net, I would use System.Management (don't forget to add this reference in the Project [Menu] -> Add Reference) ManagementObjectSearcher combined with System.Linq to find the solution like this,
Imports System
Imports System.Management
Imports System.Linq
Public Module Module1
Public Sub Main()
Console.WriteLine(HasDevice("PointingDevice"))
Console.WriteLine(HasDevice("Keyboard"))
Console.ReadKey()
End Sub
Public Function HasDevice(strtype As String)
Dim searcher As New ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_" + strtype)
Dim result = From mobj In searcher.Get()
Select mobj Where mobj("Status").ToString() = 0
Return Not IsNothing(result)
End Function
End Module
Result
You could also hard-coded it to avoid input mistake like TRiNE suggested (though using C#)
Public Function HasPointingDevice()
Dim searcher As New ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_PointingDevice")
Dim result = From mobj In searcher.Get()
Select mobj Where mobj("Status").ToString() = 0
Return Not IsNothing(result)
End Function
Public Function HasKeyboard()
Dim searcher As New ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_Keyboard")
Dim result = From mobj In searcher.Get()
Select mobj Where mobj("Status").ToString() = 0
Return Not IsNothing(result)
End Function
And call them like this
Public Sub Main()
Console.WriteLine(HasPointingDevice())
Console.WriteLine(HasKeyboard())
Console.ReadKey()
End Sub
They will produce the same result
You can use C# System.Management and ManagementObjectSearcher
Here is a sample code. Modify as it matches your need.
public bool isPointingDeviceAttached()
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PointingDevice");
int devCount = 0;
foreach (ManagementObject obj in searcher.Get()) {
if(obj["Status"].ToString().Contains("OK")) // if device is ready
devCount++;
}
return devCount>0;
}
public bool isKeyboardAttached()
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Keyboard");
int devCount = 0;
foreach (ManagementObject obj in searcher.Get()) {
if (obj["Status"].ToString().Contains("OK")) // if device is ready
devCount++;
}
return devCount>0;
}
Make sure to add using System.Management; line and to add System.Management in references.
You can use more ManagementObject attributes as match your need. Here is a list of attributes and some other external references.
Pointing device class ref. & attribute list
C#, C++ similar example
I spent a couple of days tackling this and found nothing that says a keyboard is detached. Only what is attached currently. That gave me an idea though! With my keyboard attached I checked and three keyboard devices were listed. I removed the keyboard and one disappeared. Now I just look for obj.tostring.contains("USB") and if it doesn't I know there is no keyboard attached to my tablet.
The other two were HID and ACPI which I guess are the touchscreen and mousepad.
Of course other tablets may be different so it isn't a robust
Edit: TO be clear, using the previous answer's code foreach (ManagementObject obj in searcher.Get()) { if (obj["Status"].ToString().Contains("OK")) // if device is ready devCount++;
I use obj.tostring.contains("USB") instead of (obj["Status"]xxxx If the tablet is plugged in I will get three keyboards listed and all the "Status" variables report OK. If I unplug the keyboard there are only two entries both of which have a status of OK while the USB keyboard status simply isn't there. So I just look for the USB string in the object and if it isn't there then I know it isn't plugged in.

REST API (JSON) that updates SQL Table using Windows Console Application and Scheduled Tasks

I am a newbie at JSON programming. Most of my experience is in C# and some in XML and Javascript. So I am a bit lost. I will attempt to be as specific as possible.
I have written a windows console application that runs via the task scheduler. Basically the windows application is supposed to take the API from a site that is managed by an outside company but the information is owned by my company and put the information within a SQL table. The API is pretty standard and written in JSON.
I am successful in parsing the JSON language and (for example) displaying it in a command prompt but I need to be able to parse the language and place it into an SQL table. I have read up on SQL injection attacks and I feel fairly confident that we have covered our bases here. So the problem lies in the fact that it does not update the table when the application is run via the scheduler or without the scheduler.
I have included a little bit of the JSON language below along with the language for my console application.
{"date":"2015-09-24","data":[{"cid":"17","rank":1},{"cid":"26","rank":1},{"cid":"80","rank":1},{"cid":"30","rank":1},{"cid":"90","rank":1},{"cid":"62","rank":1},{"cid":"147","rank":1},{"cid":"28","rank":1}"s":1,"e":null}
using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonApiClient
{
class Program
{
static void Main(string[] args)
{
ExecuteRiskSearch();
Console.ReadLine();
}
static void ExecuteRiskSearch()
{
string url = "https://localhost/api/getWatchList/";
string json = new WebClient().DownloadString(url);
JObject results = JObject.Parse(json);
foreach (var result in results)
{
string cid = (string)results["CID"];
JToken rank = results["rank"];
string risk = "";
if (rank is JValue)
{
risk = (string)rank;
}
else if (rank is JArray)
{
risk = (string)((JArray)rank).First;
}
else
{
SqlConnection connection = null;
SqlCommand command = null;
try
{
connection = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=apiData;Data Source=serverName;");
command = new SqlCommand("UPDATE apiData.dbo.API SET [Category] WHERE CID=CID", connection);
connection.Open();
int numrows = command.ExecuteNonQuery();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
command.Dispose();
connection.Dispose();
}
}
}
}
}
}
What am I missing to make the JSON data update my SQL table? I have scoured Google search results and I haven't found much information. Any help would be so greatly appreciated.
For the need to foreach with the correct part of the JSON object, what I mean is very simply that your variable results includes the entire JSON object, from the "date" through the "e". You need to start with the "data" object and iterate through its array or your string cid will error out on assignment, as it will be attempting to assign an array to a single value. The same goes for your JToken rank. I believe it should be this:
foreach(datum in results["data"])
{
string cid = datum["cid"];
JToken rank = datum["rank"];
/* ... */
}
In addition, your set command isn't doing anything. You need to use SET columName = " + newValue + " WHERE CID == " + cid to actually affect a change, where columnName is the column you wish to alter and newValue is your C# variable carrying the desired replacement.
It's also a best practice to include a change to an updated date field when updating via an automated process, if there is one present. Generally the convention is to have a created date and an updated date for each row in a table.
I hope this at least points you in the right direction.
-C§
As an alternative, you can send entire text to Sql Server and load it there.
Sql Server 2016 will enable you to store JSON using single command - OPENJSON. In the older versions you can use existing CLR/JSON libraries such as Json4Sql or JsonSelect.

Exception occurs reading spreadsheet after saving ExcelPackage (EPPlus)

I am manipulating data in a .xlsx spreadsheet using EPPlus (4.0.4) and saving the file, immediately afterwards I am loading parts of the data into datatables.
After I call the .Save() method the spreadsheet is saved but then I get a 'External table is not in the expected format.' exception when trying to fill a DataAdapter.
I've created a console app to minimise the amount of code required to demonstrate the issue, here is the code:
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EPPlusSaveIssue
{
class Program
{
static void Main(string[] args)
{
string pathToFile = "C:\\Users\\Peter\\Documents\\test.xlsx";
FileInfo newFile = new FileInfo(pathToFile);
using (var pck = new ExcelPackage(newFile))
{
pck.Save();
}
string connectionStringFormat = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=No;IMEX=1\"";
using (OleDbConnection con = new OleDbConnection(string.Format(connectionStringFormat, pathToFile)))
{
using (OleDbDataAdapter dataAdapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$] ", con))
{
DataTable dataTable = new DataTable();
dataAdapter.Fill(dataTable);
Console.WriteLine(dataTable.Rows.Count.ToString());
}
}
}
}
}
Steps to reproduce:
Run the application against a newly created spreadsheet, the exception occurs
Comment out the pck.Save(); line and the exception still occurs (the spreadsheet is in some way mangled at this point)
Open the spreadsheet in Excel and save it
Run the application again (with the pck.Save() still commented out) and it runs
Put the pck.Save(); line back in and the exception occurs.
So, there's a workaround which is impractical, i.e. do the manipulation save the file using EPPlus then save it in Excel then run the process without saving it using EPPlus.
It seems the .Save() method is putting the file in an unusual state, I don't know what else to try, any advice would be greatly appreciated.
You cannot call save() because your workbook doesn't have any worksheet bro. At least 1 sheet is required for a normal excel file.
Try to add var ws = pck.Workbook.Worksheets.Add("Sheet1"); before the save().
I used EPPlus long time ago. I don't know why the Document is removed but the dev team may put it back any time, below link.
http://epplus.codeplex.com/documentation
I just revisited this issue and found that EPPlus 4.0.5 has been released, the problem does not occur when using that version. Praise be!

Using SMO Library in SSIS 2008

Good afternoon,
Im having some trouble writing a simple script using SMO objects in C# to validate if an object exists and then create it. This code is within a Script Task Component in SSIS. The code executes successfully, however the new database does not show up on my local instance. Any help would be greatly appreciated.
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Collections;
using System.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
public void Main()
{
//String DBName = Dts.Variables["TmpViewDBName"].Value.ToString();
//String Instance = Dts.Variables["TmpViewDBInstance"].Value.ToString();
String DBName = "localhost";
String Instance = "TmpViewDB";
Server TmpViewServer = new Server(Instance);
//Windows Auth
TmpViewServer.ConnectionContext.LoginSecure = true;
TmpViewServer.ConnectionContext.Connect();
if (TmpViewServer.Databases[DBName] != null)
{
TmpViewServer.Databases[DBName].Drop();
}
Database TmpViewDB = new Database(TmpViewServer, DBName);
if (TmpViewServer.ConnectionContext.IsOpen)
TmpViewServer.ConnectionContext.Disconnect();
Dts.TaskResult = (int)ScriptResults.Success;
}
I believe you need to add a line to actually create the object. As it stands now, you've only instantiated the object but never actually made the call to the database to create the remote object.
Database TmpViewDB = new Database(TmpViewServer, DBName);
TmpViewDB.Create();

Entity Framework Error in Access VBA - "The specified named connection is either not found in the configuration..."

I have an Access VBA project from where I refer to a COM Interop .TLB written in C#. This C# code simply queries the SQL Server database and returns values via a simple LINQ-to-Entity query.
I'm getting the same error mentioned in this question:
The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid
However, in my case, it is a Access VBA in a .ADP application that refers to my .Net 4.0 TLB, instead of another .Net project.
I'm aware that if it were another .Net project, I could add the EF connection string XML in its app.config or web.config. But what is the fix if my 'calling' application is Access 2003 VBA?
Here's the VBA code that calls the .Net code
Dim CandidatePassword As String
Dim abc As New MISHash.Password
Dim PasswordStatus As Boolean
CandidatePassword = InputBox("Enter your password")
PasswordStatus = abc.IsValidPassword("myusername", CandidatePassword) ' FAILS HERE
If PasswordStatus Then
MsgBox "Password valid."
Else
MsgBox "Password failed."
End If
Please help. Thank you.
Update: Here is my C# code
using System.Linq;
using System.Runtime.InteropServices;
namespace MISHash
{
public class Password
{
public Password()
{
}
[ComVisible(true)]
public void HashAndSave(string SomePassword)
{
string hashed = BCrypt.HashPassword(SomePassword, BCrypt.GenerateSalt(12));
//save the hashed password in the database
}
[ComVisible(true)]
public bool IsValidPassword(string CandidateUserName, string CandidatePassword)
{
string OriginalHashedPassword;
using (MyDBEntities mycontext = new MyDBEntities())
{
OriginalHashedPassword = (from usr in mycontext.Users
where usr.UserName.Equals(CandidateUserName)
select usr.Password).FirstOrDefault();
}
bool matches = BCrypt.CheckPassword(CandidatePassword, OriginalHashedPassword);
return matches;
}
}
}
See this similar question:
Can I use / access the app.config from .net code, when called via COM
These two seem like your best options:
Manually create a secondary AppDomain
Convert to a VSTO project
Edit
You can also try passing a hard-coded connection string in the constructor:
MyDBEntities mycontext = new MyDBEntities("Server=.\SQLEXPRESS;Database=School;Trusted_Connection=true;Integrated Security=True;MultipleActiveResultSets=True"))