Pin app in taskbar in visual studio setup project - visual-studio-2010

When Mozilla Firefox installs, the installer pins Mozilla in taskbar, and I want it too!!!
I'm using VS2010

Vb.net Code snippet for Pin to/Unpin from Task Bar and Start Menu. (framework 3.5)
Dim shellApplication As Shell = New ShellClass()
Dim directoryName As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
Dim directory As Shell32.Folder = shellApplication.[NameSpace](directoryName)
Dim link As FolderItem = directory.ParseName(fileName)
Dim verbs As FolderItemVerbs = link.Verbs()
For i As Integer = 0 To verbs.Count - 1
Dim verb As FolderItemVerb = verbs.Item(i)
Dim verbName As String = verb.Name.Replace("&", String.Empty)
If (verbName.Equals("Pin to Start Menu")) Or (verbName.Equals("Unpin from Start Menu")) Then
verb.DoIt()
End If
Next
shellApplication = Nothing
'filePath is the path of .exe file that you want to pin/Unpin taskbar
In Case of Pinning/Unpinning Taskbar replace "Pin to Start Menu" with "pin to taskbar"
and "Unpin from Start Menu" to "unpin from taskbar"
All the pinned files resides in the
C:\Users\%LoggedIn_User_Name%\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned
This Code is working on Windows7 US English.
Cheers!

