I have a DLL written in C#.NET which exposes a COM interface, so a vb6 application can call my DLL. This interface looks like:
[System.Runtime.InteropServices.Guid("3D2C106C-097F-4ED7-9E4F-CDBC6A43BDC4")]
public interface IZDPharmaManager {
[System.Runtime.InteropServices.DispId(2)]
SearchPatientEventArgs FoundPatient { get; set; }
[System.Runtime.InteropServices.DispId(3)]
IntPtr Start(string server, string database, string user, string password, bool integrated, int praktijkID, string userGUID, int userID, string userName, bool hasRightToSearchPatient);
[System.Runtime.InteropServices.DispId(4)]
void Stop();
[System.Runtime.InteropServices.DispId(5)]
void InitializeSkinner(System.Object skinnerFramework);
}
[System.Runtime.InteropServices.Guid("4438852E-CF2D-4DB0-8E6E-428F65A6B16C")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IZDPharmaManagerEvents {
[DispId(1)]
void SearchPatient(ZDPharmaManager sender, SearchPatientEventArgs e);
}
[System.Runtime.InteropServices.Guid("9297D43F-C581-3F0F-AA60-9506C6B77B5F")]
[ClassInterface(ClassInterfaceType.None)]
public class SearchPatientEventArgs : WebHIS.ZDPharmaceutisch.ISearchPatientEventArgs {
public SearchPatientEventArgs() {
//Nodig voor COM.
}
public int ID { get; set; }
public string FullName { get; set; }
public string OwnName { get; set; }
public string PartnerName { get; set; }
public string DateOfBirth { get; set; }
public string ZipCode { get; set; }
public string HouseNumber { get; set; }
public string BSN { get; set; }
}
public delegate void SearchPatientEventHandler(ZDPharmaManager sender, SearchPatientEventArgs e);
[System.Runtime.InteropServices.Guid("465AC7EC-27EF-3D95-AAA6-29D01FCF15A1")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IZDPharmaManagerEvents))]
public class ZDPharmaManager : WebHIS.ZDPharmaceutisch.IZDPharmaManager {
public event SearchPatientEventHandler SearchPatient = null;
public SearchPatientEventArgs FoundPatient { get; set; }
//private MainForm GraphicalInterface { get; set; }
private ChoosePatient GraphicalInterface { get; set; }
public ZDPharmaManager() {
//Nodig voor COM.
}
#region IZDPharmaManager Members
public IntPtr Start(string server,
string database,
string user,
string password,
bool integrated,
int praktijkID,
string userGUID,
int userID,
string userName,
bool hasRightToSearchPatient) {
//Zet connectiestring.
DAL.DAC.CnnInfo = new System.Data.SqlClient.SqlConnectionStringBuilder() {
DataSource = server,
InitialCatalog = database,
UserID = user,
Password = password,
IntegratedSecurity = integrated
};
DAL.DAC.PracticeID = praktijkID;
DAL.DAC.UserGUID = userGUID;
DAL.DAC.UserID = userID;
DAL.DAC.UserName = userName;
DAL.DAC.HasRightToSearchPatient = hasRightToSearchPatient;
//Apotheek IDs ophalen en bewaren.
DAL.DAC.PharmacyIDs = DAL.PracticeDAO.GetPharmacyByPracticeID(praktijkID);
//Initialiseer grafische interface.
//this.GraphicalInterface = new MainForm();
this.GraphicalInterface = new ChoosePatient();
//Haal ongekoppelde afhaalberichten op.
this.GraphicalInterface.Patients = new VML.PatientsVM(this);
//Toon grafische interface.
this.GraphicalInterface.Show();
return this.GraphicalInterface.Handle;
}
public void Stop() {
foreach (var item in this.SearchPatient.GetInvocationList()) {
this.SearchPatient -= (SearchPatientEventHandler)item;
}
this.GraphicalInterface.Close();
this.GraphicalInterface = null;
this.FoundPatient = null;
}
public void InitializeSkinner(System.Object skinnerFramework) {
WebHIS.ZDPharmaceutisch.SkinnerModule.SkinFramework = (XtremeSkinFramework.SkinFramework)skinnerFramework;
}
#endregion
internal virtual void OnSearchPatient(SearchPatientEventArgs e) {
if (this.SearchPatient != null) {
this.SearchPatient(this, e);
}
}
}
This works fine. But each time I build this DLL without changing the interface (because I had to fix something somewhere in the logic) the reference with the vb6 application is broken and we need to recompile the vb6 application.
Does anyone know what I am doing wrong? 'Cause we had vb.net DLL's which didn't break the reference after recompile due to fixed GUIDs. Any help would be much appreciated.
Update
Both vb6 app and DLL are operational. But when I recompile the DLL and test it on our testserver via the vb6 application I get an automation error (which usually means the reference is broken and you need to recompile the vb6 app aswell)
I don't see any strong leads that could explain this problem. The [Guid] attribute for the assembly matters, that sets the type library ID. And the [AssemblyVersion] matters, that sets the type library version number. The attributes are declared in the project's AssemblyInfo.cs file. Make sure your build system doesn't monkey with these attributes.
Best way to go about it is to find out what exactly changes. Run the OleView.exe utility from the Visual Studio Command Prompt. File + View Typelib and select the .tlb file. Copy/paste the content of the right panel into a text file.
Rebuild the project and repeat the OleView exercise. You can now simply use a diffing tool to see what exactly changed. Update your question with what you found out if you need more help.
Related
How can I add a proxy to my source to exclude filtering?
my file image
public class TeleShrap
{
private WebRequest r;
public event OnMessage NewMessageFromUser;
public event OnMessage NewMessageFromChat;
private bool Start { get; set; }
private long UpdateId { get; set; }
private string addres = "https://api.telegram.org/bot";
public string filepth_addres = "https://api.telegram.org/file/bot";
public string token = string.Empty;
public void StartUpdates()
{
Start = true;
new Task(Updating).Start();
}
The TeleSharp project seems to be abandoned for years now.
I'd suggest you use the Telegram.Bot library. You can find information about adding a proxy on the Wiki.
I'm currently working on my final project which contains the use of WCF, WPF and C# and I'm having a hard time transfering an object through wcf.
I get an error after a while which says that the server did not provide a meaningful response.
The classes that are in use in the method that crashes are:
[DataContract]
public class Player
{
//public static int clientID = 0;
[DataMember]
public int Wins { get; set; }
[DataMember]
public int Loses { get; set; }
[DataMember]
public int realID { get; }
[DataMember]
public string nickName { get; set; }
public Player(int Wins, int Loses, string nickName)
{
this.Wins = Wins;
this.Loses = Loses;
this.nickName = nickName;
//clientID++;
realID = 1; //clientID;
}
}
[DataContract]
public class Run
{
[DataMember]
public List<Player> Players { get; set; }
[DataMember]
public bool isActive { get; set; }
public Run()
{
Players = new List<Player>();
}
public void playerJoined(Player player)
{
Players.Add(player);
}
public void playerLeft(Player player)
{
if (Players.Contains(player)) Players.Remove(player);
}
public void generateRun()
{
// TODO: get a random map from the DB and pass it to all players
return;
}
}
and the method that crashes the code is:
public Run getRunDetails(int runNumber)
{
runNumber = runNumber - 1;
return Runs[runNumber];
}
the code at the client side is:
ListBoxItem tempItem = ((ListBoxItem)allRuns.SelectedItem);
if(tempItem != null && !tempItem.Content.Equals("There are no runs available, create one now."))
{
string numString = ((string)tempItem.Content);
numString = numString.Substring(4, numString.Length - 4);
run = Service.getRunDetails(int.Parse(numString));
}
After some time of debugging I've found out the problem is in the list variable, I've tried to change it only to a Player variable -> getting the same error. Same goes for making my buffer and message sizes bigger.
The only way the code wont crash and send my Run object is when the List is not a data member..
//[DataMember]
public List<Player> Players { get; set; }
If I do the above the code works perfectly but I desperately need the List passed to the client side.
Sorry for the long post but I don't have a very long time and I need it done, any help will be very appreciated.
(Also, sorry for the poor formatting, I did my best)
I'm pretty sure the problem here is that you don't have a parameterless constructor in your Player...
try to add a
public Player() {}
to your class...
Either that or because your 'realId' [DataMember] has no setter, see this link for tips on correctly serializing readonly members.
WCF: Exposing readonly DataMember properties without set?
Also, dont forget to 'Update Service Reference' on the WCF Service in the Visual Studio client Project if you have changed members in classes that are passed across the WCF Channel.
I am currently developing a file indexing system. I have an interface IDiskDrive that can get immediate file items (files/folders). The interface definition is as follows...
public interface IDiskDrive
{
bool IsReady { get; }
string Name { get; }
string VolumeLabel { get; }
string VolumeLabelName { get; }
DiskDriveType Type { get; }
FolderPath RootFolder { get; }
DiskDriveUsage Usage { get; }
IEnumerable<IFileItem> GetImmediateFileItems(FolderPath path);
}
The ability to read all file/folders is complete and works correctly. Now, I need to actually index the file files and folders. Looking ahead I know I will need some reporting tools. This leads me to think I need another abstraction, based upon IDiskDrive that can read/populate. I also need the ability to select drives for indexing.
My question is should my new class inherit IDiskDrive or should I use composition (possibly a decorator)?
// inheritance
class IndexedDiskDrive : IDiskDrive
{
public IndexedDiskDrive(IDiskDrive drive)
{
...
}
public int Id {get; internal set; } // database id
public bool Selected { get; internal set; }
public DateTime? DateLastIndexed { get; internal set; }
// IDiskDrive implementation
public bool IsReady
{
get { return this.Drive.IsReady; }
}
}
or composition...
class IndexedDiskDrive
{
public IndexDiskDrive(IDiskDrive drive)
{
this.Value = drive;
}
public IDiskDrive Value
{
get;
private set;
}
// additional properties
public int Id { get; internal set; }
public bool Selected { get; internal set;}
public DateTime DateLastIndexed { get; internal set; }
}
Note:
I need access to the underlying IDiskDrive for the UI.
For example, I request user to select drives to index. I initially supply a list of local drives and the ability to add network drives. To try and keep code simple, I thought the idea of a new class with a selected property might help.
This allows the GUI to enumerate a list of IndexedDiskDrives and set/clear the select property.
In both examples you expose the IDiskDrive object from the other object. In the first case you inherit from the same inteface, which means you expose the same methods and in the other case you expose the object via a property.
I don't see a reason yet why you want to do this.
It sounds like a typical constructor DI case to me. Just have a new interface for your new class which is doing a different job and hence requires a different contract, and if it needs the IDiskDrive object as a dependency, then just inject it via the constructor and leave it as it is.
P.S.: I know this is not something you have asked, but you might be interested in Lucense.NET, which is a .NET library to index files. They might have already solved your problem for your:
http://lucenenet.apache.org/
EDIT:
From your current class design I would do the following:
void Main()
{
// Use IoC container in real app:
var diskDrive = new DiskDrive(...);
var fileIndexer = new FileIndexer();
var fileItems = diskDrive.GetImmediateFileItems(filePath);
fileIndexer.IndexFiles(fileItems);
}
// Define other methods and classes here
public interface IDiskDrive
{
bool IsReady { get; }
string Name { get; }
string VolumeLabel { get; }
string VolumeLabelName { get; }
DiskDriveType Type { get; }
FolderPath RootFolder { get; }
DiskDriveUsage Usage { get; }
IEnumerable<IFileItem> GetImmediateFileItems(FolderPath path);
}
public interface IFileIndexer
{
void IndexFiles(IEnumerable<IFileItem> files);
}
public class FileIndexer : IFileIndexer
{
public void IndexFiles(IEnumerable<IFileItem> files)
{
// do stuff
}
}
Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}
I've moved the CS files out the App_Data folder because I get errors with duplicate builds, one builds at compile time one at run time so I was told to move them out that folder.
Two files, Default.aspx.cs
namespace CrystalTech
{
public partial class newVersion_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoginInitialiser loginData = new LoginInitialiser();
loginData.loadData();
}
}
}
And Security.cs
namespace CrystalTech
{
// Handles all our login logic
public class LoginInitialiser
{
public bool isLoggedIn { get; private set; }
public LoginInitialiser()
{
this.isLoggedIn = false;
}
// Fetches the users data
public void loadData()
{
if (HttpContext.Current.Session["loggedIn"] != null)
{
this.isLoggedIn = (bool)HttpContext.Current.Session["loggedIn"];
}
// Fetch the data if we are logged in
if (this.isLoggedIn)
{
}
// Otherwise redirect
else
{
HttpContext.Current.Response.Redirect("../logins/index.asp?action=dn&r=" + CommonFunctions.GetCurrentPageName());
}
}
}
// Holds users information
public class User
{
public int ID { get; private set; }
public string username { get; private set; }
public Company company { get; private set; }
public string title { get; private set; }
public string forenames { get; private set; }
public string surnames { get; private set; }
public string email { get; private set; }
}
// Holds company information
public class Company
{
public int ID { get; private set; }
public string name { get; private set; }
public string telephone { get; private set; }
}
}
Why does Default.aspx.cs throw:
The type or namespace name 'LoginInitialiser' could not be found (are you missing a using directive or an assembly reference?)
If you want your classes to live somewhere other than App_Code, add a "Class Library" project to your solution and put that class there. Don't forget to reference that project in your webproject.
Only code in the App_Code directory is compiled - So unless your files are in there (or a subdirectory), they won't be picked up
And what do you mean by duplicate builds? Depending on project settings, the actual build can be triggered either by the first visit to the site or by the compile itself. It's also possible to do half-and-half. with a little preparation beforehand on (build/publish) and the actual compile on the first site visit
The code files (except the ones related to aspx files) are put in the App_Code folder only to ensure their compilation (This is requirement imposed by ASP.net and now I know from comments it is only for web site projects and not web application projects). If they are anywhere else they are not compiled. Check and you will not find Build Action for the code files outside App_Code folder.