www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Create Windows "shortcut" (.lnk) with D?

reply =?UTF-8?B?5bKp5YCJIOa+qg==?= <mio.iwakura gmail.com> writes:
I'm creating a small installation script in D, but I've been 
having trouble getting shortcut creation to work! I'm a linux 
guy, so I don't know much about Windows programming...

Here are the relevant bits of code I have:

import core.sys.windows.basetyps, core.sys.windows.com, 
core.sys.windows.objbase, core.sys.windows.objidl, 
core.sys.windows.shlobj, core.sys.windows.windef;
import std.conv, std.exception, std.file, std.format, std.path, 
std.stdio, std.string, std.utf, std.zip;

//Couldn't find these in core.sys.windows
extern(C) const GUID CLSID_ShellLink     = {0x00021401, 0x0000, 
0x0000,
   [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46]};

extern(C) const IID IID_IShellLinkA      = {0x000214EE, 0x0000, 
0x0000,
   [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46]};

extern(C) const IID IID_IPersistFile     = {0x0000010B, 0x0000, 
0x0000,
   [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46]};

void main()
{
     string programFolder, dataFolder, desktopFolder;
     {
         char[MAX_PATH] _programFolder, _dataFolder, 
_desktopFolder;
         SHGetFolderPathA(null, CSIDL_PROGRAM_FILESX86, null, 0, 
_programFolder.ptr);
         SHGetFolderPathA(null, CSIDL_LOCAL_APPDATA, null, 0, 
_dataFolder.ptr);
         SHGetFolderPathA(null, CSIDL_DESKTOPDIRECTORY, null, 0, 
_desktopFolder.ptr);
         programFolder = 
_programFolder.assumeUnique().ptr.fromStringz();
         dataFolder = _dataFolder.assumeUnique().ptr.fromStringz();
         desktopFolder = 
_desktopFolder.assumeUnique().ptr.fromStringz();
     }
     auto inputFolder = buildNormalizedPath(dataFolder, 
"Aker/agtoolbox/input");
     auto outputFolder = buildNormalizedPath(dataFolder, 
"Aker/agtoolbox/output");
     CoInitialize(null);
     scope(exit)
         CoUninitialize();
     IShellLinkA* shellLink;
     IPersistFile* linkFile;
     CoCreateInstance(&CLSID_ShellLink, null, 
CLSCTX_INPROC_SERVER, &IID_IShellLinkA, cast(void**)&shellLink);
     auto exePath = buildNormalizedPath(programFolder, 
"Aker/agtoolbox/agtoolbox.exe").toStringz();
     shellLink.SetPath(exePath);
     auto arguments = format("-i %s -o %s", inputFolder, 
outputFolder).toStringz();
     shellLink.SetArguments(arguments);
     auto workingDirectory = programFolder.toStringz();
     shellLink.SetWorkingDirectory(workingDirectory);
     shellLink.QueryInterface(&IID_IPersistFile, 
cast(void**)&linkFile);
     auto linkPath = buildNormalizedPath(desktopFolder, 
"agtoolbox.lnk").toUTF16z();
     linkFile.Save(linkPath, TRUE);
}

I tried sprinkling it with print statements and it crashes on 
shellLink.setPath(exePath);

This is the full script: 
https://gist.github.com/miotatsu/1cc55fe29d8a8dcccab5
I found the values for the missing windows api bits from this: 
http://www.dsource.org/projects/tutorials/wiki/CreateLinkUsingCom

I compile with: dmd agtoolbox-installer.d ole32.lib
I got the ole32.lib from the dmd install

Any help would be highly appreciated as I'm new to Windows 
programming in D and have no idea what I'm doing wrong!
Mar 05 2016
next sibling parent reply BBasile <b2.temp gmx.com> writes:
On Sunday, 6 March 2016 at 03:13:23 UTC, 岩倉 澪 wrote:
 I'm creating a small installation script in D, but I've been 
 having trouble getting shortcut creation to work! I'm a linux 
 guy, so I don't know much about Windows programming...

 [...]

 Any help would be highly appreciated as I'm new to Windows 
 programming in D and have no idea what I'm doing wrong!
If you don't want to mess with the Windows API then you can dynamically create a script (I do this in CE installer): void createLnk(string exeName, string displayName) { import std.process: environment, executeShell; import std.file: remove, exists; import std.random: uniform; import std.conv: to; string vbsName; do vbsName = environment.get("TEMP") ~ r"\shcsh" ~ uniform(0,int.max).to!string ~ ".vbs"; while (vbsName.exists); string vbsCode = " set WshShell = CreateObject(\"WScript.shell\") strDesktop = WshShell.SpecialFolders(\"Desktop\") set lnk = WshShell.CreateShortcut(strDesktop + \"\\%s.lnk\") lnk.TargetPath = \"%s\" lnk.Save "; File vbs = File(vbsName, "w"); vbs.writefln(vbsCode, displayName, exeName); vbs.close; executeShell(vbsName); vbsName.remove; }
Mar 05 2016
parent =?UTF-8?B?5bKp5YCJIOa+qg==?= <mio.iwakura gmail.com> writes:
On Sunday, 6 March 2016 at 05:00:55 UTC, BBasile wrote:
 If you don't want to mess with the Windows API then you can 
 dynamically create a script (I do this in CE installer):
This might be an option but I'd prefer to use the Windows API directly. I don't know vb script and maintaining such a script inline would just add cognitive overhead I think. If I can't get it working with the Windows API; I'll probably have to do it this way though. Thanks for the suggestion!
Mar 05 2016
prev sibling parent reply John <johnch_atms hotmail.com> writes:
On Sunday, 6 March 2016 at 03:13:23 UTC, 岩倉 澪 wrote:
     IShellLinkA* shellLink;
     IPersistFile* linkFile;

 Any help would be highly appreciated as I'm new to Windows 
 programming in D and have no idea what I'm doing wrong!
In D, interfaces are references, so it should be: IShellLinkA shellLink; IPersistFile linkFile;
Mar 06 2016
parent reply =?UTF-8?B?5bKp5YCJIOa+qg==?= <mio.iwakura gmail.com> writes:
On Sunday, 6 March 2016 at 11:00:35 UTC, John wrote:
 On Sunday, 6 March 2016 at 03:13:23 UTC, 岩倉 澪 wrote:
     IShellLinkA* shellLink;
     IPersistFile* linkFile;

 Any help would be highly appreciated as I'm new to Windows 
 programming in D and have no idea what I'm doing wrong!
In D, interfaces are references, so it should be: IShellLinkA shellLink; IPersistFile linkFile;
That's exactly what the problem was, thank you!!
Mar 06 2016
parent Andrea Fontana <nospam example.com> writes:
On Sunday, 6 March 2016 at 20:13:39 UTC, 岩倉 澪 wrote:
 On Sunday, 6 March 2016 at 11:00:35 UTC, John wrote:
 On Sunday, 6 March 2016 at 03:13:23 UTC, 岩倉 澪 wrote:
     IShellLinkA* shellLink;
     IPersistFile* linkFile;

 Any help would be highly appreciated as I'm new to Windows 
 programming in D and have no idea what I'm doing wrong!
In D, interfaces are references, so it should be: IShellLinkA shellLink; IPersistFile linkFile;
That's exactly what the problem was, thank you!!
You can use official specs to recreate binary file: https://msdn.microsoft.com/en-us/library/dd871305.aspx Ok, not the fastest way. :)
Mar 07 2016