private static void PinUnpinTaskBar(string filePath, bool pin) {
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
// create the shell application object
Shell shellApplication = new ShellClass();
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
Folder directory = shellApplication.NameSpace(path);
FolderItem link = directory.ParseName(fileName);
FolderItemVerbs verbs = link.Verbs();
for (int i = 0; i < verbs.Count; i++) {
FolderItemVerb verb = verbs.Item(i);
string verbName = verb.Name.Replace(#"&", string.Empty).ToLower();
if ((pin && verbName.Equals("pin to taskbar")) || (!pin && verbName.Equals("unpin from taskbar"))) {
verb.DoIt();
}
}
shellApplication = null;
}
be sure to include the "Microsoft Shell Controls And Automation" reference
and say thanks to #James Johnston - his original post

Related

Currupted file in non-english locale (encoding problem?)

In my MSI Windows Installer I have a custom VBScript action which extracts some files from the 'Binary' table to the filesystem. This is the code I'm using:
Inspired by: https://www.itninja.com/question/how-to-call-an-exe-which-is-stored-in-a-binary-table-through-a-vbscript-custom-action-in-the-msi
Function ExtractFromBinary(ByVal binaryName, ByVal binaryOutputFile)
Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")
Const msiReadStreamInteger = 0
Const msiReadStreamBytes = 1
Const msiReadStreamAnsi = 2
Const msiReadStreamDirect = 3
Dim binaryView : Set binaryView = Session.Database.OpenView("SELECT Data FROM Binary WHERE Name = '" & binaryName & "'")
binaryView.Execute
Dim binaryRecord : Set binaryRecord = binaryView.Fetch
Dim binaryData : binaryData = binaryRecord.ReadStream(1, binaryRecord.DataSize(1), msiReadStreamAnsi)
Set binaryRecord = Nothing
Dim binaryStream : Set binaryStream = oFSO.CreateTextFile(binaryOutputFile, True, False)
binaryStream.Write binaryData
binaryStream.Close
Set binaryStream = Nothing
End Function
This has been used without any issues in production for 2-3 years now. However now we have a case on a Japanese Windows installation where the extracted binary files are corrupted:
As you can see, the problem typically after a '?' where the script either inserts an 'E', or overwrites the following character.
Both the ReadStream method and the CreateTextFile method have a parameter which affect encoding. The combination shown above seems to be the only one which works on my English Windows 10.
What do I need to change in the code above to make it work also on a Japanese system?
#Robert-Hegner I'll propose this as an answer, even though it is subject to your testing (I have no way of testing where I am)!
I've included an updated approach here (you will need to scroll down to the second example)
It uses msiReadStreamDirect (not msiReadStreamAnsi) to extract a string of Byte pairs, converts these into binary and creates the output file using the ADODB.Stream (not the FSO).
Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim tempFolder : tempFolder = oFSO.GetSpecialFolder(2)
Dim outputFile : outputFile = tempFolder & "\notepad.exe"
extractFromBinary "notepad", outputFile
Function MultiByteToBinary(MultiByte)
'obtained from http://www.motobit.com
'MultiByteToBinary converts multibyte string To real binary data (VT_UI1 | VT_ARRAY)
'Using recordset
Dim RS, LMultiByte, Binary
Const adLongVarBinary = 205
Set RS = CreateObject("ADODB.Recordset")
LMultiByte = LenB(MultiByte)
If LMultiByte>0 Then
RS.Fields.Append "mBinary", adLongVarBinary, LMultiByte
RS.Open
RS.AddNew
RS("mBinary").AppendChunk MultiByte & ChrB(0)
RS.Update
Binary = RS("mBinary").GetChunk(LMultiByte)
End If
Set RS = Nothing
MultiByteToBinary = Binary
End Function
Function SaveBinaryData(FileName, ByteArray)
Const adTypeBinary = 1
Const adSaveCreateOverWrite = 2
'Create Stream object
Dim BinaryStream
Set BinaryStream = CreateObject("ADODB.Stream")
'Specify stream type - we want To save binary data.
BinaryStream.Type = adTypeBinary
'Open the stream And write binary data To the object
BinaryStream.Open
BinaryStream.Write ByteArray
'Save binary data To disk
BinaryStream.SaveToFile FileName, adSaveCreateOverWrite
Set BinaryStream = Nothing
End Function
Function extractFromBinary(ByVal binaryName, ByVal binaryOutputFile)
Const msiReadStreamInteger = 0
Const msiReadStreamBytes = 1
Const msiReadStreamAnsi = 2
Const msiReadStreamDirect = 3
Dim binaryView : Set binaryView = Session.Database.OpenView("SELECT * FROM Binary WHERE Name = '" & binaryName & "'")
binaryView.Execute
Dim binaryRecord : Set binaryRecord = binaryView.Fetch
Dim binaryData : binaryData = binaryRecord.ReadStream(2, binaryRecord.DataSize(2), msiReadStreamDirect)
Set binaryRecord = Nothing
'convert to string of byte pairs to binary
binaryData = MultiByteToBinary(binaryData)
'save binary data
SaveBinaryData binaryOutputFile, binaryData
End Function
Set oFSO = Nothing
Japanese Code Page: From this blog entry: "Binary Files and the File System Object Do Not Mix": "In the Japanese code page, just-plain-chr(E0) is not even a legal character, so Chr will turn it into a zero... Do not use the FSO to read/write binary files, you're just asking for a world of hurt as soon as someone in DBCS-land runs your code."
Alternatives? How about .NET? I realized too late that you are in a custom action, I made the samples as standalone .NET console applications. The WiX framework has mechanisms to create a DTF custom action. Found this on github.com.
Rehashing?: Can we ask what you are actually doing? Why do you need to extract files this way? There could be other approaches that
are more reliable if you explain the scenario?
DTF / .NET: Though I am not a huge .NET fan for deployment use (too many layers of dependencies), I think you would do better using .NET / DTF for this. What is DTF?
Sample DTF C# Application: Below is a simple, C# sample application showing one way to extract a binary stream from the Binary table (there are several other ways, I am not a .NET expert).
Create a new C# Console App (.NET Framework).
Paste the below code in and adjust parameters.
Add reference to Microsoft.Deployment.WindowsInstaller.dll (DTF framework).
using Microsoft.Deployment.WindowsInstaller;
namespace MSIExtractBinaryTableEntry
{
class Program
{
static void Main(string[] args)
{
// ADJUST 1: Name of Binary Table Entry
var binarytableentry = "ImageBmp";
// ADJUST 2: Source MSI path
var msifullpath = #"C:\MySetup.msi";
// ADJUST 3: Output target path for binary stream
var binaryfileoutputpath = #"C:\Output.XXX";
using (var db = new Database(msifullpath, DatabaseOpenMode.ReadOnly))
{
using (var binaryView = db.OpenView("SELECT Name, Data FROM Binary WHERE Name='" + binarytableentry + "'"))
{
binaryView.Execute();
binaryView.Fetch().GetStream(2, binaryfileoutputpath); // force overwrites output path
}
}
}
}
}
Alternative: Here is a tweak that exports the whole Binary Table to a folder called "Output" on the user's desktop.
Same procedure to create a test project as above. Only one parameter to specify: the full path to the input MSI.
using System;
using System.IO;
using Microsoft.Deployment.WindowsInstaller;
namespace MSIExtractBinaryTableEntry
{
class Program
{
static void Main(string[] args)
{
// ADJUST 1: Specify MSI file path
var msifullpath = #"C:\MySetup.msi";
var outputpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), #"Output\");
Directory.CreateDirectory(outputpath);
using (var db = new Database(msifullpath, DatabaseOpenMode.ReadOnly))
{
using (var binaryView = db.OpenView("SELECT Name, Data FROM Binary"))
{
binaryView.Execute();
foreach (var rec in binaryView)
{
rec.GetStream("Data", outputpath + rec.GetString("Name"));
}
}
}
}
}
}
Here is what I ended up with.
As suggested by Stein Åsmul I rewrote the custom action using C# (.NET / DTF). Initially I was hesitant to writing custom actions in C# as it introduces additional prerequisites to the installer. But it turns out that if the custom action targets .NET Framework 2.0, it should be supported on most machines without the need to manually install the framework (see here).
So here is my code:
public static class TemporaryFilesExtractor
{
[CustomAction]
public static ActionResult ExtractTemporaryFiles(Session session)
{
ExtractFromBinary(session, "binaryname1", "<filePath1>");
ExtractFromBinary(session, "binaryname2", "<filePath2>");
return ActionResult.Success;
}
private static void ExtractFromBinary(Session session, string binaryName, string binaryOutputFile)
{
session.Log($"Extracting {binaryName} to {binaryOutputFile}");
byte[] buffer = new byte[4096];
using (var view = session.Database.OpenView("SELECT Data FROM Binary WHERE Name = '{0}'", binaryName))
{
view.Execute();
using (var record = view.Fetch())
using (var dbStream = record.GetStream(1))
using (var fileStream = File.OpenWrite(binaryOutputFile))
{
int count;
while ((count = dbStream.Read(buffer, 0, buffer.Length)) != 0)
fileStream.Write(buffer, 0, count);
}
}
}
}

