How to add content file to Wix installer - c#

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MyApp" Language="1033" Version="1.0.0.0" Manufacturer="MyAppDev" UpgradeCode="067ac37f-0d36-4173-a24a-5037927bd6da">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="MyApp" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MyApp" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
<Component>
<File Source="$(var.MyApp.TargetPath)" />
</Component>
<Component>
<File Id="Postcodes.txt" Source="C:\Project\MyApp\MyApp\Files\Postcodes.txt" KeyPath="yes" />
</Component>
</ComponentGroup>
<Feature Id="MainApplication" Title="Main Application" Level="1">
<ComponentRef Id="Postcodes.txt" />
</Feature>
</Fragment>
</Wix>
My Windows form application using a text file which is stored in application directory. And with the help of WIX toolset. I have created an installer But the problem is content text file not exist. That's why i am getting File not found exception.
Please help me, How can i add this content text file to WIX installer? Or what would i need to add "Product.wxs" file?

You need to add a custom action. This is a direct copy of my work minus the company name that it was produced for.
<CustomAction Id="RestAction" BinaryKey="myCompanyAgentSetup.WixExtension.Package.dll" DllEntry="Execute" Execute="immediate" Return="check" />
This is a class file \ dll that is created in the Wix project. This is my actual dll \ class file
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Deployment.WindowsInstaller;
namespace myCompanyAgentSetup.WixExtension
{
public static class myCompanyAgentSetupWixExtension
{
[CustomAction]
public static ActionResult Execute(Session session)
{
var errorMsg = string.Empty;
var record = new Record();
var token = Environment.GetEnvironmentVariable("RX_JOB_NO");
var restUser = session["RESTUSER"];
var restPass = session["RESTPASS"];
var restUrl = string.Format(session["RESTURL"], token);
var request = (HttpWebRequest)WebRequest.Create(restUrl);
var encoded = Convert.ToBase64String(Encoding.Default.GetBytes(restUser + ":" + restPass));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + encoded);
request.Credentials = new NetworkCredential(restUser, restPass);
Console.WriteLine("attempting to get API Key");
try
{
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode.ToString() != "OK")
{
record = new Record
{
FormatString = string.Format(response.StatusDescription)
};
session.Message(InstallMessage.Error, record);
Console.WriteLine("Unable to get API Key");
Console.WriteLine("Adding RX_CLOUDBACKUP_API Environment Variable with no value");
UpdateConfigFiles("");
}
else
{
var apiKey = new StreamReader(response.GetResponseStream()).ReadToEnd();
if (apiKey.Contains("Error"))
{
record = new Record
{
FormatString = string.Format(apiKey)
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
}
Console.WriteLine("Adding RX_CLOUDBACKUP_API with value - " + apiKey);
UpdateConfigFiles(apiKey);
return ActionResult.Success;
}
}
catch (Exception e)
{
record = new Record
{
FormatString = string.Format(e.Message)
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
}
//An error has occurred, set the exception property and return failure.
session.Log(errorMsg);
session["CA_ERRORMESSAGE"] = errorMsg;
record = new Record
{
FormatString = string.Format("Something has gone wrong!")
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
return ActionResult.Failure;
}
private static void UpdateConfigFiles(string apiKey)
{
if (!string.IsNullOrEmpty(apiKey))
{
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", null, EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", apiKey, EnvironmentVariableTarget.Machine);
}
else
{
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", "", EnvironmentVariableTarget.Machine);
}
}
}
}
Hopefully this will get you going. If you need anything else let me know

Related

Unable to get NLog to send fields as numbers/ints/longs from code

