We found and interesting bug during a migration. The issue was that when there was an iteration through the controls in the forms, and you set the Enabled property, the property didn't get set.
After some research my friend Olman found this workaroung
foreach(Control c in Controls)
{
ctrl.Enabled = true;
if (ctrl is AxHost) ((AxHost)ctrl).Enabled = true;
}
Do you want to create a program to install your assembly in the GAC using C#. Well if you had that requirement or you are just curious, here is how.
I read these three articles:
Demystifying the .NET Global Assembly Cache
GAC API Interface
Undocumented Fusion
What I wanted just a straight answer of how to do it. Well here is how:
using System;
using System.Collections.Generic;
using System.Text;
using System.GAC;
//// Artinsoft
//// Author: Mauricio Rojas orellabac@gmail.com mrojas@artinsoft.com
//// This program uses the undocumented GAC API to perform a simple install of an assembly in the GAC
namespace AddAssemblyToGAC
{
class Program
{
static void Main(string[] args)
{
// Create an FUSION_INSTALL_REFERENCE struct and fill it with data
FUSION_INSTALL_REFERENCE[] installReference = new FUSION_INSTALL_REFERENCE[1];
installReference[0].dwFlags = 0;
// Using opaque scheme here
installReference[0].guidScheme = System.GAC.AssemblyCache.FUSION_REFCOUNT_OPAQUE_STRING_GUID;
installReference[0].szIdentifier = "My Pretty Aplication Identifier";
installReference[0].szNonCannonicalData= "My other info";
// Get an IAssemblyCache interface
IAssemblyCache pCache = AssemblyCache.CreateAssemblyCache();
String AssemblyFilePath = args[0];
if (!System.IO.File.Exists(AssemblyFilePath))
{
Console.WriteLine("Hey! Please use a valid path to an assembly, assembly was not found!");
}
int result = pCache.InstallAssembly(0, AssemblyFilePath,installReference);
//NOTE recently someone reported a problem with this code and I tried this:
// int result = pCache.InstallAssembly(0, AssemblyFilePath,null); and it worked. I think is a marshalling issue I will probably review it later
Console.WriteLine("Process returned " + result);
Console.WriteLine("Done!");
}
}
}
And here's the complete source code for this application: DOWNLOAD SOURCE CODE AND BINARIES
I present here the implementation of some useful tasks
In Artinsoft we perform massive migrations of VB6 code to VB.Net
and C#.
And sometimes after migration there are customizations to be
performed on the code, to add new functionality or to set certain new
properties.
The idea was to provide a couple of very simple and puntual MSBuildTask
to illustrate how easy it is to create custom tasks and to provide a starting
point to create new one.
You can freely use this code, just keep this comments and remember this is just
a sample code. There are not warranties. ;) And i made it a rush I know it could have
been written better
Artinsoft
mrojas@artinsoft.com
The implemented tasks are:
RemoveCOMReference |
Removes COMReferences from your project. COM references are for when you are using things thru Interop |
FixOutputPath |
Resets the output paths to bin\Release and bin\Debug |
AddProjectReference |
Add a reference to another project. A nice feature is that it generates RelativePaths the way Visual Studio does |
AddSimpleReference |
Add a reference to a very simple references like the ones you add when you click Add Reference and add System.EnterpriseServices |
ChangeCurrentBuildSetting |
This can be used for a lot of things.
For example to turn on or off the RegisterForComInterop setting
To set conditional compilation variables
To set debug info to pdbonly
The sky is the limit jeje |
The following is a sample project file
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- make sure that the Assembly is in a place where msbuild can find it, a simple way is just to put it
in the same directory of your .proj file -->
<UsingTask TaskName="SomeUsefulTasks.MSBuild.RemoveCOMReference"
AssemblyFile="SomeUsefulTasks.dll"/>
<UsingTask TaskName="SomeUsefulTasks.MSBuild.FixOutputPath"
AssemblyFile="SomeUsefulTasks.dll"/>
<UsingTask TaskName="SomeUsefulTasks.MSBuild.AddProjectReference"
AssemblyFile="SomeUsefulTasks.dll"/>
<UsingTask TaskName="SomeUsefulTasks.MSBuild.AddSimpleReference"
AssemblyFile="SomeUsefulTasks.dll"/>
<UsingTask TaskName="SomeUsefulTasks.MSBuild.ChangeProjectBuildSetting"
AssemblyFile="SomeUsefulTasks.dll"/>
<ItemGroup>
<VSProjects Include="$(Start)\**\*.*proj" />
</ItemGroup>
<!--
Run with
MSBUILD SampleProject.proj /target:COMReference /p:Start="C:\MyCode"
-->
<Target Name="COMReference">
<RemoveCOMReference SourceFiles="@(VSProjects)" ComReferenceName="MSXML2" />
</Target>
<!--
Adds a project reference
Run with
MSBUILD SampleProject.proj /target:AddProjectReference /p:Start="C:\MyCode" /p:ProjectPath="C:\MyCode\MyNewSuperProject\Project1.csproj"
-->
<Target Name="AddProjectReference">
<AddProjectReference SourceFiles="@(VSProjects)" AbsolutePathToProject="$(ProjectPath)"/>
</Target>
<!--
Adds a reference to a standard assembly
Run with
MSBUILD SampleProject.proj /target:AddSimpleReference /p:Start="C:\MyCode" /p:Reference="System.EnterpriseServices"
-->
<Target Name="AddSimpleReference">
<AddSimpleReference SourceFiles="@(VSProjects)" Reference="$(Reference)" />
</Target>
<!--
Resets the OutputPaths to .\bin\Debug and .\bin\Release
Run with
MSBUILD SampleProject.proj /target:FixOutput /p:Start="C:\MyCode" /p:Reference="System.EnterpriseServices"
-->
<Target Name="FixOutput">
<FixOutputPath SourceFiles="@(VSProjects)" />
</Target>
<!--
Adds a reference to a standard assembly
There are several options here for example to set the project debug info to pdb-only do this:
Run with
MSBUILD SampleProject.proj /target:ChangeSettingToPDBOnly /p:Start="C:\MyCode"
Or run with
MSBUILD SampleProject.proj /target:ChangeSettingAddAConstant /p:Start="C:\MyCode"
Or run with
MSBUILD SampleProject.proj /target:SettingComInterop /p:Start="C:\MyCode"
-->
<Target Name="ChangeSettingToPDBOnly">
<ChangeProjectBuildSetting
SourceFiles="@(VSProjects)"
ConfigurationType="All"
Setting="DebugType"
NewValue="pdbonly" />
</Target>
<Target Name="ChangeSettingAddAConstant">
<ChangeProjectBuildSetting
SourceFiles="@(VSProjects)"
ConfigurationType="All"
Setting="DefineConstants"
NewValue="MYNEWVAL"
Add="True"/>
</Target>
<Target Name="SettingComInterop">
<ChangeProjectBuildSetting
SourceFiles="@(VSProjects)"
ConfigurationType="All"
Setting="RegisterForComInterop"
NewValue="true" />
</Target>
</Project>
DOWNLOAD CODE AND BINARIES
I had the requirement of creating a MSBuild custom task that opens a .csproj
adds a reference to another project.
The problem I faced is that references in VisualStudio are generated as relative paths,
so I needed something to help me generate relative paths.
After some Googleing I finally found this code. It was in a long forum discussion
and was posted by a guy named something like Marcin Grzabski. And here it is for posterity.
private static string EvaluateRelativePath(string mainDirPath, string absoluteFilePath)
{
string[]
firstPathParts =
mainDirPath.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
string[]
secondPathParts =
absoluteFilePath.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
int sameCounter = 0;
for (int i = 0; i < Math.Min(firstPathParts.Length,secondPathParts.Length); i++)
{
if (
!firstPathParts[i].ToLower().Equals(secondPathParts[i].ToLower()))
{
break;
}
sameCounter++;
}
if (sameCounter == 0)
{
return absoluteFilePath;
}
string newPath = String.Empty;
for (int i = sameCounter; i < firstPathParts.Length; i++)
{
if (i > sameCounter)
{
newPath += Path.DirectorySeparatorChar;
}
newPath += "..";
}
if (newPath.Length == 0)
{
newPath = ".";
}
for (int i = sameCounter; i < secondPathParts.Length; i++)
{
newPath += Path.DirectorySeparatorChar;
newPath += secondPathParts[i];
}
return newPath;
}
And to use is just do somelines like:
String test = EvaluateRelativePath(@"E:\Source_Code\Code\ProjectsGroup1\Project1", @"E:\Source_Code\Code\ProjecstGroup2\Project2");
//This will genearate something like ..\..\ProjectGroup2\Project2
In my past life I spent a few eons writing Java code. And it wasn't bad. We had nice tools like Jalopy! that allowed us to have
code in a very standard way.
And I missed that. I've been looking around for something similar but I havent found anything like that :(
Until I found a great post from Chris Eargle, he improved the original solution from Kelvinpinch
Well here's the code.
Public Sub FormatSolution()
Dim sol As Solution = DTE.Solution
For i As Integer = 1 To sol.Projects.Count
FormatProject(sol.Projects.Item(i))
Next
End Sub
Private Sub FormatProject(ByVal proj as Project)
For i As Integer = 1 To proj.ProjectItems.Count
FormatProjectItem(proj.ProjectItems.Item(i))
Next
End Sub
Private Sub FormatProjectItem(ByVal projectItem As ProjectItem)
If projectItem.Kind = Constants.vsProjectItemKindPhysicalFile Then
If projectItem.Name.LastIndexOf(".cs") = projectItem.Name.Length - 3 Then
Dim window As Window = projectItem.Open(Constants.vsViewKindCode)
window.Activate()
projectItem.Document.DTE.ExecuteCommand("Edit.FormatDocument")
window.Close(vsSaveChanges.vsSaveChangesYes)
End If
End If
'Be sure to format all of the ProjectItems.
If Not projectItem.ProjectItems Is Nothing Then
For i As Integer = 1 To projectItem.ProjectItems.Count
FormatProjectItem(projectItem.ProjectItems.Item(i))
Next
End If
'Format the SubProject if it exists.
If Not projectItem.SubProject Is Nothing Then
FormatProject(projectItem.SubProject)
End If
End Sub
To use it perform the following steps:
- Go to the VS IDE Tools Option
- Then Select the Macros option and select Macros IDE...
- This will open the macros IDE
- In the Macros IDE navigate to the Module1, and Insert the code
To run the Macro go to Tools\Macros\Macro Explorer
And select FormatAll :)
And last but not least if you want to runit from the command line just do:
devenv /command "Macros.MyMacros.Module1.FormalAll" MyProject.csproj or
devenv /command "Macros.MyMacros.Module1.FormalAll" MySol.sln or
During migration to C# or .NET it is easier to keep the same ActiveX.
The VBCompanion does a great work in migrating the ActiveX control using the .NET ActiveX wrappings and fixing all method calls.
Sadly sometimes those ActiveX do not work properly in .NET.
Well we have good news.
Recently my friend Jose David (who we keep bothering because he is now
a Project Manager and now he only programs in MS Excel and MS Project, I added the MS by his request :P) fixed a curious bug
we had with an aplication we migrated from VB6 to C#.
The thing is that the aplication had an ActiveX control with a strange runtime behaviour.
We migrated the application keeping the ActiveX control and in most ocasions it worked ok.
But randomly it started throwing exceptions.
During the testing he discovered that if he repeated the steps slowly the bug did not reproduced.
So his idea was that it was due a garbage collection issue. And SURPRINSINLY he was right :P
He added this:
System.GC.Collect();
System.
GC.WaitForPendingFinalizers();
And the application started to work.
It seems like some of the COM objects needed a little more time for releasing all references :)
Recently we added some support for migrating the IsMissing function to VB.NEt or C#
The thing is. In VB6 the IsMissing Function is TRUE only if you have something like:
Public Sub Foo(Optional str)
Where you dont specify the variable type, or if you have
Public Sub Foo(Optional str as Variant)
And is IsMissing is FALSE for any other case. Including Optional variables whose definition type is not Variant.
So let's see some examples to illustrate the idea:
Example 1:
Public Sub Foo(str, a As Integer, b As Integer, Optional c As Integer)
MsgBox (str & "Foo Is missing a " & IsMissing(a))
MsgBox (str & "Foo Is missing b " & IsMissing(b))
MsgBox (str & "Foo Is missing c " & IsMissing(c))
End Sub
It occurs that IsMissing is really FALSE in all cases. So it is equivalent to:
Public Sub Foo(str, a As Integer, b As Integer, Optional c As Integer)
MsgBox (str & "Foo Is missing a " & false)
MsgBox (str & "Foo Is missing b " & false)
MsgBox (str & "Foo Is missing c " & false)
End Sub
Example 2:
Public Sub Goo(str, a As Integer, b As Integer, Optional c As Object, Optional d As Byte, Optional e)
MsgBox (str & "Goo Is missing a" & IsMissing(a))
MsgBox (str & "Goo Is missing b" & IsMissing(b))
MsgBox (str & "Goo Is missing c" & IsMissing(c))
MsgBox (str & "Goo Is missing d" & IsMissing(d))
MsgBox (str & "Goo Is missing e" & IsMissing(e))
End Sub
All cases EXCEPT "e" are equivalent to FALSE
Public Sub Goo(str, a As Integer, b As Integer, Optional c As Object, Optional d As Byte, Optional e)
MsgBox (str & "Goo Is missing a" & false)
MsgBox (str & "Goo Is missing b" & false)
MsgBox (str & "Goo Is missing c" &false)
MsgBox (str & "Goo Is missing d" & false)
MsgBox (str & "Goo Is missing e" & IsMissing(e))
End Sub
So if you are migrating your VB6 Code to C# put attention to these little details it can save you a lot of time.And remember that this is just one feature of VBCompanion tool ;)
AJAX applications are great
Their coolness factor is high, and your clients
will be trilled by the new interactivity of your pages
But as a developer when you have a problem with
AJAX it could be hard to track. Because there is
a lot going on behind the scenes
Some of our costumers migrate their ASP applications to
to ASP.NET and it's natural that they want
some AJAX in it.
So I found a GREAT GREAT application for AJAX debugging
It's called fiddler. I cannot describe it all for you so go its site and watch the videos
The tool is just amazing :)
It really can see everything that the browser
receives and sends and more.
A definitive must
Fiddler
One of my purposes for this year was to start
using more the MSBuild
Saldly I'm still not as good on it as I expected
This link provided some interetings points
MS Build and VS
Recently a friend at work had a problem querying a SQL server that indicated
that the column name was wrong.
The only thing wrong was the the case. For example he had COLUMN1 instead of
Column1. I had never seen that problem in SQLServer.
I had seed that in Sybase but not in SQLServer. He solved that by changing the
database collating sequence to something like this:
alter database database1 collate
SQL_Latin1_General_CP1_CI_AI
the CI in the collating indicates Case Insensitive
For more information on SQL Server collations check:
http://msdn2.microsoft.com/en-us/library/aa258233(SQL.80).aspx
And you determine your current database collation use a code like this:
USE
yourdb>
GO
print
'My
database ['
+
db_name()
+
']
collation is: '
+
cast(
DATABASEPROPERTYEX
(
db_name(),
N'Collation'
)
as
varchar(128)
)
print
'My tempdb
database collation is: '
+
cast(
DATABASEPROPERTYEX
(
'tempdb',
N'Collation'
)
as
varchar(128)
)
A simple way of getting the GUID from an exe, dll, ocx, tlb is using the
TLBINF32.dll
This file is in the system path and it must be registered
(Remember to use regsvr32 if you haven't registered).
TLI.
TLIApplicationClass
a = new TLI.TLIApplicationClass();
try
{
TLI.TypeLibInfo
inf = a.TypeLibInfoFromFile(@"c:\windows\system32\MSHFLXGD.OCX");
MessageBox.Show(
"TypeLibrary Name " +
inf.Name + "\r\n" +
//name of (Type Library)
"Tlb ID " + inf.GUID
+ "\r\n" + // GUID
for Library
"LCID " + inf.LCID +
"\r\n" + //
Language / Country
"Major Version "+
inf.MajorVersion + "\r\n" +
// Major Version
"Minor Version "+
inf.MinorVersion); // Minor Version
for (short
i = 1; i < inf.TypeInfoCount; i++)
{
TLI.TypeInfo inf2 = inf.TypeInfos[i];
MessageBox.Show("CLSID
" + inf2.Name + " - " + inf2.GUID,i +
" of " +
inf.TypeInfoCount);
}
}
catch (Exception
ee)
{
MessageBox.Show("No
guid");
}
Microsoft recently published the
MTPS Content Service
In brief, the MTPS Content Services are a set of
web services for exposing the content in
Microsoft/TechNet Publishing System (MTPS).
And Craig Andera, has develop a swift nice tool
called
msdnman that you use from the command line to
get information from the MSDN
Also I recommend you to see this
cool video of a Human LCD that Hugo sent me
I was trying to compile a C++ Project in Visual Studio
and this project has a post-build step that executes
some batch statements.
And succendly from one day to the other the project failed
and the only message I had was
The input line is too long
After some googling I found that this error is displayed
when the command line in the command prompt
exceeds the maximum number of characters.
(Something quite obvious jeje)
The bottom line was that recenlty I had installed
some software and my PATH variable became TOO long
I fixed it by creating a new environment variable
called EXTRA_PATH where I put some references to
Some big paths and then changed my PATH variable
replacing all those long variables for
%EXTRA_PATH%.
And problem solved!
IE7 has an interesting bug...
Someone decided to change the printing default
to "Shrink to Fit".This is supposed to be good
because it will make printing of web pages better
but it affects several applications develop for
IE, for example if you are using the Web Access
to your Exchange server, or if you have an application
that prints on a page that is not a letter size page
Well a
guy from a forum found an excellent solution.
The only problem is that he refers a hot fix,
but if you have problems with that hotfix, then
do the following:
1. Go to windowsupdate.microsoft.com
2. Make sure you have the following updates:
** Security Update for Internet Explorer 7 for Windows XP (KB938127)
** Cumulative Security Update for Internet Explorer 7 for Windows XP (KB937143)
And then run the following in the command prompt:
reg add "HKLM\SOFTWARE\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_STF_Scale_Min" /v iexplore.exe /t REG_DWORD /d 100 /f
Every SERIOUS VB6 application, ended up requiring
that you import some functionality from the windows API or another DLL.
However if you were looking for a .NET equivalent and
google did not take you to the right page,
there is a document (a little old I might say)
called
Microsoft Win32 to Microsoft .NET Framework API Map
I remember a couple of other pages that were even more complete,
but right know I can only remember
http://pinvoke.net that gives some info about APIs
I felt so stupid today. Because when for a couple of months,
every now and then, when someone asked me for a copy of the source code
I just made a copy of my src directory and then searched for the .svn* folder
and then I erased them,
until by mistake i found the
EXPORT function.
I use SmartSVN and I was creating a new project
and then the light came to me :S
Well if it happens to you just remember there is a
svn export
command. This will get the code from the repository without all the nasty files.