Error on merging ppt files using visual basic scripting

I am a newbie to visual basic scripting..
I was trying to combine multiple ppt files into a single ppt using the following .vbs code.
it was supposed to create a new ppt called merged.ppt from all the ppts stored in a subfolder called PPTmerge.
But on executing I get error on line:
Set out = Application.Presentations.Open(f)
Can someone help me please...!
Const PPTMERGE_FILE = "Merged.ppt"
Const PPTMERGE_FOLDER = ".\PPTmerge"
Dim Application
Set Application=CreateObject("PowerPoint.Application")
Application.Visible = True 'must do this for merge to work
Dim first 'to open power point only once
first = True
Dim fs
Set fs=CreateObject("Scripting.FileSystemObject")
Dim folder
Set folder = fs.GetFolder(PPTMERGE_FOLDER)
Dim out
Dim f
Dim ff
For Each ff in folder.Files
f = PPTMERGE_FOLDER + "\" + ff.Name
If first Then
Dim p
Set out = Application.Presentations.Open(f)
out.SaveAs PPTMERGE_FOLDER + "\..\" + PPTMERGE_FILE
first = False
Else
out.Slides.InsertFromFile f, out.Slides.Count
End If
Next
If Not first Then
out.Save
out.SlideShowSettings.Run
'out.Close
End If
Set folder = Nothing
Set out = Nothing
Set folder = Nothing
'Application.Quit
Set Application = Nothing
You haven't specified the full path name.
Try const pptmerge_folder = "full path name here"
The code on line 2.

Getting the cursor location in Visual Studio to clipboard

Often when describing an issue in code, I need to reference it by line/column/function.
Is there a macro/add-in for Visual Studio that copies that information for me?
It would be perfect if it could copy to clipboard: File, Line, column, function name
But I'd take any combination :).
Thanks!
I ended up doing a macro. Unfortunately I was unable to access the clipboard from the macro so I had to use NirCmd for that part. Other than that, it works great!
Public Sub CopyLocation()
Dim fileName = DTE.ActiveDocument.Name
Dim line = ""
Dim column = ""
Dim functionName = ""
Dim className = ""
Dim textDocument = TryCast(DTE.ActiveDocument.Object, TextDocument)
If textDocument IsNot Nothing Then
Dim activePoint = textDocument.Selection.ActivePoint
column = activePoint.DisplayColumn
line = activePoint.Line
Dim codeElement As CodeElement
codeElement = activePoint.CodeElement(vsCMElement.vsCMElementFunction)
If codeElement IsNot Nothing Then
functionName = codeElement.Name
End If
codeElement = activePoint.CodeElement(vsCMElement.vsCMElementClass)
If codeElement IsNot Nothing Then
className = codeElement.Name
End If
End If
Dim output As String = String.Format("File: {0} ", fileName)
If (String.IsNullOrEmpty(line) = False) Then output = output & String.Format("Line: {0} ", line)
If (String.IsNullOrEmpty(column) = False) Then output = output & String.Format("Column: {0} ", column)
If (String.IsNullOrEmpty(className) = False) Then output = output & String.Format("at {0}", className)
If (String.IsNullOrEmpty(functionName) = False) Then output = output & String.Format(".{0}", functionName)
Dim process As System.Diagnostics.Process
process.Start("c:\NoInstall files\nircmd.exe", String.Format("clipboard set ""{0}""", output))
End Sub