Our tack is c# .net 4.7 logging through NLog library to a "Graylog" GELF input which of course is storing them in an elasticsearch cluster.
The application utilizes logging heavily, but EVERYTHING is sent through as strings - no matter if it was something other than a string in code.
I want to be able send my custom logger properties as their true data types so that I can aggregate on my statistical number fields.
I DO NOT want to constrain my developers and do this with scripted fields, or pre-mapping an index in elasticsearch - in fact, graylog manages my index for me. Nor do I want to utilize graylog pipeline processor - my poor graylog does enough work. I want them to be able to send stat data as their real data types and have them mapped as "long" or "number".
A code snippet from my stuff:
public void InfoExtended2(String Message, Dictionary<string, int> extrafields, [CallerMemberName] string callerMethodName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
var fileName = callerFilePath.Substring(callerFilePath.LastIndexOf(#"\") + 1).Replace(".cs", "");
var caller = $"{fileName}.{callerMethodName}()Line:{sourceLineNumber}";
var logger = LogManager.GetLogger(caller);
if (!(extrafields.Count == 0))
{
foreach (var field in extrafields)
{
string mykey = field.Key.ToString();
extrafields.TryGetValue(field.Key, out int myvalue);
//be careful whats in object!
if (!logger.Properties.ContainsKey(mykey)) { logger.Properties.Add(mykey, "{#myvalue}"); } else { logger.Properties[mykey] = "{#myvalue}"; }
}
}
logger.Info()
.Message(Message)
.Property("ComeOnNow3", 87)
.Write();
// Create the LogEventInfo object
//LogEventInfo logEvent = new LogEventInfo();
// Now add the event characteristics
//logEvent.Properties["EventCode"] = 2222;
//logEvent.Level = NLog.LogLevel.Info;
//logEvent.Properties["EventCode]."]
//logEvent.Message = "My Message";
////logEvent.Exception = ex;
//logger.Log(logEvent);
//logger.Info("{#Message}", new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } }); // dict. Result: Test "key1"=1, "key2"=2
}
I've tried a few different ways there with no luck.
If I make a POST GELF call myself with JSON fields representing ints instead of strings, it works. My field is brand new to the index and when I go check the mapping it comes back as a long. I want my code to do that.
Test GELF Call:
{
"version": "1.1",
"host": "example.org",
"short_message": "A short message that helps you identify what is going on",
"full_message": "Backtrace here\n\nmore stuff",
"timestamp": 1385053862.3072,
"level": 1,
"_user_id": 9001,
"_some_info": "foo",
"_some_env_var": "bar"
}
Resulting mapping for user id:
"user_id" : {
"type" : "long"
What our nlog target currently looks like:
<targets>
<target name="gelftcp"
type="gelftcp"
facility="Custom Facility"
remoteaddress="192.168.95.15"
remoteport="12201"
layout="${longdate}${newline}type: ${level:uppercase=true}${newline}class: ${logger}${newline}stacktrace: ${stacktrace}${newline}error message: ${message}${newline}exception: ${exception:format=tostring,data:maxinnerexceptionlevel=10}">
<parameter name="logged_at" layout="${longdate}" />
<parameter name="type" layout="${level:uppercase=true}" />
   <parameter name="class" layout="${logger}" />
<parameter name="CustomInt" layout="${event-properties:item=CustomInt}" as="number" />
   <parameter name="stacktrace" layout="${stacktrace}" />
   <parameter name="error_message" layout="${message}" />
<parameter name="ComeOnNow3" layout="${event-properties:item=ComeOnNow3} " />
   <parameter name="exception" layout="${exception:format=tostring,data:maxinnerexceptionlevel=10}" />
<attribute name="eventProperties" encode="false" >
<layout type='JsonLayout' includeAllProperties="true" maxRecursionLimit="2"/>
</attribute>
<variable name="ComeOnNow2" value ="${event-context:ComeOnNow2}" />
</target>
Take it easy on me, I'm not the original author of this code. Just someone tasked with picking up slack & deploying in its current state.
UPDATE:
Tried the first suggestion. Even added the field I'm testing:
<field name="ComeOnNow3" layout="${threadid}" type="System.Int32" />
Attempted this log entry:
logger.Info()
.Message(Message)
.Property("ComeOnNow3", 87)
.Write();
My mapping still comes back as "keyword".
SOLVED:
The trick was to switch to Http instead of Tcp.
Instead of using "gelftcp". Then you can try out GelfLayout instead:
https://www.nuget.org/packages/NLog.GelfLayout
You can combine it with the TCP network-target:
<nlog>
<extensions>
<add assembly="NLog.Layouts.GelfLayout" />
</extensions>
<targets async="true">
<target type="Network" name="gelftcp" address="tcp://192.168.95.15:12201" newLine="true" lineEnding="Null">
<layout type="GelfLayout" facility="MyFacility">
<field name="threadid" layout="${threadid}" type="System.Int32" />
</layout>
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="gelftcp" />
</rules>
</nlog>
It also has some automatic conversion logic for simple NLog LogEventInfo.Properties (Decimal + Double + Integer + Boolean)
More examples can be found here: https://github.com/farzadpanahi/NLog.GelfLayout

Showing PDF-File with MVVM Cross on Android and IOS

I want to open a PDF on the Phone via the File-Path but i cant figure out how i could do this properly without using 3rd party packages.
You have any suggestion for this?
I already tried to use this on Android:
public void OpenFile(string filePath)
{
var fileToOpen = new Java.IO.File(filePath);
var uri = FileProvider.GetUriForFile(Application.Context, Application.Context.PackageName + ".fileprovider", fileToOpen);
var intent = new Intent();
var mime = IOUtil.GetMimeType(uri.ToString());
intent.SetAction(Intent.ActionView);
intent.SetDataAndType(uri, mime);
intent.SetFlags(ActivityFlags.NewTask);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
Application.Context.StartActivity(intent);
}
But i get the following Error:
Unhandled Exception:
Java.Lang.NullPointerException: Attempt to invoke virtual method
'android.content.res.XmlResourceParser
android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager,
java.lang.String)' on a null object reference
first you should addd this code to your manifest file :
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.easyphotopicker.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths"
tools:replace="android:resource"/>
</provider>
and create filepaths :
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" /> //root directory of the device new File("/");
<files-path name="files" path="" /> //context.getFilesDir()
<cache-path name="cache" path="" /> //context.getCacheDir()
<external-path name="external" path="" /> //Environment.getExternalStorageDirectory()
<external-files-path name="name" path="path" /> //context.getExternalFilesDirs()
<external-cache-path name="name" path="path" /> //getExternalCacheDirs()
</paths>
Your error is telling us that there is no file at the location matching that's passed into the function. There's a few ways of doing this, one of them is as shown. After accepting permissions to access folders and files, this should be one of the simplest ways. You seem to be close:
public void OpenPdfFile(string filename)
{
var f = new Java.IO.File(filename);
if (f.Exists())
{
System.Diagnostics.Debug.WriteLine("File exists!");
try
{
var openFileIntent = new Intent(Intent.ActionView);
openFileIntent.SetDataAndType(Android.Net.Uri.FromFile(f), "application/pdf");
openFileIntent.SetFlags(ActivityFlags.NoHistory);
StartActivity(Intent.CreateChooser(openFileIntent, "Open pdf file"));
}
catch (ActivityNotFoundException)
{
//handle when no available apps
}
}
}
I haven't tested your work, but the first thing would be to see if you added this to the Manifest file
android:authorities="com.{package}.{name}.fileprovider"
since your code says Application.Context.PackageName + ".fileprovider"

iText7 unable to setup logging

I'm trying to use PdfCleanUpTool with iText7.
However my final PDF is corrupted (it is only 15B in size).
When I start my console app from VS I get this in Output:
no configuration section found - suppressing logging output
I'm trying to setup logging to get error message, but without luck.
I've installed this packages:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Common.Logging" version="3.4.1" targetFramework="net47" />
<package id="Common.Logging.Core" version="3.4.1" targetFramework="net47" />
<package id="Common.Logging.NLog4412" version="3.4.1" targetFramework="net47" />
<package id="itext7" version="7.1.2" targetFramework="net47" />
<package id="itext7.pdfsweep" version="2.0.1" targetFramework="net47" />
<package id="Microsoft.CSharp" version="4.0.1" targetFramework="net47" />
<package id="NLog" version="4.4.12" targetFramework="net47" />
<package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="net47" />
</packages>
and this is my app.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog4412">
<arg key="configType" value="INLINE" />
</factoryAdapter>
</logging>
</common>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="console" xsi:type="Console" layout="${date:format=HH\:MM\:ss} ${logger} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="console" />
</rules>
</nlog>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" />
</startup>
</configuration>
Probably I need to setup a license key, but I'd like to get error message saying that I must so.
My question is:How should I correctly setup NLog with Common.Logging to get errors from iText7.
Here is full example that can be used to verify current behavior:
using Common.Logging;
using Common.Logging.Configuration;
using Common.Logging.Simple;
using iText.Kernel.Colors;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.PdfCleanup;
using System;
using System.Collections.Generic;
using System.IO;
namespace RedactTest
{
class Program
{
static void Main(string[] args)
{
NameValueCollection properties = new NameValueCollection
{
["showDateTime"] = "true",
["level"] = "All"
};
LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(properties);
using (Stream inputStream = new FileStream("D:\\test.pdf", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
PdfReader reader = new PdfReader(inputStream);
using (Stream outputStream = new FileStream("D:\\test_redact.pdf", FileMode.Create))
{
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(reader, writer);
List<PdfCleanUpLocation> cleanUpLocations = new List<PdfCleanUpLocation>
{
new PdfCleanUpLocation(1, new Rectangle(40f, 650f, 200f, 700f),ColorConstants.GRAY),
new PdfCleanUpLocation(1, new Rectangle(40f, 550f, 200f, 590f),ColorConstants.GRAY),
new PdfCleanUpLocation(1, new Rectangle(344f, 650f, 550f, 724f),ColorConstants.GRAY)
};
PdfCleanUpTool cleaner = new PdfCleanUpTool(pdfDocument, cleanUpLocations);
cleaner.CleanUp();
}
}
Console.Write("OK");
Console.ReadLine();
}
}
}
On the original logging related question
One option is to activate the console logger from code, at the start of your program put:
// create properties
NameValueCollection properties = new NameValueCollection();
properties["showDateTime"] = "true";
properties["level"] = "All";
// set Adapter
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter(properties);
E.g. for the following test
[Test]
public void CreateLogOutput()
{
// create properties
NameValueCollection properties = new NameValueCollection();
properties["showDateTime"] = "true";
properties["level"] = "All";
// set Adapter
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter(properties);
ILog logger = LogManager.GetLogger(typeof(iText.Kernel.Pdf.PdfReader));
logger.Error("Testing an error log output", new Exception("The exception message"));
}
one gets an output like this:
27.06.2018 17:07:37 [ERROR] iText.Kernel.Pdf.PdfReader - Testing an error log output
=======================================================(inner most exception)===
(1) System.Exception
================================================================================
Method : <unavailable>
Type : <unavailable>
Assembly : <unavailable>
Assembly Path : <unavailable>
Source :
Thread : 15 'NonParallelWorker'
Helplink :
Message:
"The exception message"
Stack Trace:
================================================================================
On the actual issue
After you added the code to the question, the issue became clear: You forgot to close the PdfDocument pdfDocument. Thus, everything works alright, nothing is logged, merely the changes are not written to file as the PDF document object is not closed. Simply close it after the cleaner.CleanUp() call:
PdfCleanUpTool cleaner = new PdfCleanUpTool(pdfDocument, cleanUpLocations);
cleaner.CleanUp();
pdfDocument.Close();
The result now is some 34KB in size and displays like this:

Running the program in background Wix

I have written a simple Installer that gets the value of property when I gave using following command:
msiexec /i file.msi /l*v output.txt IPADDRESS="192.168.2.1"
I extracted the value of IPADDRESS in C# Custom action and created two folder output and config. In config, I write the content of IPADDRESS and output is for logging. Here's my C# code:
namespace SetupCA
{
public class CustomActions
{
[CustomAction]
public static ActionResult WriteFileToDisk(Session session)
{
session.Log("Begin WriteFileToDisk");
string ipAddress = session["IPADDRESS"];
string path = session["LocalAppDataFolder"]; //With trailing slash
path = path.Replace(#"\", #"\\").ToString();
string log_path = path + #"lpa\\output\\";
string config_path = path + #"lpa\\config\\";
session.Log("Local App Data Modified Path is: " + path.ToString());
session.Log("Logging Folder Path is: " + log_path.ToString());
string temp = #"
{{
""logpoint_ip"" : ""{0}""
}}";
string config = string.Format(temp, ipAddress);
session.Log("Config Generated from property is: " + config);
System.IO.Directory.CreateDirectory(config_path);
try
{
System.IO.File.Delete(path + "lpa.config");
}
catch (Exception e)
{
session.Log(e.ToString());
}
System.IO.File.WriteAllText(config_path + "lpa.config", config);
session.Log("Confile file is written");
System.IO.Directory.CreateDirectory(log_path);
session.Log("Logging Folder is Created");
return ActionResult.Success;
}
}
}
Now I have created a Visual C++ application that checks if the program has been registered during startup or not. If not, it adds the exe file in Registry and enters the Infinite loop. If I run the installed exe, it appears in command window. What I want is to run the exe in background and user could view the exe inside Process in Task Manager. I don't want to disturb user by showing a blank terminal window that seems to do nothing. Can this be done in Wix or should I change my code?
I have attached my C++ code and Wix File.
CODE
#include <iostream>
#include <Windows.h>
#include <ShlObj.h>
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/layout.h>
#include <log4cplus/ndc.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/loggingmacros.h>
#include <boost\lexical_cast.hpp>
#include <boost\algorithm\string\replace.hpp>
using namespace log4cplus;
Logger root;
std::string GetLocalAppDataPath();
void LoggingInit();
void LoggingInit()
{
log4cplus::initialize ();
helpers::LogLog::getLogLog()->setInternalDebugging(false);
std::string local_path = GetLocalAppDataPath();
local_path = local_path + "\\lpa\\output\\";
SharedAppenderPtr append_1(new RollingFileAppender(LOG4CPLUS_TEXT( local_path + "outputgen.log"), 10*1024*1024, 5));
append_1->setName(LOG4CPLUS_TEXT("LogpointAgentLog"));
PatternLayout *p = new PatternLayout(LOG4CPLUS_TEXT("[%D] <%-5p> [%F : %L] %m%n"));
append_1->setLayout(std::auto_ptr<Layout>(p));
Logger::getRoot().addAppender(append_1);
root = Logger::getRoot();
}
std::string GetLocalAppDataPath()
{
HANDLE hfile;
TCHAR szPath[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(NULL,CSIDL_LOCAL_APPDATA,NULL,0, szPath)))
{
std::string path = boost::lexical_cast<std::string>(szPath);
boost::replace_all(path, "\\", "\\\\");
return path;
}
}
BOOL IsMyProgramRegisteredForStartup(PCWSTR pszAppName)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwRegType = REG_SZ;
wchar_t szPathToExe[MAX_PATH] = {};
DWORD dwSize = sizeof(szPathToExe);
lResult = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_READ, &hKey);
fSuccess = (lResult == 0);
if (fSuccess)
{
lResult = RegGetValueW(hKey, NULL, pszAppName, RRF_RT_REG_SZ, &dwRegType, szPathToExe, &dwSize);
fSuccess = (lResult == 0);
}
if (fSuccess)
{
fSuccess = (wcslen(szPathToExe) > 0) ? TRUE : FALSE;
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
BOOL RegisterMyProgramForStartup(PCWSTR pszAppName, PCWSTR pathToExe)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwSize;
const size_t count = MAX_PATH*2;
wchar_t szValue[count] = {};
wcscpy_s(szValue, count, L"\"");
wcscat_s(szValue, count, pathToExe);
wcscat_s(szValue, count, L"\" ");
lResult = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, 0, (KEY_WRITE | KEY_READ), NULL, &hKey, NULL);
fSuccess = (lResult == 0);
if (fSuccess)
{
dwSize = (wcslen(szValue)+1)*2;
lResult = RegSetValueExW(hKey, pszAppName, 0, REG_SZ, (BYTE*)szValue, dwSize);
fSuccess = (lResult == 0);
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
int main()
{
//std::string loc = GetLocalAppDataPath(); //Without trailing slashes
LoggingInit();
if(IsMyProgramRegisteredForStartup(L"My_Program"))
{
//do nothing
}
else
{
LOG4CPLUS_INFO(root, "Starting Starup App");
wchar_t szPathToExe[MAX_PATH];
GetModuleFileNameW(NULL, szPathToExe, MAX_PATH);
RegisterMyProgramForStartup(L"My_Program", szPathToExe);
LOG4CPLUS_INFO(root, "Ending Starup App");
}
LOG4CPLUS_INFO(root, "BEFORE INFINITE LOOP #######################");
while(1)
{
LOG4CPLUS_INFO(root, "INSIDE THE WHILE LOOOOOOPPPP");
Sleep(5000);
}
return 0;
}
WIX FILE
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="InstallerForStartupCPP" Language="1033" Version="1.0.0.0" Manufacturer="LPAA" UpgradeCode="70510e56-b6ab-4e6f-beb6-40bb2e30c568">
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="InstallerForStartupCPP" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Startup CPP" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent">
<File
Id="STARTUPCPPINSTALLER"
Name="StartupCPPInstaller.exe"
DiskId="1"
Source="$(var.StartupCPPInstaller.TargetPath)"
Vital="yes"
KeyPath="yes" />
</Component>
<Component Id="log4cplus">
<File Source ="G:\SarVaGYa\myworkspace\LatestLpa\lpa\lpa_c\ext_library\log4cplus\bin\Release\log4cplus.dll" />
</Component>
</ComponentGroup>
<Binary Id="SetupCA" SourceFile="..\SetupCA\bin\Release\SetupCA.CA.dll"/>
<CustomAction Id="WRITEFILETODISK" Execute="immediate" BinaryKey="SetupCA" DllEntry="WriteFileToDisk" />
<InstallExecuteSequence>
<Custom Action="WRITEFILETODISK" Sequence="2"></Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>
How am I supposed to install the MSI file and run the program in background.? Please help.
PS: I have tried making a service. I do not get GetLocalAppDataPath true value if I run the program as service.
The straightforward way would be to change C++ application subsystem type to windows in project settings: Linker->System->SubSystem and replace main function with WinMain - it will not create any windows itself, you can ignore its parameters and do what you currently do in main.
All "RegisterMyProgramForStartup" is doing is writing a registry value. This is basic Windows Installer 101 functionality exposed by the RegistryValue element. All of this custom action code is an antipattern.

ActivityLibrary for Sharepoint Designer

I'm trying to implement an MSDN tutorial to create an activity library for Sharepoint Designer, and it gives me this error:
Namespace1.CreateTaskInListOnSite' does not contain a definition for 'ListName' and no extension method 'ListName' accepting a ...
and the same for AssignedTo and 'SiteUrl`.
Shouldn't 'AssignedTo', 'ListName' and 'SiteUrl' be inherited members from SequenceActivity class?
Here is the code which is erred:
protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext)
{
try
{
SPSite sitecollection = new SPSite(this.SiteUrl); //here
SPWeb web = sitecollection.OpenWeb();
SPUser user = web.Users[this.AssignTo[0].ToString()]; // here
SPList list = web.Lists[this.ListName]; // and here
SPListItem item = list.Items.Add();
item["Title"] = this.TaskTitle;
item["AssignedTo"] = user;
item.Update();
}
catch (Exception ex)
{
EventLog.WriteEntry("MSDN Workflow", ex.ToString());
}
return ActivityExecutionStatus.Closed;
}
Here is the .actions file:
<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Create Task in List On Site"
ClassName="Namespace1.CreateTaskInListOnSite"
Assembly="CustomWorkflowActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a3170baa21b0a3e9"
AppliesTo="all" Category="Cross Site Actions">
<RuleDesigner
Sentence="Create task titled %1 for %2 on site %3 in
list %4">
<FieldBind Field="TaskTitle" DesignerType="TextArea"
Id="1"/>
<FieldBind Field="AssignTo" DesignerType="Person"
Text="this user" Id="2"/>
<FieldBind Field="SiteUrl" DesignerType="TextArea" Id="3"/>
<FieldBind Field="ListName" DesignerType="TextArea" Id="4"/>
</RuleDesigner>
<Parameters>
<Parameter Name="TaskTitle" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="AssignTo" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="SiteUrl" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="ListName" Type="System.String, mscorlib"
Direction="In" />
</Parameters>
</Action>
The .ACTIONS file calls the ClassName as MSDN.HowTo.CreateTaskInListOnSite, where as in your code file you seem to use the namespace Namespace1. The two have to match.
So either you change .ACTIONS to this:
<Action Name="Create Task in List On Site"
ClassName="MSDN.HowTo.CreateTaskInListOnSite" .... />
or change the namespace definition in your code file to MSDN.HowTo.
The assembly attribute values also have to be correct. You can use the sn.exe tool to retrieve the public key token for your assembly.

Categories

Resources