I am working on a flutter project that runs fine in development.
However I do not know how to get the build to include dll's referenced using FFI.
I can't find clear instructions on how to do it.
I tried following the steps to build an msix here, which worked but didn't seem to include the dll (it fails in the same way as the regular build)
what is the procedure to have the build process consider the dll's?
other dll's show up in the build directory from 3rd party packages so there must be a way right?
That's really hard to discover by your own, but indeed you can bind those libraries to you MSIX. In my case I just made a package for label printers using Dart FFI and DLL`s provided by manufacturer and this is how I did it.
You need to add these DLL's to your assets setting on pubspec.yaml from your package. This is my case:
[...]
flutter:
[...]
assets:
- assets/WinPort.dll
- assets/Winppla.dll
- assets/Winpplb.dll
- assets/Winpplz.dll
With this setting you will embed your DLL files in your final MSIX, but this was the easy part. Now you have make sure to load the proper load these files in code.
Based on my own tests, I still dealing with two ways to develop and test code, the first one is when I am running a project in my machine via flutter run I must set the target for current.path, when I get it done and start building for deploy I change this to resolvedExecutable.parent.path. Where is what you need to do.
Loading you DLL in development environment (flutter run):
final String _packageAssetsDirPath = normalize(join(Directory.current.path,'assets'));
On production environment (running from .exe or MSIX installed):
final String _assetsPackageDir = normalize(
join('data', 'flutter_assets', 'packages', 'YOUR_PACKAGE_NAME', 'assets'));
final String _exeDirPath = File(Platform.resolvedExecutable).parent.path;
final String _packageAssetsDirPath =
normalize(join(_exeDirPath, _assetsPackageDir));
After with this var called _packageAssetsDirPath will be easy to load your DLL's, now you invoke a DynamicLibrary constructor:
// Path for DLL file
final String _libDllSourceFullPath =
normalize(join(_packageAssetsDirPath, 'Winppla.dll'));
// Target for copy, place DLL in same place the .exe you are running
final String _libDllDestFullPath =
normalize(join(_packageAssetsDirPath, 'YOUROWN.dll'));
// Try to copy for running exe path
File(_libDllSourceFullPath).copySync(_libDllDestFullPath);
// With this copy, would be simple to load, and if it fails, try in full path
// LOAD DLL
try {
String _packageAssetsDirPath =
normalize(join(Directory.current.path, 'assets'));
String _printerLibraryPath =
normalize(join(_packageAssetsDirPath, 'Winppla.dll'));
DynamicLibrary _library = DynamicLibrary.open(_printerLibraryPath);
return _library;
} catch (e) {
try {
DynamicLibrary _library = DynamicLibrary.open('Winppla.dll');
return _library;
} catch (e) {
// Avoing errors creating a fake DLL, but you could deal with an exception
return DynamicLibrary.process();
}
}
At this point you can load a DLL and use it, you can check my package full code at https://github.com/saviobatista/argox_printer check for lib/src/ppla.dart at function _setupDll() and you will see that loading.
I built a simpler option inspired in the solution of Sávio Batista
(You must have your .dll in your assets folder)
if (kReleaseMode) {
// I'm on release mode, absolute linking
final String local_lib = join('data', 'flutter_assets', 'assets', 'libturbojpeg.dll');
String pathToLib = join(Directory(Platform.resolvedExecutable).parent.path, local_lib);
DynamicLibrary lib = DynamicLibrary.open(pathToLib);
} else {
// I'm on debug mode, local linking
var path = Directory.current.path;
DynamicLibrary lib = DynamicLibrary.open('$path/assets/libturbojpeg.dll');
}
Just replace libturbojpeg.dll for your .dll
Related
The keyword here is Web. I can get the build and version number of a Flutter app by using the package_info plugin, but if it is running on the web I can't. How do I get the package info for a Flutter Web app?
I'll include an answer below as a temporary fix, but I am looking a solution that gets the version info from pubspec.yaml.
As a temporary workaround you can create a separate file with the version info in it:
web_version_info.dart
class WebVersionInfo {
static const String name = '1.0.0';
static const int build = 1;
}
You can use that for all platforms or in your code you can use kIsWeb to just use it for the web:
Future<String> _getAppVersion() async {
if (kIsWeb) {
return WebVersionInfo.name;
} else {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
}
Of course, this is not a great solution because now you need to remember to update the version and build information in both pubspec.yaml and in WebVersionInfo every time you update the app.
If you use beta channel you can use package_info_plus plugin that appears to be a drop-in-replacement for package_info. So all you need to change is pubspec.yaml and your import. (I only use version so there could be differences that I haven't noticed)
Change pubspec and your import
pubspec.yaml: package_info_plus: '>=0.6.3 <2.0.0'
import: import 'package:package_info_plus/package_info_plus.dart'
Reference:
Github issue 46609
For those using Linux and in order to improve Suragch's answer, I suggest automating the build process using bash scripts. For that, we need two scripts: one to increase the version build number and another to call the flutter build command itself, forwarding the parameters. That way, if you prefer to just increment the version build number manually, you can just call the update script and then 'flutter build' later, but if you want to do everything in one step, you can call the builder script.
You will only need to edit the '.app_version' file as the version changes.
The '.build_seq', '.version_number' files are always rewritten, and the '.app_version' file is created only if it is not found.
The scripts:
updversion.sh:
#!/bin/bash
if [ -f ".app_version" ]; then
VER=`cat .app_version`
else
VER="1.0.0"
echo $VER > .app_version
fi
if [ -f ".build_seq" ]; then
BLD=`cat .build_seq`
else
BLD='0'
fi
((BLD++))
echo $BLD > .build_seq
echo "Ver: $VER ($BLD)" > .current_version
echo "
// Auto-generated by updversion.sh. Do not edit.
class WebVersionInfo {
static const String name = '$VER';
static const int build = $BLD;
}
" > lib/version_info.dart
exit 0
buildweb.sh:
#!/bin/bash
./updversion.sh
flutter build web $*
exit $?
I would like to ask if anyone create Assetbundle in Unity on cloud? I would like to generate the AssetBundle dynamically on cloud and the client app will download it accordingly.
Could you let me know your idea? Is there any cloud service for hosting Unity ?
The accepted answer is spot on but there are slight changes as Unity5.6 now supports every other feature in the free version too. I've been working on a similar project that required building asset bundles dynamically. I'll post my code snippet for the same so that the process of identifying this gets simpler for everyone else in future.
But before that, there are some limitations for this process that you may need to consider. Building asset bundles dynamically on cloud requires (Command line) batchmode which runs Unity on commandline (Unity should be installed to build bundles). This asset building process works only on Windows and OSX (No Linux). The command to invoke Unity in batch mode is given below and has to be executed from Unity executable's location,
this command creates an empty project,
Unity -batchmode -quit -createProject <path/to/create a project>
After creating a project, you can save a script to build assets in the Assets/Editor folder, I have a written a script to automate the process of building assetbundle for all assets in the Assets/Models folder.
//BuildAssets.cs
using System.Collections;
using System.Collections.Generic;
public class BuildAssets : UnityEngine.MonoBehaviour
{
static void BuildAssetBundle()
{
int i = 0;
string log = "log.txt";
string[] assetN;
int N_Files;
UnityEditor.AssetBundleBuild[] AssetMap = new UnityEditor.AssetBundleBuild[2];
AssetMap[0].assetBundleName = "res";
// Adding to path /Models
string path = UnityEngine.Application.dataPath + "/Models";
//log
System.IO.File.AppendAllText(log, System.DateTime.Now.ToString() + "\n\n");
System.IO.File.AppendAllText(log, path + "\n");
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(path);
System.IO.FileInfo[] files = dir.GetFiles();
// Number of files in "/Models" folder
N_Files = files.Length;
//log
System.IO.File.AppendAllText(log, "Num assets: "+N_Files + " \n");
assetN = new string[N_Files];
foreach (System.IO.FileInfo file in files)
{
if (file.Exists)
{
if (!file.Extension.Equals(".meta"))
{
assetN[i] = "Assets/Resources/" + file.Name;
System.IO.File.AppendAllText(log, assetN[i] + " \n");
i += 1;
}
}
}
AssetMap[0].assetNames = assetN;
UnityEditor.BuildPipeline.BuildAssetBundles("Assets/AssetBundles", AssetMap, UnityEditor.BuildAssetBundleOptions.None, UnityEditor.BuildTarget.Android);
System.IO.File.AppendAllText(log, "\t----X----\n"); //log
}
}
This is the command for building the asset bundle through command line.
Unity -batchmode -quit -projectPath path/to/UnityProjects/Projectname -executeMethod BuildAssets.BuildAssetBundle -logFile <Log file location>
I've tested this and it works for our project.
AssetBundles require unity pro to build. There is a command line batch mode that you can use to build your asset bundles automaticly and host it on virtualy any host (a simple HTTP get).
Remember that you might not need asset bundles (or unity pro) - you can easily download textures and audio from the web using the WWW class. For textures you can use png, jpeg and tiff, for audio wav, ogg (only desktop and webplayer), mp3 (only mobile). Mesh loading should be also possible but that will require additional tools.
How to create new file in a user directory on NetBeans Platform application? I used:
System.getProperty("netbeans.user", "user.home") + "/myfile");
But the NB IDE 7.1.1 told me that it is depreceated and I should use InstalledFile Locator instead. Ok, I tried this:
File file = InstalledFileLocator.getDefault().locate("myfile", null, false);
It works fine, if the file already exists. I cannot see any way, how to create new with the InstalledFileLocator. But the javadoc say, this method allows to get folder. So I tried this:
File file = InstalledFileLocator.getDefault().locate("myfile", null, false);
if (file == null) {
file = new File(InstalledFileLocator.getDefault().locate("", null, false), "myfile");
}
Again without success, the method locate now fails that it can't find anything (the "/" is forbidden and does not work too).
So my question is, how to corectly load in my NetBeans Platform application an existing file in the user directory (it is for writing also, so it should not be in the program directory) and if it does not exist, create it?
You could use Places.getUserDirectory().
File file = InstalledFileLocator.getDefault().locate("myfile", null, false);
if (file == null)
{
file = new File(Places.getUserDirectory() + File. separator + "myfile");
}
From the netbeans platform docs InstalledFileLocator should not be used to find resources on the system filesystem. To find data in the system filesystem, use the Filesystems API. Ex:
FileObject fo = FileUtil.getConfigFile(myfile);
if (fo == null) {
fo = FileUtil.getConfigRoot().createData(myFile,ext);
}
Probably the easiest thing you can do is to include a simple empty file (say "here.txt") in your module that will be installed in the user directory automatically. You can see an example of this here (see the section "Lessons learned: bundling files with your NetBeans modules").
Basically you include the file in the "release/modules/ext/here.txt" directory of your module.
When the module is installed the platform will install the 'here.txt' file included in your module in the user directory automatically for you, so you don't have to worry about this.
Once your module is installed an running you want to locate the file like this:
File hereTXT = InstalledFileLocator.getDefault()
.locate("modules/ext/here.txt",
"a.b.c",
false);
(Where "a.b.c" is your module identifier.)
And then from that 'hereTXT' file you can get the directory with 'hereTXT.getParent()', and you're all set.
I'm using this code to return a FileContentResult with an MSI file for the user to download in my ASP.NET MVC controller:
using (StreamReader reader = new StreamReader(#"c:\WixTest.msi"))
{
Byte[] bytes = Encoding.ASCII.GetBytes(reader.ReadToEnd());
return File(bytes, "text/plain", "download.msi");
}
I can download the file, but when I try to run the installer I get an error message saying:
This installation package could not be
opened. Contact the application vendor
to verify that this is a valid Windows
Installer package.
I know the problem isn't C:\WixTest.msi, because it runs just fine if I use the local copy. I don't think I'm using the wrong MIME type, because I can get something similar with just using File.Copy and returning the copied file via a FilePathResult (without using a StreamReader) that does run properly after download.
I need to use the FileContentResult, however, so that I can delete the copy of the file that I'm making (which I can do once I've loaded it into memory).
I'm thinking I'm invalidating the install package by copying or encoding the file. Is there a way to read an MSI file into memory, and to return it via a FileContentResult without corrupting the install package?
Solution:
using (FileStream stream = new FileStream(#"c:\WixTest.msi", FileMode.Open))
{
BinaryReader reader = new BinaryReader(stream);
Byte[] bytes = reader.ReadBytes(Convert.ToInt32(stream.Length));
return File(bytes, "application/msi", "download.msi");
}
Try using binary encoding and content-type application/msi instead of text/plain - it's not ASCII or text content so you're mangling the file.
I want to deploy a firebird installation, and thus will launch it from my installer using command-line parameters. I read Inno Setup's documentation but still can't get it to work.
I just want to install a "Super server" with no documentation or whatsoever.
Here's what I have so far
Firebird-2.1.2.18118_0_Win32.exe /sp- /silent /SUPPRESSMSGBOXES /nocancel /noicons /components="Super Server binary"
But it won't install the server. If I remove the /components it does install the server but install other developer stuff, which customers don't need.
read installation_scripted.txt in C:\Program Files\Firebird\Firebird_2_1\doc
/COMPONENTS="comma separated list of
component names"
Choose from -
ServerComponent\SuperServerComponent,
ServerComponent\ClassicServerComponent,
ServerComponent,
DevAdminComponent and
ClientComponent
Overrides the default components
settings. Using this command line
parameter causes Setup to
automatically select a custom type. A
full install requires combining
components. For example:
/COMPONENTS="ServerComponent\SuperServerComponent,ServerComponent,DevAdminComponent,ClientComponent"
would be required for a full
install.
I use the following and it works fine, however I need to install to a custom directory and also change the server option
string installerFilePath = #"C:\BennaOlivier\Randoms\Delter\Firebird\FirebirdMainInstaller\MainInstaller\MainInstaller\Firebird X64\FirebirdInstallX64\Firebird-2.5x64.exe";
Process installerProcess = new Process();
installerProcess = Process.Start(installerFilePath, Arguments);
while (installerProcess.HasExited == false)
{
//indicate progress to user
Application.DoEvents();
System.Threading.Thread.Sleep(250);
}
}
catch (Exception FBX64)
{
MessageBox.Show(FBX64.Message);
throw;
}public const string comps = #"ServerComponent\ClassicServerComponent,ServerComponent,ClientComponent";
public const string Arguments = "/VERYSILENT /SUPPRESSMSGBOXES";