Visual Studio Automation: Programatically get a project output directory

I want to programatically get a project output directory in a Visual Studio macro.
I managed to get a string of the path (through prj.ConfigurationManager.ActiveConfiguration.Properties and looking at property OutputDirectory) but this string may contain macros such as $(foo) where foo is defined in a property sheet or whatnot.
How do I resolve this output directory string to the 'real' directory?
I wrote this function for my macros which searches for full absolute output path by substring.
Function FindOutBinaryNameByExtension(ByVal prj As EnvDTE.Project, ByVal extName As String) As String
FindOutBinaryNameByExtension = Nothing
Dim cm As ConfigurationManager = prj.ConfigurationManager
If cm IsNot Nothing Then
Dim ac As Configuration = cm.ActiveConfiguration
For Each grpOut In ac.OutputGroups
If grpOut.DisplayName = "Primary output" Then
Dim lst As Array = grpOut.FileURLs
For i As Long = 0 To lst.Length - 1
Dim fileName As String = lst.GetValue(i)
If fileName.Contains(extName) Then
FindOutBinaryNameByExtension = fileName
Exit Function
End If
Next
End If
Next
End If
End Function

How to select a folder only by using common dialog control

Using VB6
Code.
CommonDialog1.DialogTitle = "Open File"
CommonDialog1.Filter = "*.*"
CommonDialog1.FilterIndex = 1
CommonDialog1.Flags = cdlOFNAllowMultiselect + cdlOFNExplorer
CommonDialog1.Flags = cdlOFNFileMustExist + cdlOFNHideReadOnly
CommonDialog1.CancelError = True
On Error Resume Next
CommonDialog1.ShowOpen
If Err Then
'MsgBox "Select Folder"
Exit Sub
End If
From the above code, i am selecting a file, But i don't want to select a file, I want to select only the folder. How to modify my code.
Need vb6 code Help?
It's been a while since I've had to do any visual basic work but I think instead of using the common dialog box for getting the name of a file to open you should use the SHBrowseForFolder function which is already part of the Windows API. Here's a link to a page that describes it's usage.
Update (2017): Provided link is broken but a backed-up version can be viewed on archive.org
To select a folder, you can use the Shell and Automation Component.
Private shlShell As Shell32.Shell
Private shlFolder As Shell32.Folder
Private Const BIF_RETURNONLYFSDIRS = &H1
Private Sub Command1_Click()
If shlShell Is Nothing Then
Set shlShell = New Shell32.Shell
End If
Set shlFolder = shlShell.BrowseForFolder(Me.hWnd, "Select a Directory", BIF_RETURNONLYFSDIRS)
If Not shlFolder Is Nothing Then
MsgBox shlFolder.Title
End If
End Sub
You will need to add a reference to shell32.dll to your project. Use the Project/References... menu and then browse for shell32.dll.
Or you can use the Windows API as Twotymz suggests.
This is an old thread, but maybe someone will be helped by this.
This code works in VB6 for me:
Private Sub ChooseDir_Click()
Dim sTempDir As String
On Error Resume Next
sTempDir = CurDir 'Remember the current active directory
CommonDialog1.DialogTitle = "Select a directory" 'titlebar
CommonDialog1.InitDir = App.Path 'start dir, might be "C:\" or so also
CommonDialog1.FileName = "Select a Directory" 'Something in filenamebox
CommonDialog1.Flags = cdlOFNNoValidate + cdlOFNHideReadOnly
CommonDialog1.Filter = "Directories|*.~#~" 'set files-filter to show dirs only
CommonDialog1.CancelError = True 'allow escape key/cancel
CommonDialog1.ShowSave 'show the dialog screen
If Err <> 32755 Then ' User didn't chose Cancel.
Me.SDir.Text = CurDir
End If
ChDir sTempDir 'restore path to what it was at entering
End Sub
I though that is more general VBA question anyway, opening select folder dialog in VBA for Office >=2k3.
I could not believe that it is so hard, as I need same functionality. Little googling made it.
Here is nice simple solution take a look
Function GetFolderName()
Dim lCount As Long
GetFolderName = vbNullString
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = OpenAt
.Show
For lCount = 1 To .SelectedItems.Count
GetFolderName = .SelectedItems(lCount)
Next lCount
End With
End Function

Resources